☰
MySQL索引原理详解:从B+树到回表,彻底搞懂为什么查询快
2026/10/11 20:59:12 网站建设 项目流程

1. 先搞清楚慢在哪:MySQL 查询为什么会慢

1.1 没索引的查询在做什么

先别急着聊索引,面试里问到“为什么快”,最忌讳一上来就背 B+ 树定义。正确的打开方式是先回答“没有索引的时候,一条查询到底在干什么”,因为慢的根本原因就藏在里面。

假设有一张用户表,里面存了几千万行数据,主键是自增 id。你执行一条非常简单的查询:

SELECT * FROM user WHERE account = 'zhangsan001';

在没有索引的情况下,MySQL 会怎么做?它会从磁盘上把这张表的数据页一个一个读出来,从头开始逐行比较 account 字段,直到找到符合条件的记录。这个过程叫全表扫描。听起来好像也不是不能接受,但你仔细算一下账:

  • 一张 2000 万行的表,假设每行平均 200 字节,数据总量大概是 4GB。
  • 哪怕你用最理想的比例压缩,实际要扫过的数据页也可能高达几十万个。
  • 每个页的读取都有磁盘 I/O 开销,机械硬盘随机读一次大概 5-10ms,SSD 也要 0.1ms 左右。

几千万行的表,全表扫描单次查询轻松超过 100ms,甚至到秒级。这还没算上并发场景——几十个这样的慢查询同时进来,数据库的连接池、CPU、I/O 全部被打满,系统直接卡死。很多线上事故的根因,翻到最后就是一条没走索引的全表扫描查询。

所以索引最本质的作用不是“让查询变快”,而是减少要读取的数据量,把“扫全表”变成“走树查找”,把“几千万行”变成“几十层比较”。这个思路在面试里讲清楚,比单纯背概念有说服力得多。

1.2 磁盘 I/O 才是真正的瓶颈

既然慢的原因是数据量大,那为什么不去优化磁盘速度,或者把数据全部塞进内存?这个反问经常会在面试里被抛出来。答案要从计算机的存储层次说起。

CPU 处理一条指令大约需要 0.3 纳秒,内存随机访问大约需要 50 纳秒,而磁盘随机访问需要几毫秒。这几毫秒和几十纳秒之间的差距,是数量级的。也就是说,一次磁盘读取所花费的时间,足够 CPU 执行几百万条指令。数据库的大部分时间其实都花在等待 I/O 上,而不是计算上。

所以 MySQL 索引的设计目标,就是尽可能减少磁盘访问次数。B+ 树恰恰就是围绕着“用最少的磁盘 I/O 找到目标数据”这个目标演变出来的。后面讲 B+ 树结构时,你会反复看到这个逻辑——树的高度、每个节点能存多少条记录、叶子节点怎么连接,全都和磁盘 I/O 次数直接挂钩。

注意:索引的本质是一种“用空间换时间”的做法。它额外占用磁盘,更新时候也要额外维护,换来的却是查询时大幅度减少 I/O。没有免费的午餐,任何索引设计都要在这三者之间做平衡。

2. 从数据结构层面拆解:为什么偏偏选 B+ 树

2.1 哈希索引:查找确实是 O(1),但它不满足场景

哈希索引是你一定要先排除的选项。它按 key 计算哈希值,然后直接定位到对应的槽位,单条等值查询的时间复杂度确实是 O(1),理论上比 B+ 树的 O(log n) 还快。但 MySQL 的 InnoDB 为什么默认不用哈希索引?

原因很实在:它只能处理等值查询。你一旦执行范围查询、排序、前缀匹配、分组,哈希索引就完全派不上用场。而现实业务中,范围查询(age > 18、time BETWEEN ...)、排序(ORDER BY)、分组(GROUP BY)是占比极高的需求。一个只能应付“精确匹配”的结构,撑不起真正的业务查询场景。

另一个问题是,哈希表在极端情况下有哈希碰撞,碰撞多了性能反而退化。而且它支持不了最左前缀匹配这种前缀索引优化。所以在 InnoDB 里,哈希索引更多是作为一个自适应索引存在,由存储引擎自己决定是否在热点页上建立,而不是让用户去建一张哈希索引表。

2.2 二叉树、AVL 树、红黑树:为什么树越高越吃亏

