MySQL优化系列2-索引原理和优化
索引简介:
索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构
为什么要用索引
- 索引能极大的减少存储引擎需要扫描的数据量
- 索引可以把随机IO变为顺序IO
- 索引可以帮助我们在进行分组、排序等操作时,避免使用临时表
为了说明索引的数据结构,先对比一下几种常见的数据结构特点
一些数据结构
二叉查找树
在二叉树中,左子树的键值总是小于根的键值,右子树的键值总是大于根的键值
当插入数据都大于节点时会形成线性链表结构。所以二叉树查找效率取决于数据的分布
平衡二叉树(例如红黑树)
首先符合二叉查找树的定义,其次必须满足任何节点的两个子树的高度最大差为1
为了达到平衡需要经过1次或多次的左旋和右旋操作,虽然查找速度比普通二叉树快了,但是维护成本增加
缺点:
深度太大:数据所处的深度决定了它的IO操作次数,IO操作耗时大
容量太小:每一个磁盘块(节点、页)保存的数据量太小了
没有很好的利用操作磁盘IO的数据交换特性
也没有利用好磁盘IO的预读能力(空间局部性原理),从而带来频繁的IO操作
多路平衡查找树(绝对平衡树)BTree
特点:
不再是二叉搜索,而是m叉搜索
叶子节点,非叶子节点都存储数据;
中序遍历,可以获得所有节点
加强版多路平衡查找树(绝对平衡树)B+Tree
B+Tree是在BTree的基础上进化而来
B+Tree和BTee的区别:
- B+Tree节点关键字搜素采用闭合区间(左闭合,插入都是往右边插入,提高了效率,保证了顺序)
- B+Tree非叶子节点不保存数据相关信息,只保存关键字和子节点的引用(非叶子节点能够存储的关键字会更多,也就是能存储的索引更多)
由这个特点可得出:定义数据类型时尽量短,有利于索引的创建,可容纳更多的数据;
选择索引关键字时尽量选择小的字段,增加索引的容量;
索引过多会影响UPDATE,INSERT操作效率,因为要保证索引树的绝对平衡需要移动很多索引节点
3.B+Tree关键字对应的数据保存在叶子节点中
4.B+Tree叶子节点是顺序排列的,并且相邻节点具有顺序引用的关系,增加了链表结构,(获取所有节点不再需要中序遍历,本身就是有序排列的,且相邻节点也是通过链表有序连接的;范围查找优化)
相对于Btree,B+Tree优点:
- B+Tree拥有BTree的所有优点
- B+Tree扫库、扫表能力更强
- B+Tree磁盘读写能力更强
- B+Tree排序能力更强
- B+Tree查询效率更加稳定
根据上述各种数据结构的比较,显然B+Tree最适合作为索引的数据结构,MySQL中索引就是用的B+Tree结构
MySQL B+Tree索引体现形式(基于5.7)
MyISam存储引擎
MyIsam存储引擎数据分两个文件存储MYD(数据文件)和MYI(索引文件),还有默认的frm(表结构描述文件,MySQL8取消了frm文件)
主键索引叶子节点不存储数据,只存储数据地址,指向MYD文件
辅助索引结构和主键索引一样
Innodb存储引擎
- 以主键为索引(聚集索引)来组织数据的存储
- 数据和索引都保存在同一个文件中(IBD后缀文件)
- 假如表没有显示的指定主键索引,Innodb会隐式的创建一个6byte int类型的主键索引
- 数据发生迁移时,辅助索引可不变
聚集索引:数据库表中数据的物理顺序与键值的逻辑顺序(索引)相同,顺序IO查找
索引知识点补充
由上面的索引结构可以推断出索引的优化原理
列的离散性(count(distinct col):count(col))
离散性越高,选择性越好(离散性低的数据重复多,选择就多,不利于检索数据。尽量选择离散性高的列来建立索引)
最左匹配原则
对索引中关键字进行计算(对比),一定是从左往右依次进行,且不可跳过
联合索引(单列索引是特殊的联合索引)
联合索引列选择原则:
- 经常使用的列优先【最左匹配原则】
- 选择性高(离散性高)的列优先【离散性原则】
- 宽度小的列优先【最少空间原则】(宽度小,非叶子节点上能保存的关键字就多)
例:假如存在联合索引name,phoneNum
则 select * from where name = ? 会命中该联合索引【最左匹配原则】,跟 like "xxx%"能使用索引原理一样
联合索引中存在某个列就不需要再对此列建索引
覆盖索引(尽量避免select * )
- 如果查询列可通过索引节点中的关键字直接返回,则该索引称之为覆盖索引。(select * 没有机会命中覆盖索引)
- 覆盖索引可以减少数据库IO,将随机IO变为顺序IO,可提高查询性能
索引字段最好不要允许为空,null在mysql中需要特殊处理增加运算和空间
唯一索引允许多个数据为null
一个查询只会走一个索引
总结:
索引列的数据长度能少则少(所以在定义表结构时尽量定的数据类型长度合理,且不要在长字段上建立索引)
索引要创建合适,过多索引反而会影响效率,且迁移麻烦,存储增大,删除、修改、新增时非常影响效率,因为数据改变索引也会跟着改变,索引维护耗费性能
匹配列前缀可用到索引 like xxx%, like %xxx%、 like %xxx用不到索引;
where条件中 not in 和<>操作无法使用索引(大概率情况下,具体要看执行成本,详见:MySQL优化系列2.1-MySQL中 IS NULL、IS NOT NULL、!= 能用上索引吗?);
多用指定列查询,只返回需要的数据列,尽量避免 select * (指定查询字段有可能命中覆盖索引)
联合索引中如果不是按照索引最左列开始查找,无法使用索引
联合索引中精确匹配最左前列并范围匹配另外一列可以用到索引;
联合索引中如果查询中有某个列的范围查询,则其右边的所有列都无法使用索引;
可视化算法演示:http://www.cs.usfca.edu/~galles/visualization/Algorithms.html
数据库索引为什么不用hash表而用b+树
1.hash表只能匹配是否相等,不能实现范围查找
select * from xx where id > 23; 这时就没办法索引了
2.当需要按照索引进行order by时,hash值没办法支持排序
select * from xx order by score desc;如果score为建立索引的字段,hash值没办法辅助排序。
3.组合索引可以支持部分索引查询,如(a,b,c)的组合索引,查询中只用到了阿和b也可以查询的,如果使用hash表,组合索引会将几个字段合并hash,没办法支持部分索引
4.当数据量很大时,hash冲突的概率也会非常大