MYSQL数据库索引,终于懂了!

92 阅读7分钟

不少朋友私信问我MySQL索引底层是怎么实现的,想听听B+树的原理。

其实啊,讲懂B+树真不难,关键是要理解"数据库索引为什么要这么设计"。

为什么用B+树不用B树?
为什么B+树不用哈希做索引,哈希查询O(1),不是更快吗?
为什么B+树能在数据库索引领域称王?

索引存在的意义

想象一下,图书馆里藏着1000万本书,你要找《Java编程》这本书,一本本翻下去,得找到猴年马月?

所以图书管理员会想办法:一楼放历史类,二楼放文学类,三楼放IT类;

IT类里又细分软件、硬件;

软件类再按书名排序......这样一来,找书就快多了。

数据库也是一个道理。存了1000万条数据,想找name="xiaobei"的那条记录,要是一条条查,效率得多低?

这就是索引诞生的原因——用来给数据库查询提速。

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

为什么不用哈希而用树?

说到加速查找,很多人第一反应是哈希。

确实,HashMap的查询、插入、修改、删除,平均时间复杂度都是O(1);

而树结构(比如平衡二叉搜索树),这些操作的时间复杂度是O(log(n))。

从速度上看,哈希明显更快啊,那为啥数据库索引偏偏要用树呢?

这个问题也是面试时的经典题目,能答上来的人真不多。答案其实藏在SQL的实际需求里

如果你的查询都是这种:

select * from t where name="xiaobei";

单条记录查询,哈希确实更合适。像passport这类业务,查询都是单行访问,用哈希索引没毛病。

但问题是,数据库要面对的场景复杂多了:分组查询(group by)、排序查询(order by)、比较查询(<、>)......这些场景下,哈希索引的时间复杂度会退化到O(n),而树结构凭借"有序"这个天然优势,依然能保持O(log(n))的效率。

任何脱离实际需求谈设计,都是耍流氓。

顺便说一句,InnoDB其实不支持手动创建哈希索引,只有自适应哈希索引是引擎内核自己的机制。

面试的时候别傻傻的回答手动创建哈希索引了

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

从二叉树到B+树的进化之路

为了把这事儿讲清楚,咱们得简单过一遍几种树结构。

二叉搜索树大家应该都不陌生吧?

![](files.mdnice.com/user/77339/… =70%x)

这种树是计算机课本里的常客,但它真不适合做数据库索引。

为啥?

数据量一大,树就会长得特别高,查询起来慢;而且每个节点只存一条记录,可能导致频繁的磁盘IO。

B树的出现就是为了解决这些问题:

B树做了几个改进:不再是二叉,而是多叉(m叉);不管是叶子节点还是非叶子节点,都存数据;通过中序遍历可以拿到所有节点。

B树之所以适合做索引,核心在于它巧妙利用了"局部性原理"。什么意思呢?

内存读写快,磁盘读写慢,这个大家都知道。

但磁盘读写有个特点——它不是你要多少读多少,而是按页预读的,一次会读一整页(一般4K)。如果你接下来要用的数据正好在这一页里,就能避免额外的磁盘IO。

局部性原理说的就是:软件设计要尽量让数据读取集中,用到某个数据时,大概率会用到它附近的数据。这样磁盘预读就能发挥最大价值。

B树正是抓住了这一点:因为是m叉的,树的高度大大降低;每个节点能存多条记录,如果把节点大小设置成一页(比如4K),就能充分利用预读特性,极大减少磁盘IO。

B+树在B树基础上又做了优化:

改进主要有两点:

  1. 非叶子节点不再存数据,数据都存在同一层的叶子节点上;
  2. 叶子节点之间加了链表,获取所有数据不用再中序遍历了。

这些改进让B+树有了更突出的优势:

范围查询简直不要太方便。定位到最小值和最大值后,中间的叶子节点就是结果集,不用回溯。SQL里范围查询用得特别多,这是B+树最大的杀手锏。

叶子节点存实际记录,记录存储相对紧密,适合大数据量的磁盘存储;非叶子节点只存记录的主键,用来加速查询,适合放内存。

不存实际记录,只存KEY的话,相同内存能存下更多索引。

咱们来算笔账,看看m叉B+树到底能把高度降低多少。

假设局部性原理下,一个节点大小就是一页,4K;一个KEY占8字节,那一个节点能存500个KEY;m叉树大概m/2 <= j <= m,也就是说差不多是1000叉树。

那么:

  • 一层树:1个节点,500个KEY,占4K
  • 二层树:1000个节点,50万个KEY,占4M
  • 三层树:100万个节点,5亿个KEY,占4G

看到没?存5亿条数据,树的高度只要3层,索引也就占4G内存。这效率,简直了。

说到底

数据库索引就是用来加速查询的。虽然哈希是O(1),树是O(log(n)),但SQL有太多"有序"需求,所以数据库选择了树型索引。

InnoDB不支持手动建哈希索引,只有内核自己的自适应哈希。

数据预读的思路值得琢磨:磁盘不是按需读取,而是按页预读,一次多读点,未来就能少跑几趟磁盘。

局部性原理更是软件设计的精髓:数据读取要集中,用到某个数据时大概率会用到附近的数据,这样预读才能真正提升磁盘IO效率。

数据库索引最常用B+树,理由很充分:特别适合磁盘存储,能充分利用局部性原理和磁盘预读;树高度很低,能存海量数据;索引本身占用内存小;对单点查询、范围查询、有序查询的支持都很出色。

这就是B+树能在数据库索引领域称王的原因。

最后说一句(求关注,求赞,别白嫖我)

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

本文,已收录于,我的技术网站 cxykk.com:程序员编程资料站,有大厂完整面经,工作技术,架构师成长之路,等经验分享

求一键三连:点赞、分享、收藏