如果把索引结构设计成普通二叉树,在均匀分布的情况下,查找复杂度是 O(log n)。但普通二叉树的形态非常不稳定,最差情况下会退化成链表,查询复杂度直接变成 O(n)。

那么 AVL 树和红黑树是不是就行的?它们是平衡树,能保证树的深度不是太离谱。问题在于,这两种树每个节点只能存一个 key,导致同等数据量下树的层级非常深。举一组数字你就懂了:

  • 100 万条数据,存储在二叉平衡树中,树高大约在 20 左右。
  • 1000 万条数据,树高大约在 24 左右。
  • 1 亿条数据,树高大约在 27 左右。

每次访问下一层,就意味着一到两次磁盘 I/O。二十多次磁盘 I/O,每次都按毫秒级算,这查询根本快不起来。所以我们需要一种“每个节点能多装点东西”的树,让树的宽度变大、高度变矮。这就是多路平衡查找树的基本思路。

如果节点能装 100 个 key,那么树的分叉数可以做到 101。同样是 1 亿条数据,树的高度只需要 4 层左右。每层一次磁盘 I/O,四层 I/O 就能定位到数据,这跟二十多层完全不是一个量级。

2.3 B+ 树到底强在哪:三个点讲透它

B+ 树的优势,可以从三个层面展开。

第一个点是“多路分叉”。B+ 树不像二叉树那样每个节点只有两个子节点,而是一个节点可以存很多条记录,子节点数量跟着记录数走。InnoDB 默认的页大小是 16KB,在这个空间里能塞下多少条索引记录取决于行大小。假设主键是 bigint(8 字节),加上 6 字节的指针开销,一个页大约能存一千多条索引项。于是树的样子变成:根节点分叉出上千个分支,每个分支又继续分叉。数据量再怎么膨胀,树的高度增长得非常缓慢。

第二个点是“只有叶子节点存数据”。在 B+ 树中,非叶子节点只存索引键值和指向子节点的指针,真正的数据行全部放在叶子节点。这样非叶子节点在同样的 16KB 页空间内可以存放更多的键值和指针,分叉数更大,树就变得更矮。而在早期的 B 树(不是 B+ 树)里,每个节点都保存数据,同样的数据量下树的层级更深,I/O 次数更多。

第三个点是“叶子节点用链表串联”。B+ 树的所有叶子节点按照键值顺序连接成一个双向链表。这意味着当你需要做范围查询时,找到起点后顺着链表往后遍历就行,不需要回头再去父节点找兄弟节点。像 SELECT * FROM user WHERE age BETWEEN 20 AND 30 这种查询,B+ 树的处理效率非常高,这也是 B+ 树替代 B 树成为主流数据库索引结构的决定性理由。

我用一个生活化类比帮你理解:二叉树像一个人在迷宫每个路口只能看到一个门牌号,走两步就得停下来问一次路;B+ 树的非叶子节点像一本目录,翻开一页能看到一千个门牌号和对应楼层,四页就能定位到你要的房间,而且所有房间是按门牌号排成一排的,找完 20 号往后顺路就能找 21、22、23 号。

面试关键点:B+ 树不是为了“查询快”这一个目标服务的,它是同时为“查询快”“范围查询快”“排序快”“插入删除稳定”这多个目标设计的。如果你只说了一个点,说明理解还比较浅。

2.4 InnoDB 的页与预读机制

另一个容易忽略的细节是 InnoDB 的页。InnoDB 是面向页的存储引擎,页是磁盘和内存之间交互的最小单位,默认 16KB。当你访问某一行数据时,存储引擎不是只把这个 8 字节读到内存,而是把这一行所在的整个页读进来。

这听起来像是浪费,其实是利用了一个非常重要的局部性原理。因为相邻的行大概率会被一起访问,一次读进一个页,后续的访问可能直接命中内存,不需要再发一次磁盘请求。这个机制叫预读。

在 B+ 树中,同一个叶子节点内的多条记录,天然就在一个或者连续几个页里。对于范围查询来说,顺着叶子链表往下走的过程,基本上就是读取连续页的过程,顺序 I/O 的效率远高于随机 I/O。这也是为什么主键自增的插入性能好——新记录追加到叶子节点的末尾,写的是顺序页,不会到处随机写。

