【计算基础|MySQL08】索引(上):为什么是 B+ 树
你上一篇已经知道数据按页存在 .ibd 文件里。那"查一条数据怎么变快"?答案是索引------给数据建个目录。但为什么这个目录偏偏是 B+ 树,而不是你学过的红黑树、哈希表?聚簇索引、二级索引、回表、覆盖索引到底是什么关系?
这篇用 50 万行真实数据说话。先把几种数据结构挨个比一遍,再讲 InnoDB 为什么选 B+ 树,最后实测有索引和没索引的耗时。B+ 树结构、回表过程全用 Mermaid 画。
环境:Docker MySQL 8.0.46,表
blog08.sbtest共 50 万行(含 id 主键 + k 字段普通索引),实测耗时见正文。
一、索引是什么:数据的目录
结论先给:索引就是一份排好序的数据目录,让你不用翻完整本书就能找到那一页 。没有索引,查 WHERE k=9999 就得把 50 万行从头扫到尾(全表扫描 O(n));有了索引,顺着目录找,O(log n) 就到。
⚠️ 但索引不是越多越好:索引本身也占存储空间,而且每次 INSERT/DELETE 都要同步维护所有索引,拖慢写入。一张表别超过五六个索引。
二、为什么是 B+ 树:六种数据结构逐个淘汰
要给磁盘上的数据建索引,这个数据结构必须满足:树要矮(每层一次磁盘 IO,树越高 IO 次数越多)、支持范围查询、查找快。挨个看:
2.1 哈希表
哈希表 O(1) 查找,确实快。但:
- 只支持等值查询(
=),不支持范围查询 (WHERE k>100直接抓瞎); - 有哈希冲突。
Memory 引擎支持 HASH 索引,但 InnoDB 默认不用它------业务里范围查询太多了。
2.2 二叉搜索树 / 平衡二叉树(AVL)
二叉树每个节点只能有俩孩子,数据一多层就很高。百万行数据,树高约 20 层------每层一次磁盘 IO,查一条要 20 次 IO,慢到没法用。AVL 虽然严格平衡,但旋转开销大、树还是高。红黑树同理,路数少,树照样高。
2.3 B 树(B-Tree)
B 树改成多路 ------一个节点存多个 key、开很多叉,树立刻变矮。但 B 树有个硬伤:非叶子节点也存数据。每个节点(一页 16KB)既要存 key 又要存整行数据,能存的 key 数量就少,扇出小,树又变高了。
2.4 B+ 树(InnoDB 的选择)
B+ 树是 B 树的变种,两个关键改进:
- 非叶子节点只存 key,不存数据 → 一页能塞下更多 key,扇出极大,树更矮更胖;
- 所有真实数据都存在叶子节点 ,且叶子节点之间用双向链表串起来 → 范围查询顺着链表扫就行。
#mermaid-svg-9uL8OHCMNZwA3oEA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9uL8OHCMNZwA3oEA .error-icon{fill:#552222;}#mermaid-svg-9uL8OHCMNZwA3oEA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9uL8OHCMNZwA3oEA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9uL8OHCMNZwA3oEA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9uL8OHCMNZwA3oEA .marker.cross{stroke:#333333;}#mermaid-svg-9uL8OHCMNZwA3oEA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9uL8OHCMNZwA3oEA p{margin:0;}#mermaid-svg-9uL8OHCMNZwA3oEA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA .cluster-label text{fill:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA .cluster-label span{color:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA .cluster-label span p{background-color:transparent;}#mermaid-svg-9uL8OHCMNZwA3oEA .label text,#mermaid-svg-9uL8OHCMNZwA3oEA span{fill:#333;color:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA .node rect,#mermaid-svg-9uL8OHCMNZwA3oEA .node circle,#mermaid-svg-9uL8OHCMNZwA3oEA .node ellipse,#mermaid-svg-9uL8OHCMNZwA3oEA .node polygon,#mermaid-svg-9uL8OHCMNZwA3oEA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9uL8OHCMNZwA3oEA .rough-node .label text,#mermaid-svg-9uL8OHCMNZwA3oEA .node .label text,#mermaid-svg-9uL8OHCMNZwA3oEA .image-shape .label,#mermaid-svg-9uL8OHCMNZwA3oEA .icon-shape .label{text-anchor:middle;}#mermaid-svg-9uL8OHCMNZwA3oEA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9uL8OHCMNZwA3oEA .rough-node .label,#mermaid-svg-9uL8OHCMNZwA3oEA .node .label,#mermaid-svg-9uL8OHCMNZwA3oEA .image-shape .label,#mermaid-svg-9uL8OHCMNZwA3oEA .icon-shape .label{text-align:center;}#mermaid-svg-9uL8OHCMNZwA3oEA .node.clickable{cursor:pointer;}#mermaid-svg-9uL8OHCMNZwA3oEA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9uL8OHCMNZwA3oEA .arrowheadPath{fill:#333333;}#mermaid-svg-9uL8OHCMNZwA3oEA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9uL8OHCMNZwA3oEA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9uL8OHCMNZwA3oEA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9uL8OHCMNZwA3oEA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9uL8OHCMNZwA3oEA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9uL8OHCMNZwA3oEA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9uL8OHCMNZwA3oEA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9uL8OHCMNZwA3oEA .cluster text{fill:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA .cluster span{color:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9uL8OHCMNZwA3oEA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9uL8OHCMNZwA3oEA rect.text{fill:none;stroke-width:0;}#mermaid-svg-9uL8OHCMNZwA3oEA .icon-shape,#mermaid-svg-9uL8OHCMNZwA3oEA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9uL8OHCMNZwA3oEA .icon-shape p,#mermaid-svg-9uL8OHCMNZwA3oEA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9uL8OHCMNZwA3oEA .icon-shape .label rect,#mermaid-svg-9uL8OHCMNZwA3oEA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9uL8OHCMNZwA3oEA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9uL8OHCMNZwA3oEA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9uL8OHCMNZwA3oEA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} B+树
双向链表
根节点(非叶子,只存key)
10 | 20 | 30
内部节点
5 | 10
内部节点
15 | 20 | 25
内部节点
30 | 40
叶子(存整行)
1,5,9
叶子
10,15,19
叶子
20,25,29
叶子
30,35,40
🔴 为什么 InnoDB 选 B+ 树,四条理由:
| 优点 | 说明 |
|---|---|
| ① 树矮 | 非叶子不存数据,扇出大,3~4 层就能存千万级数据,查一次 3~4 次 IO |
| ② 范围查询快 | 叶子节点双向链表,WHERE k>10 找到起点后顺着链表扫 |
| ③ 查询稳定 | 任何数据都在叶子节点,每次查找路径长度相同 |
| ④ 磁盘预读友好 | 按 16KB 页读,局部性原理,一次读一整页 |
面试怎么考:为什么用 B+ 树不用红黑树/B 树/哈希?答:树矮(IO 少)、叶子链表支持范围查询、非叶子不存数据扇出大。
三、聚簇索引 vs 二级索引
3.1 聚簇索引(主键索引)
InnoDB 的主键索引就是聚簇索引 ------它的叶子节点直接存完整的一行数据。一个表只有一个聚簇索引。
渲染错误: Mermaid 渲染失败: Parse error on line 2: ...TD subgraph 聚簇索引(主键) P1["根: ----------------------^ Expecting 'SEMI', 'NEWLINE', 'SPACE', 'EOF', 'GRAPH', 'DIR', 'subgraph', 'SQS', 'end', 'AMP', 'COLON', 'START_LINK', 'STYLE', 'LINKSTYLE', 'CLASSDEF', 'CLASS', 'CLICK', 'DOWN', 'UP', 'NUM', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'PS'
如果你没定义主键,InnoDB 会:① 选第一个唯一非空索引;② 再没有就偷偷生成一个 6 字节的 ROW_ID。所以一定要显式定义自增主键。
3.2 二级索引(非聚簇索引)
你建的普通索引(CREATE INDEX idx_k ON sbtest(k))是二级索引。它的叶子节点不存整行,只存主键值。
#mermaid-svg-MkEGk5XiOfeyqPBj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MkEGk5XiOfeyqPBj .error-icon{fill:#552222;}#mermaid-svg-MkEGk5XiOfeyqPBj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MkEGk5XiOfeyqPBj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MkEGk5XiOfeyqPBj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MkEGk5XiOfeyqPBj .marker.cross{stroke:#333333;}#mermaid-svg-MkEGk5XiOfeyqPBj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MkEGk5XiOfeyqPBj p{margin:0;}#mermaid-svg-MkEGk5XiOfeyqPBj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj .cluster-label text{fill:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj .cluster-label span{color:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj .cluster-label span p{background-color:transparent;}#mermaid-svg-MkEGk5XiOfeyqPBj .label text,#mermaid-svg-MkEGk5XiOfeyqPBj span{fill:#333;color:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj .node rect,#mermaid-svg-MkEGk5XiOfeyqPBj .node circle,#mermaid-svg-MkEGk5XiOfeyqPBj .node ellipse,#mermaid-svg-MkEGk5XiOfeyqPBj .node polygon,#mermaid-svg-MkEGk5XiOfeyqPBj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MkEGk5XiOfeyqPBj .rough-node .label text,#mermaid-svg-MkEGk5XiOfeyqPBj .node .label text,#mermaid-svg-MkEGk5XiOfeyqPBj .image-shape .label,#mermaid-svg-MkEGk5XiOfeyqPBj .icon-shape .label{text-anchor:middle;}#mermaid-svg-MkEGk5XiOfeyqPBj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MkEGk5XiOfeyqPBj .rough-node .label,#mermaid-svg-MkEGk5XiOfeyqPBj .node .label,#mermaid-svg-MkEGk5XiOfeyqPBj .image-shape .label,#mermaid-svg-MkEGk5XiOfeyqPBj .icon-shape .label{text-align:center;}#mermaid-svg-MkEGk5XiOfeyqPBj .node.clickable{cursor:pointer;}#mermaid-svg-MkEGk5XiOfeyqPBj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MkEGk5XiOfeyqPBj .arrowheadPath{fill:#333333;}#mermaid-svg-MkEGk5XiOfeyqPBj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MkEGk5XiOfeyqPBj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MkEGk5XiOfeyqPBj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MkEGk5XiOfeyqPBj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MkEGk5XiOfeyqPBj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MkEGk5XiOfeyqPBj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MkEGk5XiOfeyqPBj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MkEGk5XiOfeyqPBj .cluster text{fill:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj .cluster span{color:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MkEGk5XiOfeyqPBj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MkEGk5XiOfeyqPBj rect.text{fill:none;stroke-width:0;}#mermaid-svg-MkEGk5XiOfeyqPBj .icon-shape,#mermaid-svg-MkEGk5XiOfeyqPBj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MkEGk5XiOfeyqPBj .icon-shape p,#mermaid-svg-MkEGk5XiOfeyqPBj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MkEGk5XiOfeyqPBj .icon-shape .label rect,#mermaid-svg-MkEGk5XiOfeyqPBj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MkEGk5XiOfeyqPBj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MkEGk5XiOfeyqPBj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MkEGk5XiOfeyqPBj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 拿到主键id
二级索引 idx_k
叶子存: k值 + 主键id
回表: 再查聚簇索引
聚簇索引叶子: 完整行
查 WHERE k=9999 的过程:
- 先在二级索引 idx_k 找到 k=9999,拿到对应的主键 id(50 个 id);
- 再拿着这些 id 去聚簇索引 查完整行------这一步叫回表。
3.3 覆盖索引:不用回表
如果查询要的列刚好都在索引里 ,就不用回表了------这叫覆盖索引。比如 SELECT id FROM sbtest WHERE k=9999,idx_k 叶子节点已经有 k 和 id,直接返回,不用再查聚簇索引。EXPLAIN 里 Extra 显示 Using index 就是覆盖索引生效。
四、实测:50 万行数据,索引到底快多少
我们造了一张 50 万行的表 sbtest(id 主键, k, c, pad),先看没索引时 EXPLAIN:
sql
EXPLAIN SELECT * FROM sbtest WHERE k=9999;
type possible_keys key rows Extra
ALL NULL NULL 497352 Using where
type=ALL、rows=497352------全表扫描 50 万行。建索引:
sql
CREATE INDEX idx_k ON sbtest(k);
再看:
type possible_keys key rows Extra
ref idx_k idx_k 50 NULL
type=ref、rows=50------只扫 50 行。真实计时(Python 测):
主键 id=250000 (聚簇索引): 0.3 ms, rows=1
二级索引 k=9999 (+回表): 0.5 ms, rows=50
覆盖索引 SELECT id (不回表): 0.2 ms, rows=50
全表扫描 k 条件 (约50万行): 32.1 ms, rows=1
🔴 数字说话 :全表扫描 32ms,走索引 0.2~0.5ms------差了 60~160 倍。而且数据量越大差距越夸张,百万、千万行全表扫描能到秒级。
看索引详情:
sql
SHOW INDEX FROM sbtest\G
Table: sbtest Key_name: PRIMARY Seq_in_index: 1 Column_name: id Cardinality: 493200 Index_type: BTREE
Table: sbtest Key_name: idx_k Seq_in_index: 1 Column_name: k Cardinality: 9984 Index_type: BTREE
重点看 Cardinality(基数):索引列里不重复值的个数。idx_k 基数 9984(k 只有 1 万种值),区分度一般。区分度越低的索引越没用------这是下一篇联合索引和最左前缀要讲的。
五、索引怎么建、有哪几类
sql
CREATE INDEX idx_name ON sbtest(k); -- 普通索引
CREATE UNIQUE INDEX uk_email ON user(email); -- 唯一索引
ALTER TABLE sbtest ADD INDEX idx_k(k); -- 另一种建法
DROP INDEX idx_k ON sbtest; -- 删索引
SHOW INDEX FROM sbtest; -- 看索引详情
| 索引类型 | 关键字 | 特点 |
|---|---|---|
| 主键索引 | PRIMARY | 聚簇索引,一张表一个,不允许 NULL |
| 唯一索引 | UNIQUE | 值不重复(但允许多个 NULL) |
| 普通索引 | INDEX/KEY | 加速查询 |
| 联合索引 | 多个列 | 按列顺序排序,遵守最左前缀(下篇讲) |
| 全文索引 | FULLTEXT | 全文检索,MySQL 8 InnoDB 支持 |
六、面试怎么考
Q1:为什么 InnoDB 用 B+ 树?
非叶子节点不存数据,扇出大、树矮(3~4层存千万级,IO 少);叶子节点双向链表支持范围查询;查询路径稳定;友好磁盘预读。
Q2:聚簇索引和二级索引区别?
聚簇索引叶子存整行数据,一张表一个(主键);二级索引叶子存主键值,查其他列要回表。
Q3:什么叫回表?
二级索引找到主键后,再去聚簇索引查完整行的过程。覆盖索引能避免回表。
Q4:什么是覆盖索引?
查询的列都在索引里,不用回表,EXPLAIN 显示 Using index,是性能优化利器。
Q5:为什么主键推荐自增?
自增主键顺序插入,B+ 树末尾追加,不页分裂;UUID 乱序插入导致频繁页分裂和碎片。
Q6:B+ 树一般多高存千万级数据?
3~4 层(非叶子节点一页能存上千个 key,扇出大)。
总结:你真正需要记住的 7 件事
- 索引是数据目录,加速查询但占空间、拖慢写入,别贪多。
- InnoDB 选 B+ 树:树矮 IO 少、叶子链表支持范围查询、非叶子不存数据扇出大。
- 聚簇索引(主键)叶子存整行,一张表一个;二级索引叶子只存主键。
- 二级索引查非索引列要回表;覆盖索引(Using index)免回表,是优化利器。
- 实测:50 万行全表扫描 32ms,走索引 0.2~0.5ms,差两个数量级。
- 索引区分度(Cardinality)越低越没用,建索引前先看列的重复率。
- 主键用自增,顺序写入不页分裂;别用 UUID。
验证清单
- 能解释为什么不用哈希、红黑树、B 树,而选 B+ 树
- 默画一棵 B+ 树,标出非叶子只存 key、叶子存数据且双向链表
- 说清聚簇索引和二级索引叶子节点各存什么
- 用自己的话讲一遍"回表"过程,并说出怎么避免(覆盖索引)
- 在本机 sbtest 上复现:全表扫描 ~30ms,走 idx_k ~0.5ms
- 跑 EXPLAIN,认出
type=ALL(全表扫描)和type=ref(走索引) - 跑
SHOW INDEX,看懂 Cardinality 的含义 - 说出自增主键为什么能避免页分裂
参考资源
- InnoDB 索引结构 https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html
- 聚簇索引 https://dev.mysql.com/doc/refman/8.0/en/innodb-clustered-indexes.html
- 二级索引与回表 https://dev.mysql.com/doc/refman/8.0/en/innodb-secondary-indexes.html
- EXPLAIN https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
- 覆盖索引 https://dev.mysql.com/doc/refman/8.0/en/innodb-covered-indexes.html
下一篇《索引(下):联合索引、最左前缀与索引失效》:上一篇知道了 B+ 树怎么加速,这篇解决实战中最头疼的问题------为什么我明明建了索引却没生效?联合索引 (a,b,c) 为什么 WHERE c=? 用不上?什么是最左前缀、索引下推、索引失效的常见场景?我们把这些"建了索引却不走"的坑一个个踩给你看。