MySQL索引(2)

1、索引基础html

1. B-Tree索引mysql

<1> 全部的值都是按顺序存储的,而且每个叶子页到根的距离相同。算法

<2> 顺序组织存储,很适合查找范围数据,效率会很是高。sql

<3> 能够有效使用B-Tree索引的查询:全值匹配、匹配最左前缀、匹配列前缀、匹配范围值、精确匹配某一列并范围匹配另外一列、只访问索引的查询,还能够用于查询中的order bygroup by操做。数据库

<4> B-Tree索引的限制:缓存

  若是不是按照索引的最左列开始查找,则没法使用索引;服务器

  不能跳过索引中的列;数据结构

  若是查询中有某个列的查询范围,则其右边全部列都没法使用索引优化查询。并发

2. 哈希索引函数

<1> 基于哈希表实现,只对精确匹配索引全部列的查询才有效。

<2> 对于每一行数据,存储引擎都会对全部的索引列计算一个哈希码,将全部的哈希码存储在索引中,同时在哈希表中保存指向每一个数据行的指针。

<3> mysql中只有memory引擎显式支持哈希索引。Memory引擎支持非惟一哈希索引,若是多个哈希列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希条目中。

<4> 哈希索引只需存储哈希值和对应的行数据地址指针,因此索引的结构十分紧凑,使得哈希索引查找的速度很是快。

<5> 哈希索引限制:

  哈希索引只包含哈希值和行指针,而不存储字段值,因此不能使用索引中的值来避免读取行,即没法使用哈希索引进行索引覆盖查询。

  哈希索引数据并非按照索引值顺序存储的,因此没法用于排序。

  不支持部分索引列匹配查找,由于哈希索引始终是使用索引列的所有内容来计算哈希值的。

  只支持等值比较查询(=IN<=>),不支持任何范围查询。

  出现哈希冲突时须要遍历链表中全部的行指针,逐行进行比较直到找到全部符合条件的行。冲突不少的话一些索引维护操做的代价也会很高。

3. 建立自定义哈希索引

  若是存储引擎不支持哈希索引,能够在B-Tree索引基础上建立一个伪哈希索引。仍是使用B-Tree进行查找,可是使用哈希值而不是键自己进行索引查找。这在字段值是很长的字符串的时候,能够很好地提高性能。

  能够选择CRC32FNV64做为哈希函数(哈希值为整数),不要使用SHA1MD5,由于这两个函数产生的哈希值是很是长的字符串。

  可使用触发器来维护哈希值。

  为避免哈希冲突,使用哈希索引进行查询的时候,必须在where条件中带入哈希值和对应列的值。

 

2、索引优势

1. 大大减小了服务器须要扫描的数据量。

2. 能够帮助服务器避免排序和临时表。

3. 能够将随机I/O变为顺序I/O

  评价一个索引是否适合某个查询的“三星系统”:索引将相关的记录放到一块儿;索引中的数据顺序和查询中的排列顺序一致;索引中的列包含了查询中须要的所有列。

  然而,索引并不老是最好的解决方案,只有当索引帮助存储引擎快速查找到记录带来的好处大于其带来的额外工做时索引才是有效的。对于很是小的表,大部分状况下简单的全表扫描更高效;对于中到大型表,索引就很是有效;但对于特大型表,创建和使用索引的代价增大,须要考虑使用分区、块级别元数据技术等其余技术。

 

3、高性能的索引策略

1. 独立的列

  若是查询中的列不是独立的,mysql就不会使用索引,“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数。

2. 前缀索引和索引选择性

<1> 前缀索引能够节约索引空间,提升索引效率。

<2> 使用前缀索引会下降索引的选择性(不重复的索引值和数据表记录总数的比值,索引的选择性越高则查询效率越高,惟一索引的选择性是1,这是性能最好的),因此要选择足够长的前缀,但又不能太长,太长会占用太多存储空间,因此须要对最多见值列表进行分析来决定前缀的合适长度。能够将平均选择性:Select count(distinct left(name,length))/count(*) from ...; 与完整列选择性进行对比得出最适合的前缀长度。另外,数据分布不均匀时也须要考虑最坏的状况。

<3> 前缀索引没法使用于order bygroup by,也没法作覆盖扫描。