这套“页+预读+顺序访问”的配合,让 B+ 树在做范围查询时表现得尤其出色。你如果只解释了“树的高度低”,没有提到页和 I/O,那深度还是差了一层。

3. 从 MySQL 实现层面拆解:聚簇索引和二级索引

3.1 聚簇索引:数据自己就长在树上

InnoDB 的表是索引组织表,这句话非常关键。它的意思是:表里的数据行并不是单独存放的,而是直接保存在主键索引的叶子节点上。这张以主键为 key 的 B+ 树,就是聚簇索引。

当你执行 SELECT * FROM user WHERE id = 123 时,MySQL 从聚簇索引的根节点出发,按照 key 一路向下查找,定位到叶子节点时,整行数据已经在手里了。一次索引查找,直接拿到全部字段,不需要二次回表。这就是主键查询快得离谱的原因。

聚簇索引有几个特性值得记住:

  • 数据行物理上按主键顺序排列,所以范围查询主键 ID 时效率极高。
  • 如果一张表没有定义主键,InnoDB 会找第一个非空的唯一索引作为聚簇索引;如果也没有,它会生成一个隐藏的 rowid 作为聚簇索引。
  • 聚簇索引的叶子节点是数据页,不是索引页,因此占用空间很大,通常一个表只会有一个聚簇索引。

基于这一点,面试里经常会问“为什么不建议用 UUID 做主键”。UUID 有 36 个字符,远大于 bigint 的 8 字节。如果用它做主键,聚簇索引的所有非叶子节点能容纳的分叉数会大幅减少,树变高;更糟糕的是 UUID 无序,插入时要随机在树中间插入,数据页频繁分裂,写性能非常差。而自增 bigint 主键就是奔着这个设计去的:只需要往叶子末尾追加,顺序写,几乎不会造成页分裂。

3.2 二级索引与回表:为什么有时候要“查两次”

除了聚簇索引外,其他的索引都叫二级索引(也叫辅助索引、非聚簇索引)。二级索引的 B+ 树叶子节点不存整行数据,只存当前索引列的值和对应主键值。

举个例子,你在 account 字段上建了一个普通索引:

ALTER TABLE user ADD INDEX idx_account (account);

执行查询:

SELECT * FROM user WHERE account = 'zhangsan001';

MySQL 会先去 idx_account 这棵 B+ 树里查 "zhangsan001",找到叶子节点后,拿到这行记录的主键 id。然后它再用这个主键 id,回到聚簇索引里查一次,最终取出完整行。这一步操作在 MySQL 里叫回表。

多了一次树查找,性能肯定有所损耗,但整体仍然远快于全表扫描。更进一步优化,如果 SELECT 只需要查询 account 字段本身,回表就可以省掉,这就牵出了“覆盖索引”的概念。

3.3 覆盖索引和索引下推:两个直接提效的优化手段

覆盖索引指的是:所需要查询的列,全部包含在同一个二级索引里。这样查询只需要遍历二级索引的 B+ 树就能拿到结果,不需要回表。

假设你只需要查 account 和 nickname:

SELECT account, nickname FROM user WHERE account = 'zhangsan001';

如果你建的索引是 idx_account_nick(account, nickname),那么执行这条 SQL 时,二级索引的叶子节点里已经同时包含 account 和 nickname 两列,直接返回即可,完全不用碰聚簇索引。这个优化在应对高频、重复的 SQL 时效果极其明显,可以减少一半以上的磁盘 I/O。

索引下推(Index Condition Pushdown,简称 ICP)是 MySQL 5.6 以后引入的优化。在没有 ICP 之前,二级索引查出来的记录要先回表,再在完整行上进行其他条件过滤。有了 ICP 之后,MySQL 允许在遍历二级索引时,直接先用索引中已有的字段做一部分条件判断,过滤掉不合格的索引项,减少回表次数。

举个例子:

SELECT * FROM user WHERE account = 'zhangsan001' AND nickname LIKE '张三%';

如果你建立了联合索引 idx_account_nick(account, nickname),存储引擎在遍历二级索引时,先通过 account 定位,再在索引里直接判断 nickname 是否以“张三”开头,不满足条件的直接跳过。这样需要回表的记录数大幅减少。把这个点讲出来,面试官会觉得你不只是知道概念,而是真的在优化线上 SQL 时关注过执行计划。

