MySQL优化那些事儿

MySQL的使用优化主要从优化库表结构、使用合适的索引、优化查询等方面考虑。mysql

优化库表结构

说说数据类型

字符串类型

在MySQL中表示字符串的类型有多种,其中常见的有Char和VarChar,BLOB和TEXT以及能够实现某些优化的ENUM。咱们要认识到不一样的数据类型在存储和使用的区别,而后合理的使用就能实现优化。sql

关于MySQL中数据类型的介绍,能够参看个人另外一篇博文: MySQL的常见操做数据库

Char和VarChar缓存

Char是定长类型,MySQL会根据定义的字符串的长度分配足够的空间,而且MySQL在存储CHAR值的时候会删除末尾全部的空格。对于常常变动的数据,通常采用CHAR来进行存储,由于CHAR类型在变化的时候不容易产生碎片。服务器

VARCHAR是变长类型,它比CHAR更加节省空间,可是VARCHAR在数据变化的时候容易产生碎片,因此通常用于做为不常常变化的数据的数据类型。VARCHAR须要多使用一个或者两个额外字节来记录字符串的长度,若是列的最大长度小于等于255字节就用一个额外的字节来存储长度,不然使用两个字节。例如VARCHAR(10)的列须要11个字节的存储空间,VARCHAR(1000)的列须要1002个字节。并发

注意,在5.0或者更高的版本中,MySQL在存储或者检索VARCHAR数据类型时保留末尾空格,可是在4.1或者更老的版本中,MySQL在存储或者检索VARCHAR数据类型时和CHAR同样都是删除末尾的空格app

BLOB和TEXT函数

BLOB即SMALLBLOB,TEXT即SMALLTEXT。BLOB和TEXT都是为了存储很大的数据而设计的字符串数据类型,分别采用二进制和字符串的方式来存储。性能

使用ENUM来代替字符串类型测试

MySQL在存储枚举的时候很是的紧凑,会根据列表值的数量压缩到一个或者两个字节中。MySQL在内部会将每一个值在列表中的位置保存为整数,而且在表的.frm文件中保存"数字-字符串"映射关系的"查找表"。

例如:create table enum_test(e enum('apple','banana','pear'));insert into enum_test(e) values('apple'),('banana'),('pear');而后咱们所插入的数据在表中实际上是存储为整数的。

数字类型

MySQL能够为整数类型指定宽度,如INT(1),INT(20),可是这对大多数应用是没有意义的。MySQL所指定的整数类型的宽度只是用于设置一些MySQL客户端用于显示字符的个数,对于存储和计算而言,INT(1)和INT(20)不会限制值的合法范围,这两种类型都是相同的。

日期和时间类型

DateTime和TimeStamp

DATETIME和TIMESTAMP是两种日期类型,两种类型在MySQL中存储数据的格式彻底相同(都是yyyy-MM-dd HH:mm:ss),可是二者也有不一样之处。

DATETIME能保存大范围的值,从1001年到9999年,精度为秒。MySQL采用8个字节来存储DATETIME数据类型所包含的值。默认状况下,MySQL以一种可排序的、无歧义的格式显示DATETIME的值。

TIMESTAMP保存了从1970年1月1日午夜(格林尼治标准时间)以来的秒数,它和UNIX的时间戳相同。TIMESTAMP仅仅使用4个字节的存储空间,因此它能表示的时间范围也比DATETIME小,只能表示从1970年到2038年。TIMESTAMP也有DATETIME没有的特殊属性,默认状况下,若是插入时没有指定第一个TIMESTAMP列的值,MySQL则设置该列的值为当前时间。

采用合适的索引

索引优化

索引优化是一个很大的方面,这里只是简单的介绍一些基本使用,事后会推出关于索引优化与设计的专题。

索引基础

在MySQL中,索引是在存储引擎层而不是服务器层实现的。Mysql中索引结构有:B-Tree索引、哈希索引、空间数据索引(R-Tree索引)、全文索引等索引结构,不一样的存储引擎对于上述索引结构的实现不一样,并且也不是全部的存储引擎都有这5种索引结构类型。

索引类型:

