Java软件架构师成长之路(二)Mysql 索引深入理解底层数据结构
文章目录
1. 什么是索引
是帮助 MySQL 高效获取数据的数据结构,实现了高级查找算法的数据结构,索引一般以文件形式存储在磁盘上。
- 如此来看,索引首先是一种数据结构。
- 我们常用的数据结构有:
- Hash 表
- 二叉树
- 红黑树
- B Tree
- B+ Tree
2. MySQL 中的索引为什么要使用数据结构的知识呢?
- 比如现在有一个表格:
我们现在如果用 MySQL 对其进行存储,我们可以使用多种数据结构进行存储,下面我们就来比较一下各种数据结构的优劣:
2.1 Hash 表
- 使用哈希表的时候,如果想把 id 当做索引,user_name 信息作为值的话,要对每一个 id 使用 hash 函数来分配一个物理地址,这个物理地址中存放着信息。
- 可以看到,图中把 id 当做索引,所以对 id 进行了 hash 函数处理,最终对应每一个 key 生成了一个 value 的值,这个值就是存放对应信息的物理地址。
- 在进行信息检索的时候,只要匹配到对应的物理地址值,就可以取出其中的信息。
【Hash 表的好处】:
- 复杂度是 O(1) 查找效率特别高
【hash 表存在的问题和局限性】:
- 不能进行范围查询,如下图所示,当我想要的数据 id 是 1~6,就无法完成检索,因为 hash 表内部使用的是散列表
- 无法进行 MySQL 数据的排序,与上面的原因相似,只能找到记录,但由于是散列表所以无法进行排序
2.2 二叉树
按照普通的 “平衡” 二叉树来说,树的结构是有优势的,比如我们想要搜索 7 这个数字,我们只需要查找三次 4 --> 6 --> 7 就可以查到了。
- 但是二叉树的问题在于,二叉树的结构与存储的时候的顺序有很大关系。比如要得到上面的这个图,我们存储的顺序就是:4–>2–>1–>3–>6–>5–>7
- 如果我们换一种方式,我们存储数据的时候按照 1–>2–>3–>4–>5–>6–>7 的顺序,得到的二叉树就会如下图所示
- 比较上面两种不同的存储顺序,第一个图的情况下,我们要查找 7 只需要查找 3 次,而第二个图的情况,我们要查找 7 则需要查找 7 次,这就跟没有使用数据结构的情况一样了,完全就是顺序查找了。而这种情况在现实生活中极为多见,我们存储数据,一般都按照从小到大的顺序进行存储,例如:班里有50个人,我总要按照第 1 个,第 2 个···· 第50个的顺序来排列。所以,二叉树的数据结构在生活中的大多数情况下都会变成效率很低的第二种情况,也就是我们说的 “ 右倾 ”现象。
如果我们能够尽量保持二叉树的 “平衡” 也就是保持左边和右边均衡,那么查找效率就会提高;这也就衍生出了红黑树。
2.3 红黑树
如图所示,当我们从 1–>2–>3–>4–>5–>6–>7–>8–>9–>10–>11–>12–>13–>14–>15 的顺序存储的时候,我们可以看到,比起普通的 二叉树 的结构,红黑树通过 “旋转” 调整,已经很大程度上解决了 二叉树存在的 “右倾” 问题,但是问题依然很明显,所以使用红黑树作为 MySQL的索引的数据结构依然不明智。
那么,这就要求我们对这种数据结构进行进一步的优化,进而产生了 B+ tree。
2.4 B+ tree
- B+ Tree 这种数据结构的特点在于:在非叶子节点中不存放数值的信息,只存放数据的指向;使得系统最终能够找到叶子节点中的数值。
- 当节点和存储信息增加的时候,永远都是近乎平衡的结构
- 而且,B+ Tree 的结构支持 “范围查询” ,因为在底层叶子节点之间,都是使用指针进行连接,所以当我要查找 id < 7 的位置,我们就可以找到 id = 7 然后使用指针来找到前面所有的信息。
3. MySQL存储引擎
MySQL 存储引擎通过使用不同的方法来对数据和索引进行存储。主要分为两类:
-
非聚簇(聚集)索引方式:存储的时候把 数据文件 和 索引文件 分开存储
-
聚簇(聚集)索引方式:存储的时候吧 数据文件和索引文件 存在一个文件里面
【注意】: 我们所说的存储引擎,其作用的对象是 Table 表,而不是 数据库
在这里创建了两个 Table,分别叫做 user 和 user2,采用了不同的索引方法。
从这里可以看出来,选择何种 ENGINE 只是 Table 索引的时候的一个选项,所以其作用的对象是 Table
3.1 MySAM 引擎(非聚集索引方式)
- 使用 MySAM 引擎建立索引方式的时候,生成了三个文件:
- user2.frm 中包含的是创建表的语句
- user2.MYD 是 user2 表中的数据文件, D --> data
- user2.MYI 是user2 表中的 索引文件, I --> index
3.1.1 MYD 和 MYI 文件中的索引实现和数据结构细节
图中展示的是使用 id 作为索引:
- 可以看到,图中的左边为 MYI 文件,即整个 B+ Tree 的叶子节点存储的全都是能够链接到 MYD 文件的物理地址
- 右边的 MYD 文件,则对应不同的物理地址写着详细的数据信息。
类似的,下面的图中展示使用 user_name 作为索引的情况:
我们可以在数据表中按照自己喜欢的方式进行索引,既可以用 id 作为索引,也可以吧 user_name 项作为索引,最终的结果都是指向同一个物理地址,进而在 MYD 中找出我们需要的数据信息。
3.2 INNODB 引擎(聚集索引方式)
- 使用 INNODB 引擎建立索引方式的时候,生成了两个文件:
- user.frm 中包含的是创建表的语句
- user.ibd 是 user 表中的数据文件 + 索引文件
可以看出,idb 文件吧索引和数据放在同一个文件中,所以其内存的占用就会大很多,而 user2 table 分开存储,数据量就会小很多。
3.2.1 idb 文件中的索引实现和数据结构细节
先来看以 id 来索引的这种方式:
与前面不同的是,由于没有额外的文件来存储数据,idb 文件会在叶子节点中把所有的表信息都存进去。
值得一提的是,当我们来看使用 user_name 这种方式进行索引的时候,我们在最后的叶子节点中存的是 id:
在叶子节点中存放 id 而不存放 表中的数值的原因是: 如果我们在这里再存一遍表中的所有信息,会造成冗余,而存 id 信息,则可以通过最终的 id 信息去搜索以 id 为索引建立起的树,最终得到表中的值,整个过程如下图所示:
- 使用 user_name 索引的方式,最终找到叶子节点中存放的 id 值
- 通过 id 值搜索 id 为索引建立的树,最终找到表的值 1,a。