MySQL性能调优
- 1. 索引数据结构红黑树,Hash,B+树详解
- 1.1 什么是索引?
- 1.2 二叉树
- 1.3 红黑树
- 1.4 B-Tree
- 1.5 B+Tree(B-Tree变种)
- 1.6 Hash索引
- 2. 存储引擎
- 2.1 MyISAM索引
- 2.2 INNODB
- 3. 索引最左前缀原理
本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。
主要内容
1、索引数据结构红黑树,Hash,B+树详解
2、千万级数据表如何用B+树索引快速查找
3、聚集索引&聚簇索引&稀疏索引到底是什么
4、为什么DBA总推荐使用自增主键做索引
5、联合索引底层数据结构又是怎样的
6、Mysql最左前缀优化原则是怎么回事
本次主要是索引
1. 索引数据结构红黑树,Hash,B+树详解
1.1 什么是索引?
索引是帮助MySQL高效获取数据的排好序的数据结构。
“排好序” 这三个字其实就是对索引最好的形容和体现。
我们可以简单把索引比喻成为书本的目录页,当然这么说太过于抽象,并没有把索引的底层特性说明白。
索引数据结构包括
- 二叉树
- 红黑树
- Hash表
- B-Tree
下面我们将依次分析 二叉树、红黑树、Hash表、B树、B+树
1.2 二叉树
假设我们有一条sql语句是:
select * from t where t.col2=89;
如果这个表是下图所示:
正常情况下如果是没有索引的,他就会在数据库中的第一条开始从上到下一条一条比对。直到找到所要找到的元素。
我们也许会想这张表里面这些数据不都是挨着呢吗,按理来说查起来应该很快的。其实不是的。
我们数据是放在磁盘里的,我们第一次插入的数据放到这个点上,但是等我们下次去插入的时候不一定是挨着这个点去放的,可能放到其他地方去了。原因就是第一条数据的插入与第二条数据的插入可能相差几天,而磁盘写数据是一个磁道一个磁道写的,也可能这几天有其他的程序把他旁边都写满了。说白了就是我们一张表上的数据是在磁盘上随机分布的。他不一定是挨在一起的。
由于MySQL是放在磁盘里面的,每次查找下一个节点都要经过一次IO操作!
综上我们减少我们拿到我们所需要数据的查找次数,只要把次数控制在一定范围之内,效率就会比较高.
基于此我们的索引就诞生了。把clo2建一个索引,把这个索引放到右侧这个二叉树里面。每一个节点都有一个key和value。key是是clo2里面的值,value就是磁盘文件地址。
建立索引之后查找89原来是需要6次查找,现在只需要2次。
但其实mysql底层并没有用二叉树。
我们来看一个例子。假如我们是想用clo1作为索引,我们试一下哈,建立二叉树:
数据结构网址: https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
我们都知道二叉树的元素是右边的大于左边的。
当我们使用clo1作为索引时变成了一个链表,但其实他是一个二叉树,但是这个二叉树和链表一样了。当我们还查找之前89那一行时,仍然是进行了6次查找。与之前的全表查询没有任何查表哈。并没有做到提升效率。这就是mysql底层并没有用二叉树的原因
,因为他对这种单边增长的并不友好,单边增长的倾斜问题,变相为链表,查找效率低,与全表扫描差别不大
。那有没有更好的数据结构呢?
继续往下看
1.3 红黑树
平衡二叉树(AVL)树
是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而的英文旋转非常耗时的。所以平衡二叉树(AVL)适合用于插入与删除次数比较少,但查找多的情况。
红黑树
在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(logn)。所以红黑树适用于搜索,插入,删除操作较多的情况。
二者的区别:
红黑树是一种特殊的AVL,它不会遵循AVL过于苛刻的“平衡规则”,而是用自己的一套红黑规则来实现平衡!而且在实现的效率上来看,是要优于AVL的!
但是同样它也不是最优的解决方案!!!
在数据库中,如果数据量比较大的情况下,假设有2000万条记录,那么该树将会达到24层!
也就是说大数据量情况下,高度不可控
其实我们可以对红黑树进行优化,也就是控制树的高度。B树
1.4 B-Tree
B-Tree
- 叶节点具有相同的深度,叶节点的指针为空
- 所有索引元素不重复
- 节点中的数据索引从左到右递增排列
也就是每个叶节点可以放很多个数据,这就比刚才的红黑树优化了一点点。
但是其实Mysql底层也没有使用这个纯粹的B树,而是对这个B树进行了优化,做了一点点改造,也就是B+树
1.5 B+Tree(B-Tree变种)
- 非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
- 叶子节点包含所有索引字段
- 叶子节点用指针连接,提高区间访问的性能(也就是说在每个叶子之间有一个指针,这个时B-树所没有的。有了这个指针可以范围查询。)
一个页节点的大小是16KB
Mysql为什么要这么设置呢?这么设置的好处是:
一个节点写到硬盘上面是一次I/O,我们在内存里面定位一个数据的时候,给一次I/O相比可以忽略不计。
其实我们在查找一个数据的时候最浪费时间的是node这三个节点,就是红色圈起来的,而在一个node里面进行定位折半查找的时间是很短的。
那我们肯定就会有这样的疑问,我们直接把数据全部放到一个叶子几点上不就可以了吗?如图:
然后都写到内存里面,直接在内存里面进行比对。
这肯定是不合适的,如果我们一张表有上千万条记录,我们整个数据库有几千张表。这样会把内存撑爆的。其实数据多的话都在内存里进行比对也不会快啊。其实上面那个叶节点的大小选择是很有讲究的,16KB是由道理的。bigint大概占8个字节,下一个磁盘的文件地址大概分配的空间是6个字节。那么(16KB)/(8+6)B=1170.也就是可以放1170个数据。
这样以来一棵树大概可以存储2千多万的数据
其实一般情况下会把根节点放到常驻内存当中。
在B树与B+树上面为什么mysql选择了B树?B+树把data都挪到第三层去了,为什么?
对于树结构来说影响我们查询某一个元素的效率就是和树的高度有关。
如果一个2千万多条的数据用B树进行存储,树的高度是多少?
16的h次方等于两千万,而这个h肯定远远大于3.
之所以把data数据挪到第三层就是为了让其空出来。
如果数据超过了2千多万怎么办?可能就要分库分表了。
数据表都是存在磁盘上的。其实这些数据表也是存在磁盘上的。
这种存储引擎到底是形容数据库的还是数据库表的?是数据库表
1.6 Hash索引
- 对索引的key进行一次hash计算就可以定位出数据存储的位置
- 很多时候Hash索引要比B+树索引更高效
- 仅能满足“=”,“IN”,不支持范围查询
- hash冲突问题
将Col3做为Hash索引。
他的索引结构大概是下图所示:
在一些情况下Hash的效率比B+树的要高,因为Hash计算出值之后很可能一下就查出来了。
但是在99.99%的情况下还是使用的是B+树。
因为不支持范围查询、还存在Hash冲突问题。比如查询大于Alice的所有元素,你查不到啊,因为不存在比大小。所以最后还得全表扫描。
那么B+树就支持范围查找吗
这个时候就知道B+树叶子节点的第三层中,叶子与叶子之间存在指针的作用了。支持范围查找
比如我们要差一个Col > 20的
2. 存储引擎
2.1 MyISAM索引
frm数据表结构相关的信息存在这里。
MYD:M指的就是MYISAM,D指的就是data,
MYI:指的就是索引。
假设把Clo1作为索引字段,如果查询的是Clo1=30,MYI文件会讲Col1以B+树的形式组织好。这个表的记录是存在MYD文件的。
2.2 INNODB
InnoDB索引实现(聚集)
- 表数据文件本身就是按B+Tree组织的一个索引结构文件
- 聚集索引-叶节点包含了完整的数据记录
- 为什么建议InnoDB表必须建主键,并且推荐使用整型的
自增
主键? - 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
innodb对应两个文件,frm文件就是表结构相关的信息
ibd文件是存数据和索引的。
聚集索引:叶节点包含了完整的数据记录。
那个MyISAM索引就是非聚集索引。
因为这个叶子节点中没有包含Col2与Col3
因为他们是分开存储的。
聚集索引与非聚集索引,但从查找时间上哪一个更快一些?
单纯索引的角度考虑 ,聚集的肯定更快一点。聚集索引信息在一个文件上;非聚集索引是要跨文件查询的。
为什么建议InnoDB表必须建主键,并且推荐使用整型的自增主键?
Mysql在设计这个InnoDB表的时候,在进行存储数据的时候必须要使用一个B+树进行组织,如果主键自带索引,我们就可以直接使用主键组织整张表的数据。
那如果我不建主键数据又是如何存储的呢?
他会从第一列开始判断,选择一列中没有重复的元素作为索引数据来组织B+树。
如果没有选到呢?
那他就会帮你建一个隐藏列。这个隐藏列会去维护唯一的ID,利用隐藏列去组织整张表。
为什么推荐使用整型的自增主键
UUID既不是整型,也不是自增,之间有什么优劣?
我们在找元素的时候是从根节点开始查。在找索引定位的过程中中间要经历很多次比大小。那到底是整型比大小快还是UUID比大小快呢?UUID字符串比大小还要逐位去比较ASCII码。肯定是整型效率高一点。
为什么要推荐使用自增的主键呢?为什么要这么做呢
这时候不得不先看Hash索引,为了排版,还是将Hash索引放到1.6里面。
学完之后我们继续看为什么要自增。
其实B+树叶子节点之间是一个单箭头,其实Mysql的底层是一个双箭头。Mysql是对这些数据结构进行优化的。
如果我们是自增的就不需要,一直往后插入就行。可以插入就插,不可以就再创建一个放进去,他不会硬插入使其分裂。
因此肯定是自增的效率更高。
为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
对于MyISAM的主键索引与非主键索引是一样的,没有什么区别。
对于innodb主键索引与非主键索引是有区别的。二级索引,也称为辅助索引。
二级索引里面的值放的是主键。
为什么要建二级索引
如果一张表的字段很多,主键索引不够用了,我们就建二级索引。
3. 索引最左前缀原理
联合索引的底层存储结构长什么样?
联合字段共同组合成一个索引。(a,b,c)。一般来说一个表不建议建太多单值索引的。
你会发现你有很多种很多种sql语句,如果我有5个sql语句,难道我去建5个单值索引吗?
不会,一般会想办法通过1-2个或者2-3个联合索引把百分之八九十的sql查询的条件全部覆盖。
对于innodb表来说,如果通过二级索引进行查找,先要定位到主键,再通过主键去聚簇索引里面找到那一条记录。
其实这个二级索引也是非聚集索引。非聚集索引都要做一次回表的操作。
联合索引也是排好序的B+树。
这三个字段是B+树存储还得是排好序的。应该如何维护这个联合索引。
先比较第一个字段,如果确定不了再比较第二个字段,如果还确定不了再比较第三个字段。
索引最重要的一点就是排好序,为了有助于我们理解,我们举一个例子来看看到底怎么排好序的。
那么这三条语句哪一个会走索引。当然是第一条,因为是左列原理。
当想使用这个联合索引,一定要按照它建这个索引的一个顺序去用,就是name、age、position这个顺序。
根据下面这个图,第2、3条不走索引
最左前缀原理为什么是这样定的,为什么是这样子的,一定要先用name、age、position才会走这个索引呢
因为这个是排好序的,他排好序肯定是按照索引字段的从左到右按照顺序进行排列的。
现在跳过name第一个字段,我们按照age进行查询,age是排好序的吗?他就不是了。这样都不符合索引了,索引就是要排好序。
这就是排好序是怎么回事。