高性能mysql
五:索引
1,B-Tree索引
全值匹配:匹配所有列。
匹配最左前缀:只使用索引的第一列。
匹配列前缀:只使用索引的第一列。
匹配范围值:只使用索引的第一列。
精确匹配某一列并范围匹配另外一列:第一列精确,第二列模糊。
2,哈希索引
只有精确匹配索引所有列的查询才有效。
只有Memory引擎显式支持哈希索引。也支持btree索引。
哈希索引只需要存储对应的哈希值,所以结构十分紧凑,所以速度快,但是限制是:
哈希索引只包含哈希值呵行指针,不储存字段值,所以不能从索引中取值,但是速度快,可以忽略不计。
哈希数据并不是按照索引顺序来存的,所以不能排序。
哈希也不支持索引列匹配查找。
哈希只支持等值比较查询,不支持任何范围查询。
冲突越多,代价越高。
除了Memory索引支持,还有NDB集群引擎也支持唯一哈希索引。
InnoDB引擎的自适应哈希索引:
当某些索引引值被使用得非常频繁时,他会在内存中基于btree索引上再创建一个哈希索引。(也可以人为创建一个字段作为索引,使用crc32或者fnv64函数)
3,索引的优点
索引大大减少了服务器需要扫描的数据量
索引可以帮助服务器避免排序呵临时表
索引可以随机I/o变为顺序I/o。
4,索引策略
独立的列:
索引列不能是表达式的一部分,也不能是函数的参数
前缀索引呵索引选择性:
有时候需要索引很长的字符列,回让索引很慢很大,一个策略是模拟哈希索引,一个策略是选择这列字符的前缀一部分进行索引(1,根据情况对这列字符进行合适的处理截取当作新的一列,再用这个做索引,查询用这列和完整的字符查询,2,使用left函数截取长度)
多列索引:
选择合适的索引顺序
在一个多列Btree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,,所以索引可以按照升序或者降序进行扫描,以满足排序查询。
选择顺序有一个经验法则:将选择行最高的列放到索引最前列,在不考虑排序和分组时,
聚簇索引
聚簇索引并不是一种单独的索引类型,而是一种数据存储方式,具体细节依赖其实现方式,,但是InnoDB 聚簇索引实际上在同一个结构中保存了btree索引和数据行。
当表有聚簇索引时,他的数据行实际上存放在索引的叶子页中,聚簇表示数据行和相邻的健值紧凑的存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
优点:
可以把相关数据保存在一起,
数据访问更快,聚簇索引将索引和数据保存在同一个btree中,
使用覆盖索引扫描的查询可以直接使用页节点中的主键值
缺点:
聚簇数据最大限度的提高了i/o密集型应用的性能,但如果数据全部都放在内存中,则访问的顺序就没那么重要了,聚簇也就没有什么优势了。
插入速度严重依赖于插入顺序,按照主键的顺序插入是加载数据到innodb表中速度最快的方式,但是如果不是按照主键顺序加载数据,那么在加载完成后最好使用optimize table命令重新组织一下表。
更新聚簇索引列的代价很高,因为会强制innodb将每个被更新的行移动到新的位置。
基于聚簇索引的表在插入新行,或者主键被更新导致需要移动的时候,可能面临页分裂的问题。
聚簇索引可能导致全表扫描变慢。
MyISAM的数据分布
在行旁边显示了行号,主键索引和其他索引在结构上没什么不同,主键索引就是一个名为primary的唯一非空索引
InnoDB的数据分布
在innodb中,聚簇索引“就是”表
聚簇索引的每一个叶子结点都包含了主键值,事物id,用于事务和mvcc的回滚指针以及所有的剩余列,如果主键是一个列前缀索引,innodb也会包含完整的主键列和剩下的其他列。
innodb的二级索引和聚簇索引很不相同,innodb二级索引的叶子结点中存储的不是行指针,而是主键值,这样的好处是,innodb在移动时无须更新二级索引中的这个指针,
innodb按主键顺序插入,减少时间还能减少主键索引的内存,默认的最大填充因子是页大小的15/16
覆盖索引
如果索引的叶子结点中已经包含还要查询的数据,那么还有什么必要再回表查询呢,如果一个索引包含或者说覆盖所有需要查询的字段的值,那么称之为覆盖索引。
索引条目通常远小于数据行大小,所以如果只需要读取索引,那么mysql就会极大的减 少数据访问量。
因为索引是按照列值顺序存储的,所以简单的范围查询能使用完全顺序的索引访问。
一些存储引擎如myisam再内存中只缓存索引,数据则依赖操作系统来缓存,因此要访问数据需要一次系统调用。
由于innodb的聚簇索引,覆盖引擎对innodb特别有用,innodb的二级索引再叶子结点中保存了行的主键值,所以如果二级主键能够覆盖查询,则避免了对主键索引的二次查询。
不是所有类型的索引都可以成为覆盖索引,覆盖索引必须要储存索引列的值,而哈希索引,空间索引和全文索引等都不存储索引列的值,所以mysql只能使用btree索引做覆盖索引,另外,不同的存储引擎实现覆盖索引的方式也不同,而且不是所有的引擎都支持覆盖,总结:只有btree支持覆盖
没用到覆盖的原因:
没有任何索引能够覆盖到这个查询,因为查询从表中选择了所有的列
mysql不能再索引中执行like的操作,这是底层存储引擎api的限制
解决这个两个问题
使用索引扫描来做排序
mysql有两种方式可以生成有序的结果:通过排序操作,或者按索引顺序扫描,如果Explain出来的type列的值为“index”,则说明mysql使用了索引扫描来做排序(不要和extra列的"using index"搞混淆了)
mysql可以使用同一个索引既满足排序,又用于查找行,因此,如果可能,设计索引时应该尽可能地同时满足这两种任务。
只有当索引的列顺序和order by顺序完全一致,并且所有列的排序方向都一样时,mysql才能使用索引来对结果做排序。如果查询需要关联多张表,则只有当ordery by应用字段全部为第一个表时,才能使用索引做排序,order by和查找查询的限制时一样的,需要满足索引的最左前缀的要求,不然没法利用索引排序。
压缩(前缀压缩)索引
myisam使用前缀压缩来减少索引的大小,从而让更多的索引可以放入内存中,这在某些情况下极大的提高性能,默认只压缩字符串,
压缩方法是,先完全保存索引快中的第一个值,然后将其他值和第一个值进行比较得到相同前缀的字节数和剩余的不同后缀部分,把这部分存储起来就行,
冗余和重复索引
mysql允许再相同列上创建多个索引,但是mysql都是需要单独维护重复的索引,并且优化器在优化查询的时候也需要一个个的进行考虑,这会影响性能,
重复索引时指在相同列上按照相同的顺序创建了相同类型的索引,应该避免这种操作。
索引和锁
索引可以让查询锁定更少的行,如果你的查询从不访问那些不需要的行,那么就会锁定更少的行,好处:首先,虽然innodb的行锁效率很高,内存使用的少。但是锁定行的时候冷然会带来额外开销,其次,锁定超过需要的行会增加锁争用并减少并发。
innodb只有在访问行的时候才会对其加锁,而索引能够减少inndb访问的行数,从而减少锁的数量。
索引案例学习
1,支持多种过滤条件,选择合适的where频繁列做索引,
基本原则:
考虑表上所有的选项,设计索引时,不要只为现有的查询考虑需要哪些索引,尽量的把搜索查询频繁的列放在前缀列,生僻列放在后面,查询时使用in()技术来处理搜索时没有指定这些列的场景。
例子:
2,避免多个范围条件
两个列,mysql可以使用a列索引和b列索引,但是没法同时使用他们。
3,优化排序
维护索引和表
1,找到并修复损坏的表
check table检查是否发生了表损坏
repair table修复,不是所有的存储引擎都支持该命令,如果不支持,业可以通过一个不做任何操作的alter操作来重建表,例如:alter table 表名 engine=innodb。
如果innodb引擎的表出现了损坏,需要马上查处原因,innodb的设计保证了它并不容易损坏,如果坏了那一半就是硬件问题,
2,减少索引和数据的碎片
btree索引可能会碎片化:行碎片,行间碎片,剩余空间碎片
对于myisam表这三种碎片都可能有,innodb不会出现断小的行碎片
解决:可以通过执行optimize table或者导出再导入的方式来重新整理数据,对于不支持optimize table的引擎,可以通过一个不做任何操作的alter table来重建表,例如:alter table 表名 engine=innodb。