MySQL索引与优化策略

转自:http://www.cnblogs.com/yuyue2014/p/3662005.htmlhtml

1. MySQL索引实现算法

在MySQL中,索引属于存储引擎级别的概念,不一样存储引擎对索引的实现方式是不一样的,下面主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。缓存

MyISAM索引实现

MyISAM引擎使用B+Tree做为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:服务器

图1函数

这里设表一共有三列,假设咱们以Col1为主键,则图1是一个MyISAM表的主索引(Primary key)示意。能够看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是惟一的,而辅助索引的key能够重复。若是咱们在Col2上创建一个辅助索引,则此索引的结构以下图所示:性能

 

图2 优化

一样也是一颗B+Tree,data域保存数据记录的地址。所以,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其data域的值,而后以data域的值为地址,读取相应数据记录。spa

MyISAM的索引方式也叫作“非汇集”的,之因此这么称呼是为了与InnoDB的汇集索引区分。3d

InnoDB索引实现

虽然InnoDB也使用B+Tree做为索引结构,但具体实现方式却与MyISAM大相径庭。code

第一个重大区别是InnoDB的数据文件自己就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件自己就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引。

图3

图3是InnoDB主索引(同时也是数据文件)的示意图,能够看到叶节点包含了完整的数据记录。这种索引叫作汇集索引。由于InnoDB的数据文件自己要按主键汇集,因此InnoDB要求表必须有主键(MyISAM能够没有),若是没有显式指定,则MySQL系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段做为主键,这个字段长度为6个字节,类型为长整形。

第二个与MyISAM索引的不一样是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的全部辅助索引都引用主键做为data域。例如,图4为定义在Col3上的一个辅助索引:

图4

这里以英文字符的ASCII码做为比较准则。汇集索引这种实现方式使得按主键的搜索十分高效,可是辅助索引搜索须要检索两遍索引:首先检索辅助索引得到主键,而后用主键到主索引中检索得到记录。

了解不一样存储引擎的索引实现方式对于正确使用和优化索引都很是有帮助,例如知道了InnoDB的索引实现后,就很容易明白为何不建议使用过长的字段做为主键,由于全部辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段做为主键在InnoDB中不是个好主意,由于InnoDB数据文件自己是一颗B+Tree,非单调的主键会形成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段做为主键则是一个很好的选择。

2. 高性能的索引策略

2.1 独立的列

“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数。

错误的写法:

select id from tab where id+1=5;
select id,value from tab where to_days(now())-to_days(gmt_created) <=10;

应该养成简化where条件的习惯,始终将索引列单独放在比较符号的一侧。

正确的写法:

select id,value from tab where gmt_created >= DATA_SUB(now(),interval 10 day );

 

2.2 索引选择性

selectivity = distinct Values / total Rows

索引的选择性是指,不重复的索引值和数据表的记录总数的比值。索引的选择性越高则查询效率越高,由于选择性高的索引可让MySQL在查找时过滤掉更多的行。

惟一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

 

2.3 前缀索引

对于BLOB、TEXT或者很长的VARCHAR类型的列,必须使用前缀索引,由于MySQL不容许索引这些列的完整长度。

诀窍在于要选择足够长的前缀以保持较高的选择性,同时又不能太长(以便节约空间)。前缀应该足够长,以使得前缀索引的选择性接近于索引整个列。

 

2.4 多列索引

一个常见的错误是,为每一个列建立独立的索引,或者按照错误的顺序建立多列索引。

错误的写法:

复制代码
create table t (
c1 int,
c2 int,
c3 int,
key(c1),
key(c2),
key(c3)
);
复制代码

在多个列上创建独立的单列索引大部分状况下并不能提升MySQL的查询性能。对于下面的查询where条件,这两个单列索引都是很差的选择:

select film_id, actor_id from table1 where actor_id=1 or film_id=1;

在老的MySQL版本中,MySQL会对这个查询使用全表扫描。除非改写成两个查询UNION的方式。

select film_id, actor_id from table1 where actor_id=1 
union all
select film_id, actor_id from table1 where film_id=1 
and actor_id<>1;

MySQL5.0和更新的版本引入了一种叫“索引合并”的策略,查询可以同时使用这两个单列索引进行扫描,并将结果合并。这种算法有三个变种:OR条件的联合(union),AND条件的相交(intersection),组合前两种状况的联合及相交。索引合并策略有时候是一种优化的结果,但实际上更多时候说明了表上的索引建得很糟糕:

(1)当出现服务器对多个索引作相交操做时(多个AND条件),一般意味着须要一个包含全部相关列的多列索引,而不是多个独立的单列索引。

(2)当出现服务器对多个索引作联合操做时(多个OR条件),一般须要耗费大量的CPU和内存资源在算法的缓存、排序和合并操做上。特别是当其中有些索引的选择性不高,须要合并扫描返回的大量数据的时候。

(3)若是在explain中看到有索引合并,应该好好检查一下查询和表的结构,看是否是已是最优的。

 

2.5 覆盖索引

若是一个索引包含(或者说覆盖)全部须要查询的字段的值,就称之为“覆盖索引”。MySQL利用索引返回select列表中的字段,而没必要根据索引再次回表读取数据页。

  select  id,status from tab where id=2

  alter table add key ind_t_id_status (id,status);

不是全部类型的索引均可以成为覆盖索引。覆盖索引必需要存储索引列的值,而哈希索引、空间索引和全文索引等都不存储索引列的值,因此MySQL只能用B-Tree索引作覆盖索引。另外,不一样的存储引擎实现覆盖索引的方式也不一样,并且不是全部的引擎都支持覆盖索引。

 

2.6 组合索引 

和覆盖索引相似对查询语句中多个经常使用字段创建索引,固然,建立组合索引并非说就需要将查询条件中的全部字段都放在一个索引中,还应该尽可能让一个索引被多个 Query 语句利用,尽可能减小同一个表上的索引数量,减小由于数据更新带来的索引更新成本,同时还能够减小由于索引所消耗的存储空间。

 

2.7 尽可能避免NULL

(1)尽量把字段定义为NOT NULL,能够放置一个默认值,如’’,0等。

(2)MySQL 难以优化NULL列。NULL列会使索引统计和值更加复杂。

(3)NULL列须要更多的存储空间,还须要在MYSQL内部进行特殊处理。

(4)NULL列加索引,每条记录都须要一个额外的字节,还致使MyISAM中固定大小的索引变成可变大小的索引。

 

3. 总结

在选择索引和编写利用这些索引的查询时,有如下三个原则:

(1)单行访问是很慢的。最好读取的块中能包含尽量多所须要的行。使用索引能够建立位置引用以提高效率。

(2)按顺序访问范围数据是很快的,这有两个缘由。第一,顺序I/O不须要屡次磁盘寻道,因此比随机I/O快不少。第二,若是服务器可以按须要顺序读取数据,那么就不须要额外的排序操做,而且group by 查询也无需在作排序和将行按组进行聚合计算了。

(3)索引覆盖查询是很快的。若是一个索引包含了查询须要的列,那么存储引擎就不须要再回表找行,这避免了大量的单行访问。

总的来讲,编写查询语句时应该尽量选择合适的索引避免单行查找,尽快能使用数据原生顺序从而避免额外的排序操做,并尽量使用索引覆盖查询。

理解索引是如何工做的很是重要,应该根据这些理解建立最合适的索引,而不是根据一些诸如“在多列索引中将选择性最高的列放在第一列”或“应该为where子句中出现的全部列建立索引”之类的经验法则及其推论。

相关文章
相关标签/搜索