3. 多列索引选择合适的索引列顺序

  多列索引的索引列顺序选择一般都要考虑如何更好知足查询、排序、分组的须要。当不须要考虑排序和分组时,将选择性最高的列放在前面一般是很好的,固然也须要结合值的分布考虑,可能须要根据那些运行频率最高的查询来调整索引列的顺序。

4. 聚簇索引

<1> 当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中。(叶子页包含了行的所有列数据,可是节点页只包含了索引列)

<2> 一个表只能有一个聚簇索引。(覆盖索引能够模拟多个聚簇索引的状况)

<3> 不是全部的存储引擎都支持聚簇索引。

<4> 一些数据库服务器容许选择哪一个索引做为聚簇索引,但mysql的存储引擎并不支持。InnoDB经过主键汇集数据,如果没有定义主键,则选择一个惟一的非空索引代替。若是没有这样的索引,就隐式定义一个主键来做为聚簇索引。InnoDB只汇集在同一个页面中的记录,包含相邻键值的页面可能会相距甚远。

<5> 聚簇索引的优势:

  能够把相关的数据保存在一块儿。

  数据访问更快。

  使用覆盖索引扫描的查询能够直接使用页节点中的主键值。

<6> 聚簇索引缺点:

  聚簇数据最大限度地提升了I/O密集型应用的性能,可是若是数据所有都放在内存中,则访问的顺序就没那么重要了,聚簇索引也就没什么优点了。

  插入速度严重依赖于插入顺序。

  更新聚簇索引列的代价很高,由于会强制InnoDB将每一个被更新的列移动到新的位置。

  基于聚簇索引的表在插入新行或者主键被更新致使须要移动行的时候,可能面临“页分裂”的问题,致使表占用更多的磁盘空间。

  聚簇索引可能致使全表扫描变慢,尤为是行比较稀疏,或者因为页分裂致使数据存储不连续的时候。

  二级索引(非聚簇索引)可能要更大,由于在二级索引的叶子节点包含了引用行的主键列。

  二级索引访问须要两次索引查找。(对于InnoDB自适应哈希索引能减小这样的重复工做)

InnoDBMyISAM的数据分布对比:

<1> MyISAM按照数据插入的顺序存储在磁盘上。

<2> MyISAM中主键索引和其余索引在结构上没什么不一样,主键索引就是一个名为primary的惟一非空索引。

<3> 聚簇索引的每个叶子节点都包含了主键、事务ID、用于事务和MVCC的回滚指针以及全部的剩余列。

<4> InnoDB二级索引的叶子节点存储的不是“行指针”而是主键值,并以此做为指向行的“指针”。好处:InnoDB在移动行时无须更新二级索引中的这个“指针”,坏处:二级索引占用更多的空间。

InnoDB表中按主键顺序插入行:

  使用InnoDB时应该尽量地按主键顺序插入数据,而且尽量地使用单调增长的聚簇键的值来插入新行,不然会致使已经刷到磁盘上的目标页被从新读取到内存中、页分裂、数据碎片等状况。须要optimize table来重建表并优化页的填充。

  对于高并发工做负载,在InnoDB中按主键顺序插入可能会形成明显的争用,可能须要考虑从新设计表或应用,或者更改innodb_autoinc_lock_mode配置。

5. 覆盖索引

  索引包含了全部须要查询的字段的值,无须读取数据行。

覆盖索引优势:

<1> MySql会极大地减小数据访问量。

<2> 对于I/O密集型的范围查询会比随机从磁盘中读取每一行数据的I/O要少得多。

<3> 一些存储引擎如MyISAM在内存中只缓存索引,数据则依赖于操做系统来缓存,所以访问数据须要一次系统调用,使用覆盖索引就不须要系统调用了。

<4> 若是InnoDB的二级索引为覆盖索引,则能够避免对主键索引的二次查询。

注意:不是全部类型的索引均可以成为覆盖索引,也不是全部存储引擎都支持覆盖索引。MySql只能使用B-Tree索引作覆盖索引。

索引覆盖查询:

  MySQL查询优化器会在执行查询前判断是否有一个索引能进行覆盖。(就算没有where子句也可使用索引覆盖查询)Explain出来的“extra”列为“using index”说明查询使用了覆盖索引。

使用索引扫描来作排序:

  若是Explain出来的“type”列为“index”说明使用了索引扫描来作排序。按索引顺序读取数据的速度一般要比顺序地全表扫描慢,由于若是索引不能覆盖查询所需的所有列,每扫描一条索引记录就要回表查询一次对应的行,这基本上都是随机I/O

  MySQL可使用同一个索引既知足排序又用于查找行。用于排序的索引须要知足如下条件:

