MySQL 索引机制以及调优
Posted 老邋遢
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 索引机制以及调优相关的知识,希望对你有一定的参考价值。
mysql 索引机制以及调优
此文不是特别全,建议和另一篇文章结合着看
目录
文章目录
开篇
一起来看一下我们工作中常见的几个数据库问题
- 数据库常见的慢查询优化方式是什么?
- 为什么加索引能优化慢查询?
- 哪些数据结构可以提高查询速度?
- 众多数据结构中为什么Mysql选择使用B+树?
下面一起带着这些问题进入以下学习
基础知识储备
局部性原理
其实这个原理的提出源于1968年研究的一个问题
作业信息不全部装入主存的情况下能否保证作业的正确运行?
答案是肯定的,研究发现程序和数据的访问都有聚集成群的倾向。即在一个时间段内,仅会使用一小部分数据或代码(空间局部性),并且在未来很快被再次访问的可能性很大(时间局部性)。
这个类似于二八法则,80%时间会去访问20%的数据。
实际场景中就是我想从磁盘中读取1字节数据,实际上读取的是磁盘1个page的数据(页大小通常为4k),当然极端场景下这1字节的数据可能是从2页甚至4页里读取的
磁盘预读
上面已经简单介绍了磁盘的存储机制,页是存储器的逻辑块,操作系统往往将主存和磁盘存储划分为连续且大小相等的块,每个存储块为一页,主存和磁盘以页为单位交换数据
索引是什么?
- 索引是帮助MySQL高效获取数据的数据结构
- 索引存储在文件系统中
- 索引的文件存储形式与存储引擎有关
- 索引文件的结构
- hash
- 二叉树
- B树
- B+树
hash
hash通常是采用hash算法计算出一个hash值
最简单的就是通过%运算计算出余数,然后将数据放到与余数对应的内存地址上
如果产生了hash碰撞,通常会将数据放到同一地址的链表里
-
缺点
- 使用hash存储需要将所有的数据文件添加到内存中,比较耗费内存空间
- 如果所有查询都是等值查询,那么hash确实很快,但是实际生产场景中范围查询会比较多,这时候hash就不太适合了
二叉树和红黑树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcXGppoa-1623210785331)(image-20201018220203092.png)]
红黑树也是二叉树的一种,是通过左旋/右旋来维持二叉树的平衡
通过将值较小的放到左叶子节点,数值较大的放到右叶子节点来保证查找效率(类似于二分查找)
-
缺点
无论是二叉树还是红黑树,都会因为树的深度过深而造成IO次数变多,影响数据读取的效率
结合之前讲的“局部性原理”,一次读取都是4K的整数倍,也就是一个节点是4K的话,那么上图二叉树找13这个数值需要找5次
-
总结
所以我们提升索引查询效率主要从两个维度,一个是减少IO次数(主要手段),一个是减少单次IO数据量
B树
首先纠正一个错误的认识
没有B-树一说,实际上B-树就是B树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swB7hDkd-1623210785337)(image-20201018221325539.png)]
- B树的特点(m为树的阶)
- 所有的键值分布在整棵树中
- 搜索有可能在非叶子节点结束,在关键字全集内做一次查找,性能逼近二分查找
- 每个节点最多拥有m个子树
- 根节点至少有2个子树
- 分支节点至少拥有m/2棵子树(除根节点和叶子节点都是分支节点)
- 所有叶子节点都在同一层,每个节点最多可以有m-1个key,并且以升序排列
这里不再赘述,摘自知乎某文的说法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xExLVrn0-1623210785340)(image-20201020172432399.png)]
B树的查询流程:
如上图我要从上图中找到E字母,查找流程如下
(1)获取根节点的关键字进行比较,当前根节点关键字为M,E<M(26个字母顺序),所以往找到指向左边的子节点(二分法规则,左小右大,左边放小于当前节点值的子节点、右边放大于当前节点值的子节点);
(2)拿到关键字D和G,D<E<G 所以直接找到D和G中间的节点;
(3)拿到E和F,因为E=E 所以直接返回关键字和指针信息(如果树结构里面没有包含所要查找的节点则返回null);
-
缺点
会存在某个指针对应数据块过大的问题,比如4K一个数据块,每个只存储了1.9k,最终会造成空间浪费,并加深树的结构,导致读取(查)效率降低
B+树
-
B+ 树是B树的基础上做的一个优化
- 每个节点可以包含更多的节点,这么做的理由有二:第一个是降低树的高度,第二个原因是将数据范围变为多个区间,区间越多,数据检索越快(有点类似跳表)
- 非叶子节点存储key,叶子节点存储key+value
- 叶子节点两两指针相互连接(符合磁盘预读特性,InnoDB每次预读为16K),顺序查询性能更高
-
特点
1、B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
2、B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
3、B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
4、B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树的非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。
因为B+树具备以上特点,所以可以对B+树进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找
B+树能做到3次IO取到百万甚至千万级数据(取决于数据大小)
InnoDB和MyISAM文件存储和索引区别
MySQL文件存储结构
1、如果表格b采用MyISAM,在数据库/usr/local/mysql/data/user 中会产生3个文件:
b.frm :描述表结构文件,字段长度等
b.MYD(MYData):数据信息文件,存储数据信息(如果采用独立表存储模式)
b.MYI(MYIndex):索引信息文件。
2、如果表格user采用InnoDB,在数据库/usr/local/mysql/data/user中会产生1个或者2个文件:
b.frm :描述表结构文件,字段长度等
如果采用独立表存储模式,/usr/local/mysql/data/user中还会产生b.ibd文件(存储数据信息和索引信息)
如果采用共存储模式的,数据信息和索引信息都存储在ibdata1中
索引
InnoDB
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCazgp70-1623210785346)(image-20201020181711863.png)]
注意:
-
InnoDB是通过B+树结构对主键创建索引,然后叶子节点中存储记录,如果没有主键,那么会选择唯一键,如果没有唯一键,那么会生成一个6位的row_id作为主键
-
如果创建索引的键是其它字段,那么在叶子节点中存储的是该记录的主键,然后再通过主键索引找到对应的记录
比如有(id,name,age)三列,如果是对name创建索引,那么实际通过name查询的时候会先在name对应的索引树里找到对应记录,但是记录中只存了ID,第二步会通过这个ID去主键索引树找到并将数据返回
综上,主键索引和非主键索引创建和查询方式都是不同的
MyISAM
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0OfkhCoE-1623210785352)(image-20201020182832643.png)]
MyISAM也是采用B+树的方式,但是不再存储具体的数据,而是一个地址,然后根据地址去磁盘里读取对应数据
这也是为什么MyISAM会存储两个文件,而InnoDB只会存储一个文件
存储引擎对比
MyISAM | InnoDB | |
---|---|---|
索引类型 | 非聚簇索引 | 聚簇索引 |
支持事务 | 否 | 是 |
支持表锁 | 是 | 是 |
支持行锁 | 否 | 是 |
支持外键 | 否 | 是 |
支持全文索引 | 是 | 是(5.6 or later) |
适合操作类型 | 大量select | 大量insert、delete、update |
索引的分类
MySQL索引分五类
-
主键索引
主键是一种唯一性索引,但它必须指定为Primary Key,每个表只能有一个主键(一个主键并不表示一个列,也有可能是多个列组成的复合主键)
(这个我们无需创建,MySQL会自动帮我们创建)
-
唯一索引
索引列的所有值都只能出现一次,即必须唯一,值可以为空
(主键索引也是唯一索引的一种,除了非空,MySQL会自动帮我们创建)
-
普通索引
基本的索引类型,值可以为空,没有唯一性的限制
-
全文索引
全文索引的索引类型为FullText。全文索引可以在varchar、char、text类型的列上创建
(基本不用,现在一般全文检索都用Solor、Lucene、ES)
-
组合索引
多列值组成的一个索引,专门用于组合搜索
索引基本知识
谈到索引,一定要往存储引擎上靠,常见的存储引擎有基于InnoDB、MyIsam、Memory三种
索引有什么优点?
- 大大减少了需要扫描的数据量
- 避免了排序和临时表
- 将随机IO变成了顺序IO
索引的用处?
- 快速查找匹配where子句的行
- 从约束中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到最少行的索引
- 如果表具有多列索引,则优化器可以使用索引的任何最左前缀来查找行(最多匹配原则,后文会有详细阐述)
- 当有表连接的时候,从其他表检索行数据
- 查找特定索引列的min或者max值
- 如果排序和分组时在可用索引的最左前缀上完成,则对表进行排序和分组
- 在某些情况下,可以优化查询以检索值而无需查询数据行
为什么MySQL不自动为所有字段创建索引?
首先索引越多查询越快这个认知是错误的
索引最终会写到磁盘里,所以查询的时候会有读盘操作会因为索引增多而变慢
增加删除数据MySQL会重新维护索引,而B+树会存在页分裂和页合并问题
每一次读取磁盘数据都是基于磁盘上的”页“
如果插入数据且页已经装满
这时候对页会有一个分裂的操作
如果数据量比较小,且页有富余空间,这时候会有页合并的操作
这个过程会消耗IO
MySQL建表的时候主键应该怎么选择?
-
自然主键
跟业务挂钩的主键
-
代理主键
跟业务无关的主键,比如ID列
ID列是否需要设置自增?
需要,这样方便MySQL去维护索引
常见技术名词?
-
回表
在InnoDB和MyIsam存储引擎中,底层用的数据结构是B+树
而Memory方式底层数据结构为Hash表
而InnoDB下叶子节点直接放置数据
主键索引可以通过索引直接找到数据并返回
但是实际场景下,我们不经常使用主键去查询数据,可能是name列
我们就可以给name列添加索引
但是name的索引叶子节点并不会存储数据,而是会存储主键的值
查找到对应name后会取主键的值,然后再去主键表中取数据的这种方式就叫回表
-
覆盖索引
首先看两条查询语句
select * from student where name='zhangsan' # 有回表操作 select id from student where name='zhangsan' # 无回表操作
因为基于name的B+树存储的本来就是主键id的值,所以第二条sql不会有回表操作
以上不需要回表操作的过程就被称为索引覆盖(或覆盖索引)
-
最左匹配
组合索引(多列索引),比如用name和age建立组合索引idx_name_age
那么在查找的时候会先查找name再查找age
select * from student where name='zhangsan' # 走索引 select * from student where name='zhangsan' and age=10 # 走索引 select * from student where age=10 # 不走索引,因为跳过了name直接查age,所以按照最左匹配原则不会走进索引 select * from student where age=10 and name='zhangsan' # 走索引,MySQL优化器会自动优化我们的查询
-
索引下推
https://www.bilibili.com/video/BV1K64y1F76m?p=6 3:04
以上是关于MySQL 索引机制以及调优的主要内容,如果未能解决你的问题,请参考以下文章