目录
目标
数据结构
索引类型
索引用途
时间与空间的考虑
更好的创建索引
ICP
索引使用
对mysql的学习总结,我会从执行引擎、索引、sql、锁、MVCC、事务等几个部分进行阐述。本节阐述索引,包括索引采用的数据结构、如何更好的创建索引、索引使用的注意事项等。
mysql各种索引都采用B+Tree数据结构
考虑磁盘IO消耗低,查找效率更稳定等等。
每个节点都存储具体数据,从而块中可存记录变少,导致磁盘IO增多,树高度增加。
命中可能发生在任意节点,查找的效率不稳定
只有叶子节点存储具体数据,其它节点只存储key和指针,从而块中记录增多,树高度降低,同样大小的内存可缓存更多的节点,磁盘IO也减少。
命中只发生在叶子节点,查找效果稳定。
需要读取的数据未缓存到内存中。
树的数据结构
聚族索引结构+二级索引(非聚簇索引)结构图示
如图可见:
表存在主键时:聚簇索引=主键索引
二级索引的叶子结点数据包括 索引内容与主键ID。
二级索引命中主键ID后,使用主键ID查询主键索引命中记录。
树的高度=索引的高度
索引的高度影响查询效率,建议索引高度控制在3层以内,表数据量控制在1千5百万左右。
仅限InnerDB引擎
聚集原则
1:存在主键,则主键构建聚簇索引
2:不存在主键,则第一个不允许NULL的唯一索引构建为聚簇索引
3:均不满足,则使用rowid构建聚簇索引。
特点
索引节点+数据节点,构成了数据叶子节点。
一个文件:索引文件+数据文件。
通过索引获取数据不需要再次磁盘寻址。
MyIsam引擎
特点
索引叶子节点存储的数据为:记录在磁盘中的位置
两个文件:索引文件,数据文件
通过索引获取数据在磁盘的位置,然后磁盘寻址并获取数据。
不作为上述索引的其它索引,均为二级索引。
叶子节点:索引数据+对应的(非)聚族索引数据,例如:索引数据+ID
1:快速查询
2:如果where所有条件满足最左匹配,且索引包含了所有select中字段,则从索引中直接取出select字段数据
2:如果where所有条件满足最左匹配,且索引包含了所有order by中字段,且order by列不在where但符合最左匹配,则直接使用索引中字段进行排序
从锁实现原理,加锁情景理解,导致聚族索引/甚至二级索引都被加锁。
从用途可以得出,当索引中字段足够多时,可以直接从索引中获取,而省去了读取数据节点,从该角度理解:查询速度会更快。
结合 索引节点大小,page,索引深度,上述做法使得索引节点很大,索引深度增加,导致查找次数增多,可能导致更多磁盘IO
索引变大,但查询速度未必提升。空间和时间应该合理考虑。
针对使用最频繁的sql,创建索引
如果一组列条件从表中扫描的数据量占总量的比例很小(一般5%甚至1%以下,千万级别的表最好在0.1%以下),这些列可以作为组合索引。
要求扫描的数据行少,就要求条件列组合能过滤掉大量的数据,即为列组合的NDV(number distinct value 不重复数据量)很高。
show indexes from table
可查看索引各列的基数Cardinality, 可大致表示NDV.
列的选择性=(Cardinality)列的基数/(total)表记录总数。选择性越高,索引的作用越大。
由图可知
虽然NDV和cardinality数据存在差异,但均能表达出列的是否适合做为索引。
status和biz_type的选择性太低了,扫描行会很多,不适合做索引。
buyer_id和pay_timeNDV很大,扫描行会较少,适合做索引。
将选择性最高的列放到索引首列,选择性越高越应该放到前边。
或者与查询频率很高的列进行组合。
索引字段类型大小 直接影响 索引高度。
索引高度与磁盘IO次数的关系(暂不考虑内存cache):
聚族索引(如主键)查找记录的理论IO次数与聚族索引高度有关;
非聚族索引查找记录的理论IO次数=非聚族索引查找次数+聚族索引查找次数。
计算索引高度
准备已知条件
mysql中page大小=16K
page使用最大比例:70%
指针大小=8byte
聚族索引字段类型大小
二级索引字段类型大小
记录行大小估值
举例:
聚族索引字段大小:4byte
二级索引字段大小:4byte
记录行大小:200byte
计算过程:
一个page可存聚族索引叶子节点数
page大小 * page最大使用率 / (行记录大小+聚族索引字段大小)
16384(page大小)*70% /200(聚族索引字段大小4相对200太小,被省略了) 约等于 50
一个page可存储非聚族索引的节点数
page大小 * page最大使用率 / (指针大小+非聚族索引字段大小)
16384*70% / (8+4) 约等于 1000
计算索引高度与行数的关系
索引高度为2,则聚族索引可存记录数=1000*50=5W,则非聚族索引节点数=1000*1000=100W
索引高度为3,则聚族索引可存记录数1000^2*50=5KW,非聚族索引节点数=1000^3=10亿
若已知记录数A,索引大小B;则A不变时,B越大,索引高度越大,使用的page就越多,可能磁盘IO就越多。
总结
单表索引高度最好<=3
索引字段越大,索引高度就越大,从而降低索引查找效率,导致sql性能降低。
空间成本
当表很大且索引字段较多时,索引占据的磁盘空间也会很大。
时间成本
索引使用不好,查询效率很低。
从扫描行数和返回行数考虑,索引使用不好,导致引擎的扫描行很多,即返回的记录数很多,然后在mysql server层进行filter,得到返回行,这样效率低下。
更新成本
更新记录时,也需要更新二级索引。当二级索引较多时,造成更新成本增加。
ICP index condition pushdown 索引条件过滤下推,官网地址。
扫描行:引擎使用命中的索引字段查找并返回的记录行数
返回行:mysql server使用where中其它条件进行过滤后的记录行数
server将where过滤下推到引擎中,索引过滤后,使用where过滤。
减少回表的次数,减少IO次数,减少server过滤记录数,降低时间成本。
减少DML走二级索引时加锁的范围和时间,降低阻塞和死锁的发生。
只适用于二级索引
只适用于单表查询,不适用于关联查询
只适用如下索引操作类型:EQ_REF/REF_OR_NULL/REF/SYSTEM/CONST
只能时RANGE操作,其它都不行,如ALL/FT/INDEX_MERGE/INDEX_SCAN
最左匹配原则,要求必须是精确查询,>=< >= <= between and in
类型转换
使用表达式
使用函数
模糊查询 like
不符合最左规则
按照最左匹配的列越多,数据越精确,则速度越快;反之,速度越慢;
让条件尽可能的具体,使扫描行与返回行更加接近;
DML操作最好使用聚族索引,因为使用二级索引的话,会在二级索引和聚族索引都加锁,导致扩大了锁的范围。