注意:不可能每个查询都能用覆盖索引,因为索引列越多,页能容纳的记录越少,树会越高,写入成本也会上升。覆盖索引不是越多越好,而是只针对高频且固定的查询组合去设计。

4. 为什么索引能“快”到真实业务中:一个查询的过程全复盘

4.1 从一条 SQL 开始完整走一遍

为了让你真正理解索引是怎么生效的,我模拟一个典型场景。假设有一张订单表:

CREATE TABLE `order` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL, `user_id` bigint NOT NULL, `amount` decimal(10,2) NOT NULL, `status` tinyint NOT NULL, `create_time` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_user_create` (`user_id`, `create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

现在执行:

SELECT order_no, amount FROM `order` WHERE user_id = 10086 AND create_time >= '2024-01-01' ORDER BY create_time DESC LIMIT 20;

这条查询的回溯路径是这样的:

  1. 优化器选择 idx_user_create 这个联合索引。
  2. 在这个联合索引的 B+ 树中,先定位 user_id = 10086 的第一条记录。
  3. 因为 create_time 也是索引的第二列,所以范围内的记录已经在索引上天然按 create_time 排序,直接从这个位置开始往前遍历叶子链表。
  4. 如果需要 order_no 和 amount 字段,二级索引叶子节点有 order_no 吗?没有,所以每条记录都需要回表到聚簇索引取 amount。
  5. 查询真正需要 order_no 和 amount,如果 dev 在开发时意识到这一点,把索引改成 idx_user_create_order(user_id, create_time, order_no, amount),那连回表都省了。

这个例子同时展示了联合索引中的最左前缀原则、索引排序能力、覆盖索引的回表收益,这是面试里非常完整的一套连招。

4.2 执行计划里怎么看索引有没有生效

日常写 SQL 和面试问答不一样,面试讲究原理,开发讲究验证。验证索引是否生效,最直接的工具就是 EXPLAIN。

EXPLAIN SELECT order_no, amount FROM `order` WHERE user_id = 10086 AND create_time >= '2024-01-01' ORDER BY create_time DESC LIMIT 20;

你主要看这几个字段:

字段含义怎么判断好不好
type访问类型结果从好到差一般为 all、index、range、ref、eq_ref、const,const 最理想,all 最差
key实际用到的索引不是 null 说明走索引了
rows预估扫描行数越小越好
Extra额外信息看到 Using index 说明覆盖索引生效;看到 Using filesort 说明排序没走索引

如果执行计划里 rows 接近全表行数,或者 type 是 all,那你基本可以确定索引没被用上。常见的原因包括:索引列参与了函数运算、隐式类型转换、联合索引没满足最左前缀、优化器觉得全表扫描比索引更快等。

这部分虽然是你实际工作的基本功,但面试里也非常加分,因为你表现出了“能设计和验证”的能力,而不是只背结论。

4.3 联合索引:最左前缀原则背后的设计逻辑

联合索引是面试高频点,原理其实不复杂。你把联合索引理解成一本字典的目录:第一列是页码排序的部首,第二列是在同一个部首下的笔画数排序,第三列是在同一个部首和笔画数基础上的排序定义。所以联合索引的排序规则是先按第一列排,再在第一列相同的情况下按第二列排。

这就带来了最左前缀原则:你查询条件里如果用到了联合索引的列,必须从最左侧的列开始连续匹配,索引才能走全。比如 idx_user_create 是 (user_id, create_time) 两个字段:

  • WHERE user_id = 10086 AND create_time >= '2024-01-01':能充分利用索引。
  • WHERE user_id = 10086:能利用索引,但只用到第一列。
  • WHERE create_time >= '2024-01-01':无法使用这个联合索引,因为跳过了第一列。

这个原则也提示我们建联合索引时,要把区分度高、查询频繁、经常作为等值条件的列放在最左边。区分度可以用一个简单的 SQL 验证:

SELECT COUNT(DISTINCT user_id) / COUNT(*) FROM `order`;

如果这个值接近 1,说明这一列的区分度很高,非常适合放在联合索引左侧。

4.4 索引能加速哪些类型的查询,哪些反而会拖慢

索引不是万能的,它能加速的操作范围要清楚。加速效果明显的是等值查询、范围查询、排序、分组、去重,以及两张表做连接时对连接字段的查找。这些操作在 B+ 树上都能大幅减少扫描范围。