MySQL中的索引类型主要有5种:

  1. 普通索引: 最基本的索引、没有任何限制。MyIASM中默认的BTREE类型的索引。如ALTER TABLE article ADD INDEX index_title_name ON title(100);CREATE INDEX index_name ON table(column(100)),或者直接在建立表的时候定义索引index index_title_name(title(100))
  2. 惟一索引: 索引列的值能够为空。与普通索引相似,不一样之处在于索引列的值必须惟一。如ALTER TABLE article ADD UNIQU index_title_name ON title(100);CREATE UNIQUE INDEX index_name ON table(column(100))或者直接在建立表的时候定义索引UNIQUE index_title_name(title(100))
  3. 全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时耗空间。如ALTER TABLE article ADD FULLTEXT index_content(content)CREATE FULLTEXT INDEX index_content ON article(content)FULLTEXT (content)
  4. 主键索引:它是一种特殊的惟一索引,不容许有空值。
  5. 最左索引(组合索引): 组合索引能够更好的提升MySQL效率,最左索引遵循"最左索引"原则。建立复合索引时应该将最经常使用(频率)做限制条件的列放在最左边,依次递减。

索引方法:

可使用B树索引的查询:

  1. 全值匹配的查询
  2. 匹配最左前缀的查询
  3. 匹配列前缀的查询
  4. 匹配范围值的查询
  5. 精确匹配左前列而且范围匹配另一列
  6. 覆盖索引(只须要访问索引而无需查询数据行)

使用B树索引的限制

  1. 在多列索引中,必须按照索引的最左列开始查找,不然索引没法使用
  2. 在多列索引中,不能跳过索引中的列。好比一个三列组成的联合索引,不能只使用第一列和第三列进行查询而跳过第二列。
  3. not in和<>操做没法使用索引
  4. 若是查询中有某一个列的范围查询,则其右边全部的列都不能使用索引。

Hash索引的特色:

Hash索引是基于Hash表实现的,只有查询条件精确匹配Hash索引中的全部列时,才能使用Hash索引,Hash索引只适用于等值查询不适合模糊查询和范围查询。

对于Hash索引中的全部列,存储引擎都会为该列的每一行计算一个Hash码,Hash索引中存储的就是Hash码。

使用Hash索引的限制:

  1. Hash索引必须进行二次的查找。
  2. Hash索引没法进行排序。
  3. Hash索引不支持部分索引查找也不支持范围查找。
  4. Hash索引中Hash的计算可能存在Hash冲突。

B树索引与Hash索引在不少地方是不一样的。B树索引除了能加快数据的查找速度以外还能够作到排序和分组,B树索引的叶子节点存储了索引关键字的值,能够直接经过索引查找关键字的信息从而避免了访问数据行。可是Hash索引的叶子节点中存储的是关键字信息的Hash码,咱们须要将查询信息转化成Hash在表中找到对应的数据行才能查找到数据的信息。所以Hash索引不能做为覆盖索引来使用。

覆盖索引:

若是一个索引包含全部须要查询的字段的值(where语句的参数、order by的参数、group by的参数),那么咱们一般称这个索引为覆盖索引。对于Memory存储引擎不能使用覆盖索引,查询过程当中若是包含了太多的列(如select *)也不适合使用覆盖索引。 使用覆盖索引也有不少的优势。

  • 优化缓存,减小磁盘I/O操做。
  • 减小随机I/O,变随机I/O为顺序I/O。
  • 能够避免对Innodb主键索引的二次查询。

InnoDB存储引擎中的索引

InnoDB做为MySQL最为著名的存储引擎,这里要作特别的介绍。InnoDB中存储引擎支持B+树索引、全文索引和哈希索引。InnoDB存储引擎支持的哈希引擎是自适应的,InnoDB存储引擎会根据表的使用状况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引。

传统意义上的索引就是指的B+树索引,这是目前关系型数据库系统中查找最为经常使用和有效的索引,其构造就是采用了二叉树的思想,根据键值对快速找到数据。经过B+树索引找到被查找数据行所在的页,而后数据库把页读入到内存,再在内存中进行查找,找到对应的数据。

InnoDB使用的是行锁,只有在修改行时,才会对行进行加锁。使用索引可以使得数据在查询过程当中锁定更少的行,增长了数据处理的并发性,提升了数据库的性能。

索引使用的注意事项

  1. 保证在MySQL中查找数据时,表中对应的列数独立的。独立的列在于索引列不能是表达式中的一部分,也不能是函数的参数。即不容许select id from article where id+1=5等状况的出现,不然索引将不能使用。
  2. 索引很长的字符列,会让索引变得大且慢。这个时候就要采用前缀索引,就是选取列开始的部分字符做为索引,前缀索引的选择也要保证合理的索引选择性(越接近1越好)。
  3. 若是不须要考虑排序和分组的须要,在联合索引中,应该将选择性最高的索引放到索引的最前列、将常常会被使用的列放到索引的最前列、宽度较小的列放到索引的最前列。
  4. 使用pt-duplicate-key-checker h=127.0.0.1查找重复和冗余的索引,而后将重复冗余的索引删除。