<1> 索引的列顺序和order by子句的顺序彻底一致,而且全部列的排序方向都同样。

<2> 若是查询须要关联多张表,则只有当order by子句引用的字段所有为第一张表时,才能使用索引作排序。

<3> order by子句引用的字段须要知足索引的最左前缀的要求。有一种状况order by子句能够不知足索引的最左前缀的要求,就是前导列为常量的时候,例如where子句或join子句中对这些列指定了常量。

压缩(前缀压缩)索引:

  MyISAM存储引擎使用前缀压缩来减小索引大小,从而让更多的索引能够放入内存中,提升性能。具体方法:先彻底保存索引块中的第一个值,而后将其余值和第一个值进行比较获得相同前缀的字节数和剩余的不一样后缀部分,把相同前缀的字节数和剩余的不一样后缀部分存储起来便可。

  压缩索引的缺点是:myiSAM查找时没法在索引块使用二分查找而只能从头开始扫描,order by desc时倒序扫描更慢。

  能够在create table语句中指定pack_keys参数来控制索引压缩的方式。

冗余和重复索引:

  重复索引是指在相同的列上按照相同的顺序建立相同类型的索引。MySQL须要单独维护重复的索引,而且优化器在优化查询的时候也须要逐个进行考虑,重复索引会影响性能。

  MySQL的惟一限制和主键限制都是经过索引实现的。

  若是建立了索引(A,B),再建立索引(A)就是冗余索引。不一样类型的索引不算冗余。应该尽可能扩展已有的索引而不是建立新索引,除非扩展以后会对性能有很差的影响。

索引和锁:

<1> 索引可让查询锁定更少的行。

<2> 即便使用了索引,InnoDB也可能锁住一些不须要的数据。

<3> InnoDB在二级索引上使用共享读锁,但访问主键索引须要排它锁,这消除了使用覆盖索引的可能性,而且使得select for updatelock in share mode或非锁定查询要慢不少。

 

4、索引优化策略

1. 有些列虽然选择性低,但若是在where子句中使用频率很高的话也应该建立索引,或将其做为多列索引的前缀列。

2. 就算在查询中没有某个列的限制,也能够加上这个列的判断,这样mysql才能匹配索引的最左前缀。如:sex in (‘m’,’f’)但也不能滥用,每增长一个In条件优化器须要作的组合都将以指数形式增长,下降查询性能。

3. Mysql查询只能使用索引的最左前缀,直到遇到第一个范围条件列,因此尽量将须要作范围查询的列放在索引的后面,以便优化器能使用尽量多的索引列。(用IN来来代替范围条件)

4.除了or查询,MySQL查询每次最多只使用一个索引,由于相对于全表扫描和只使用一个索引,去分析两个或两个以上的索引二叉树更加耗时。对于查询:select col from table where col1=** and col2=*** and col3=****,创建复合索引(col1,col2,col3)来进行查询的性能是最好的。

 

5、维护索引和表

1. check table:检查索引和表的错误。

2. repair table:修复损坏的表。若是存储引擎不支持能够经过一个不作任何操做的alter命令来重建表。

3. analyze table:从新生成索引统计信息。

  MySQL的查询优化器会经过records_in_range()info()这两个API来了解存储引擎的索引值的分布信息,以决定如何使用索引。每种存储引擎实现索引统计信息的方式不一样:Memory引擎不存储索引统计信息,myisam将索引统计信息存储在磁盘中,InnoDB经过随机的索引访问进行评估并将统计信息存储在内存中。InnoDB在打开某些information_schema表,或使用show table statusshow index,或在mysql客户端开启自动补全功能的时候都会触发索引统计信息的更新。

4. optimize table/导出再导入:减小索引和数据的碎片。若是存储引擎不支持能够经过不作任何操做的alter命令来重建表。

 

最后推荐一篇博文:MySQL索引背后的数据结构及算法原理,这篇博文中结合数据结构和计算机磁盘读写原理详细分析了数据库系统为什么使用Btree做为索引数据结构,以及MySQL聚簇索引和非聚簇索引的实现,如何高性能使用索引的策略,是我阅读过写得最好的文章了,强力推荐!!!

相关文章
相关标签/搜索