但会拖慢的地方也同样明显:

  • 写入操作:每次 INSERT、UPDATE、DELETE 都要同步维护所有索引的 B+ 树结构,索引越多,写入越慢。
  • 部分查询:当表数据量非常小,优化器算出走索引的代价(随机 I/O)比全表扫描还高时,就会放弃使用索引。比如一张只有几百行的配置表,扫描全表只要读几个页,走索引反而多出树查找和回表开销。
  • 无选择性的查询:比如 gender 字段只有“男/女”两个值,即使建了索引,查到一半数据以上,MySQL 也会选择全表扫描。因为从磁盘随机读半张表的代价比顺序扫描整张表还大。

所以别看到慢查询就无脑加索引。你得多想一步:这条查询是不是本身就该这么慢,有没有可能通过改写 SQL、调整业务逻辑来避免大范围扫描。

5. 设计索引时真正要避开的那些坑

5.1 一定有坑:主动写入 vs 被动查询的权衡

我实际看过的很多项目里,索引设计最大的问题不是不会建,而是乱建。比如有人为了保证所有查询都能覆盖,一个表建了七八个联合索引,结果每次插入都要维护七八棵树,写入事务耗时直线上升。更麻烦的是,一旦字段更新,所有包含该字段的索引都得更新一遍。

我记得之前优化过一张订单流水表,业务方建了六个索引,写性能压测时一直卡在 500 TPS。后来把索引收敛到三个,同时把高频查询改成覆盖索引,写入性能翻了一倍,查询也没有受损。这个案例说明,索引数量要控制在合理范围内,通常单表单索引数不建议超过五个,具体还是看写入频率和查询比例。

一个小技巧:同一个查询模式,多条件要尽可能合并成一个联合索引,而不是各建各的。比如查询经常同时出现 user_id、status、create_time,那就建一个 (user_id, status, create_time) 的联合索引,别拆成三个独立索引。拆开后不仅浪费空间,还会因为最左前缀原则导致其中两个索引实际用不上。

5.2 失效场景:函数、隐式转换、前缀模糊

索引失效是工作中遇到的最多的坑,总结起来有这么几类:

  • 对索引列使用函数,比如 WHERE DATE(create_time) = '2024-01-01'。MySQL 在大多数情况下,不会对函数处理后的结果做索引匹配。
  • 隐式类型转换,比如 varchar 类型的 account 字段,查询时写 WHERE account = 123,字符串和数字比较会发生类型转换,索引通常失效。
  • 前缀模糊匹配,比如 WHERE nickname LIKE '%张%'。因为 B+ 树是按键值有序排列的,前面带通配符时无法确定搜索起点,索引失效。而 LIKE '张%' 是可以走索引的。
  • OR 两边的条件不全是索引列。比如 WHERE id = 1 OR nickname = 'abc',如果 nickname 没有索引,MySQL 找不到合适的路径,可能整体不走索引。

这些内容在面试中属于“加分细节”。面试官问你“为什么索引快”,其实经常会接着问“那什么情况索引会失效”,因为一个人能说出失效场景,才说明他真的理解索引的机制。如果只背一句“索引是 B+ 树”,显然深度不够。

5.3 排序和分组也能靠索引,但姿势要对

ORDER BY 排序走索引,前提是排序字段和查询条件满足索引的最左前缀规则。假设你有联合索引 (user_id, create_time),执行:

SELECT * FROM `order` WHERE user_id = 10086 ORDER BY create_time;

这时索引已经帮我们按 create_time 排好了,不需要额外的文件排序。但如果把 ORDER BY 改成 ORDER BY amount,而 amount 不在索引里,那就出现 Using filesort,性能会差很多。

GROUP BY 和去重也是同理。如果分组字段正好是联合索引的前缀,可以用索引做有序分组,效率会明显高于临时表。这些细节在优化慢查询时非常有用。

5.4 一个小型索引优化复盘

拿我最近在处理的一个模拟项目举例。系统里有张商品浏览记录表,量级接近一亿。原本的索引设计是这样的:一个主键、一个商品 ID 索引、一个用户 ID 索引、一个浏览时间索引。看起来面面俱到,但业务方反馈页面加载非常慢。