改造SQL查询语句

MySQL链接过程与状态

MySQL链接状态

MySQL客户端和服务器之间的通讯协议是"半双工"的,在任何一个时刻,要么是由服务器向客户端发送数据,要么是由客户端向服务器端发送数据,两个动做不能同时发生。对于每个时刻,能够经过命令show full processlist来查看mysql当前链接的状态(Command列就表明当前的状态)。

MySQL的状态以下:

Sleep: 线程正在等待客户端发送新的请求

Query: 线程正在执行查询或者正在将结果发送给客户端。

Locked: 在MySQL服务器层,该线程正在等待表锁.

Sorting result: 线程正在对结果集进行排序。

Copying to tmp table [on disk]: 线程正在执行查询,而且将其结果集都复制到一个临时表中,这种状态要么是在作GROUP BY操做,要么是文件排序操做,或者是UNION操做。若是状态上有on disk的标记,那么表示MySQL正在将一个内存临时表放到磁盘上。

Analyzing and statistics: 线程正在收集存储引擎的统计信息,并生成查询的执行计划。

Sending data: 这表示线程或者在多个状态之间传送数据,或者在生成结果集,或者在向客户端返回数据。

MySQL链接过程

  1. MySQL客户端发送一条查询给服务器
  2. MySQL若是开启了查询缓存,那么MySQL服务器会优先检查查询缓存。检查的过程是经过一个对大小敏感的哈希查找实现的,若是缓存命中,那么在返回查询结果以前MySQL会检查一次用户权限,若是权限合适,那么直接返回缓存中的结果信息,查询完成,不然执行下一步。
  3. 服务器进行SQL解析、预处理,而后再由优化器生成对应的执行计划。
  4. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询。
  5. 将查询的结果返回给客户端。

查询优化

在上面MySQL执行查询的过程当中,服务器已经提供了一些SQL的优化措施,咱们也须要正确理解并使用这些个优化措施

  1. 使用explain+sql查询语句能够查看SQL查询的效率。
  2. mysql使用基于成本的优化器。使用show status like 'last_query_cost'能够查询当前会话的last_query_cost值来得知mysql计算的当前查询的成本,mysql会进行评估并获得成本最小的执行计划。
  3. MySQL自带一种"嵌套循环"可以对咱们的大多数查询进行优化操做,调整关联表的关联顺讯以达到高效的查询。

优化数据访问

优化数据访问的关键在于:减小数据访问量,只检索必要访问的数据,保证向数据库发出的查询数据量只是实际须要的数据量。

为了实现数据访问量的优化,可使用:

  1. 在SQL的查询语句中,合理的使用limit控制行数。
  2. 在多表关联的SQL查询中,只查询须要的表的列,尽可能不要用"select *"
  3. 借助第三方的缓存系统,将常常查询的数据缓存起来。
  4. 若是查询是须要扫描大量的数据但只是返回少许的行,那么可使用索引覆盖扫描,把须要数据的行放到索引中。

重构查询

  1. 将大的查询分解成小的查询。特别是对于删除不须要的数据,通常来讲就是分批删除少许的数据,这样能够大大减小数据库锁的持有时间。
  2. 合理的分解关联查询。关联查询分解成单表查询能够减小锁的竞争;同时单表查询的结果在应用层作关联,能够实现数据库的拆分,作到高性能和可扩展。此外,经过将重复查询的数据作缓存能够提升效率。
  3. MySQL的某些子查询效率很低(如使用in的子查询),咱们应该使用explain语句测试当前查询的成本,而后决定是否应该使用内链接或者左(右)外链接改写mysql的in()子查询。但当咱们须要返回一个表中的某些列时,多表关联查询咱们可使用exists关键字的子查询,这样效率也会更高。——在MySQL5.6版本之前需注意
  4. 在使用union关键字进行sql查询时,若是有限制数据量和排序等操做,应在每一条sql语句中使用这些限制。
  5. 使用主键自带的排序效果和limit关键字来代替max和min关键字实现最大和最小值。
  6. MySQL在须要进行分页时,经过使用limit外加偏移量来实现,同时加上合适的order by子句,这样能够充分的利用具备索引的列。此外,在分页中,偏移量若是相差数据量过大,应该采用索引覆盖扫描。
  7. 进行关联查询时,在on和using子句的列上添加索引,而且注意在关联顺序上,应该在第二章表中添加索引,提升效率。
  8. 确保group by和order by子句的表达式上只涉及一个表中的列,只有这样才有可能使用索引优化这个过程。