Mysql的几个灵魂拷问(二)
今天这篇主要是针对索引,开篇前先对Mysql数据库的性能有个整体的认识,一般来讲8c16g的数据库qps在1000~2000,而16c32g的数据库 qps在2000~4000左右,我们讲qps也一般是针对单个系统、服务的性能衡量,而tps:一个业务系统(请求会调用多个其他服务)的性能衡量。
索引篇
索引为什么要放在磁盘中
上一篇数据更新流程中,有讲Innodb引擎会在内存引入buffer pool,但是由于内存容易丢失,一般而言会配合各种日志和刷盘策略,将数据持久化在磁盘文件中,但是和内存相比,从磁盘中读取数据的速度会慢上百倍千倍甚至万倍,所以,我们应当尽量减少从磁盘中读取数据的次数。另外,从磁盘中读取数据时,都是按照磁盘块来读取的,并不是一条一条的读。如果我们能把尽量多的数据放进磁盘块中,那一次磁盘读取操作就会读取更多数据,那我们查找数据的时间也会大幅度降低。如果采用树这种数据结构作为索引的数据结构,那每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块。
为什么不用平衡二叉树(B-Tree)作为索引呢?
平衡二叉树每个节点只存储一个键值和数据的,这就意味着每个磁盘块仅仅存储一个键值和数据!存的越多,二叉树的节点也会越多,并且高度也会极其高,查找数据时也会进行很多次磁盘 IO,效率也非常低下!为了解决这个问题,就可以采用 B 树结构来解决层级过高。
B树的每个节点称为页,页就是前面提到的磁盘块,B 树相对于平衡二叉树,每个节点存储了更多的键值(key)和数据(data),并且每个节点拥有更多的子节点,子节点的个数一般称为阶。 基于这个特性,B 树查找数据读取磁盘的次数将会很少,数据的查找效率也会比平衡二叉树高很多。
为什么要用B+树?
- B 树节点中不仅存储键值,也会存储数据,而B+ 树非叶子节点上是不存储数据的,仅存储键值,如果不存储数据,那么就会存储更多的键值,层高基本不会因为数据扩大而增高(三层树结构大概可以存放两千万数据量)。
- B+树有利于磁盘的IO,数据越多,只需要提升树的阶数(节点的子节点树)即可,树就会更矮更胖,IO次数就更少。
- B+树的所有数据都在叶子节点上,所以B+树的查询效率稳定,一般都是查询3次(根节点存放在内存中)。B+ 树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的。范围查找,排序查找,分组查找以及去重查找变得异常简单,而B 树因为数据分散在各个节点,要实现这一点是很不容易的。
- B+ 树中各个页之间是通过双向链表连接的,叶子节点中的数据是通过单向链表连接的。在 InnoDB 中,我们通过数据页之间通过双向链表连接以及叶子节点中数据之间通过单向链表连接的方式可以找到表中所有的数据。
底层的索引结构
MySQL的数据是以页为基本单位组合而成的,页的大小是16KB,里面包含我们的多条数据,它还有指向下一页的指针和指向上一页的指针。Mysql取页还有一个预读机制(数据库在查询到一条数据的时候会把页中相邻的数据也取出来)。
将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。假设B+Tree的高度为h,一次检索最多需要h-1次I/O(根节点常驻内存),复杂度O(h) = O(logmN)。实际应用场景中,M通常较大,常常超过100,因此树的高度一般都比较小,通常不超过3。
Mysql底层的B+树结构
Mysql会采用页目录的目录项来指向一行数据,这条数据就是存在于这个目录项中的最小数据,那么就可以通过页目录来查找所需数据。非叶子节点是双向链表,叶子节点是单向链表。
B+树的单双链表每个节点就可以理解为是一个页,而叶子节点也就是数据页,除了叶子节点以外的节点就是目录页。非叶子节点只存放了索引,而只有叶子节点中存放了真实的数据。
B+