我分析后发现,页面上的真实查询是:查某个用户最近浏览的若干商品,同时只展示商品 ID 和更新时间。原始查询用了两个单列索引,MySQL 最终只能选择一个,要么按用户 ID 先过滤再文件排序,要么按时间先排序再过滤。无论哪种,都要处理海量中间数据。

后来我把索引改成联合索引 (user_id, update_time, product_id),针对这个查询完全覆盖。执行计划从 type=all、rows=上千万,变成 type=ref、rows=几十。页面的响应时间从 2.8 秒降到了 30 毫秒左右。改动成本其实只是一条 ALTER TABLE 语句,关键是你要学会分析业务查询的最常用路径,然后为这个路径量身定做索引结构。

6. 面试官追问的常见问题怎么答

6.1 问题一:主键为什么用自增整数比用 UUID 好

这个问题前面已经铺垫过了,面试时可以分三点讲:

  • 存储空间:自增整数通常用 bigint,8 字节;UUID 是 36 字节字符串。聚簇索引的每一层非叶子节点,都会因为记录的索引项变大而减小分叉数,树变高。
  • 写入顺序:自增整数插入时按顺序追加到叶子节点末尾,页分裂少;UUID 随机插入,常导致节点分裂,产生碎片,写性能很差。
  • 二级索引占用:每个二级索引的叶子节点都要存一遍主键值,主键越大,所有二级索引占用的空间越大。

6.2 问题二:为什么一个表只能建一个聚簇索引,但能建很多二级索引

因为聚簇索引的叶子节点直接存的是整行数据。整行数据在物理上只能按照一种顺序排列,所以聚簇索引只能有一个。而二级索引的叶子节点只存索引列和主键值,可以按任意列组合建立出无数棵独立的 B+ 树。

6.3 问题三:一张表数据量很大,索引建完还是慢,怎么继续排查

这个问题最能体现经验。我的排查路径一般是这样的:

  • 先看 EXPLAIN,确认 type 和 rows 是不是合理。如果 type 是 all,先解决索引失效问题。
  • 再看 Extra 里有没有 Using filesort、Using temporary,有的话尝试调整索引让排序分组走索引。
  • 其次查回表次数。如果二级索引筛选出的记录非常多,回表代价就高,考虑改造成覆盖索引。
  • 最后看数据分布。如果筛选条件的区分度本身就低,索引再完美也可能慢,这时候要想业务层面怎么拆,比如分库分表、汇总表、缓存。

这几层排查写出来,本身就是一个完整的“面试官想听的思考路径”。

6.4 问题四:为什么小表有时候反而不走索引

这个前面提到过,原因是优化器会计算代价。小表全表扫描可能只需要读很少的页,顺序读的代价低;走索引反而要经历 B+ 树多层查找和随机 I/O。虽然大多数情况下索引更快,但 MySQL 的优化器是基于代价估算的,它判断全表扫描更超值,就会放弃索引。

7. 我的最后结论和一些实战建议

我个人在实际优化过程中的体会是:理解索引快的原理,最有效率的方式不是背资料,而是找一张真实生产环境的表,手动跑几个查询和 EXPLAIN 对照着看。你会看到同样的 WHERE 条件,系统加了一个索引后 rows 从几十万变成几十,执行时间从一秒钟变成十几毫秒,那种直观冲击比任何理论都来得深刻。

几个亲手踩过坑之后沉淀下来的建议分享给你:

  • 建索引之前,先把这条查询的 EXPLAIN 打出来,看它走的是什么路径,再决定索引怎么建。
  • 联合索引的列顺序,按照“等值条件优先、区分度高的靠前”的基本原则来安排。
  • 别给低频查询建太多索引,索引不是越多越好,它是要还债的,每一次插入更新都会还。
  • 对热点查询,优先考虑覆盖索引,这是性价比最高的优化方式。
  • 线上环境改索引,尽量在低峰期做,注意表锁和重建索引的成本,数据量大时要评估执行时间。

如果你在准备面试,建议把“一条 SQL 从执行计划到 B+ 树查找过程”整个完整地讲一遍,从 MySQL 收到 SQL,到优化器选索引,到走二级索引,到回表,到返回结果。把这套流程想通透,比背十个问题答案都管用。索引之所以快,总结到根源上,是它把“大海捞针”变成“按图索骥”,把一个柳暗花明的随机过程,变成了一条稳定、可控、可预期的查找路径。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询