mysql系列——索引底层存储结构(2)

本文详细解析了Mysql中使用的B+树索引结构及其与B树的区别,探讨了B+树的特点及优势,并介绍了Mysql中聚集索引与非聚集索引的工作原理,以及联合索引的最左前缀匹配原则。

上一篇讲的是Mysql存储结构

https://blog.youkuaiyun.com/sbitxmdf/article/details/113766970

 

这篇记录对索引存储结构的理解

目前知道Mysql索引用的数据结构是B+树,那么什么是B+树,和B树有什么区别?

 

 

 

可以看出B树与B+树的区别

1、B+树在叶子节点处都有箭头相连,但B树没有。

2、B-树的非叶子节点在叶子节点没有,但B+树不同,在叶子节点的数据,包含了非叶子节点的所有数据。

为什么有这样的区别?这和他们存储数据的不同有关系。

B树会在非叶子节点和叶子节点都记录值和引用的页指针;B+树只会在叶子节点处存储值和页指针,并且,数据是有序的,因为它是有序的,所以可以用箭头将他们连接起来,对于查询范围内的数据相当有效。

那么B+树肯定比B树更好了?所有的数据结构都有优缺点,直接复制

b树的优点

B树的每一个节点都包含key和value。

所以,经常访问的元素可能离根节点更近,所以访问也是更加的迅速

b+树的优点

1、b+树的中间节点不保存数据,可以容纳更多的节点元素

2、所有的叶子结点使用链表相连,有助于区间查找和遍历

B树的话,就需要进行每一层的递归遍历

相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好


B+树介绍完之后,看下索引存储结构

下面看一下都在用的图(盗图)

解释下这张图:

存储两类数据,一类是索引值,一类是页物理地址(这里直接写行数据是有问题的)

在索引时,应先找到对应值,然后根据页地址找到数据所在页,然后在页中进行搜索,找到数据所属的糟,再确定数据行的位置,这部分内容可参考上一篇,mysql存储结构

这张图表示的是聚集索引,非聚集索引有些区别,下面说下非聚集索引

先简单说下聚集和非聚集索引,也叫聚簇和非聚簇索引,聚集索引可以理解为主键索引,非聚集索引就是非主键索引,就是平时创建的普通索引,也是二级索引。

也是上面这张图,但存储内容会有变化,

存储两类数据,一类是索引值,一类是主键值,通过索引值找到对应的主键值,再走上面的聚集索引过程,这个过程也叫回表

 

下面引用下联合索引存储结构,转载

 联合索引

所谓联合索引,也称多列所谓,就是建立在多个字段上的索引,这个概念是跟单列索引相对的。联合索引依然是B+树,但联合索引的健值数量不是一个,而是多个。构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。

例如在a和b字段上建立联合索引,索引结构将如下图所示:
在这里插入图片描述
一目了然,当我们再执行SELECT score FROM student WHERE name='叶良辰';时,可以直接通过扫描非聚集索引直接获取score的值,而不再需要到聚集索引上二次扫描了。

最左前缀匹配

联合索引中有一个重要的课题,就是最左前缀匹配。

最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

这是为什么呢?我们再仔细观察索引结构,可以看到索引key在排序上,首先按a排序,a相等的节点中,再按b排序。因此,如果查询条件是a或a和b联查时,是可以应用到索引的。如果查询条件是单独使用b,因为无法确定a的值,因此无法使用索引。

假如在table表的a,b,c三个列上建立联合索引,简要分类分析下联合索引的最左前缀匹配。

首先看等值查询:

1、全值匹配查询时(where子句搜索条件顺序调换不影响索引使用,因为查询优化器会自动优化查询顺序 ),可以用到联合索引

SELECT * FROM table WHERE a=1 AND b=3 AND c=2
SELECT * FROM table WHERE b=3 AND c=4 AND a=2
  • 1
  • 2

2、匹配左边的列时,可以用到联合索引

SELECT * FROM table WHERE a=1
SELECT * FROM table WHERE a=1 AND b=3
  • 1
  • 2

3、未从最左列开始时,无法用到联合索引

SELECT * FROM table WHERE b=1 AND b=3
  • 1

4、查询列不连续时,无法使用联合索引(会用到a列索引,但c排序依赖于b,所以会先通过a列的索引筛选出a=1的记录,再在这些记录中遍历筛选c=3的值,是一种不完全使用索引的情况)

SELECT * FROM table WHERE a=1 AND c=3
  • 1

再看范围查询:

1、范围查询最左列,可以使用联合索引

SELECT * FROM table WHERE a>1 AND a<5;
  • 1

2、精确匹配最左列并范围匹配其右一列(a值确定时,b是有序的,因此可以使用联合索引)

SELECT * FROM table WHERE a=1 AND b>3;
  • 1

3、精确匹配最左列并范围匹配非右一列(a值确定时,c排序依赖b,因此无法使用联合索引,但会使用a列索引筛选出a>2的记录行,再在这些行中条件 c >3逐条过滤)

SELECT * FROM table WHERE a>2 AND c>5;

 

 

 

 

<think>嗯,用户这次想深入了解MySQL索引存储结构,看来是个认真钻研技术细节的人。从ta引用的资料来看,已经对B+树、聚集索引等概念有基础了解,现在需要更系统的解析。 用户可能正在做数据库性能优化,或者准备面试。ta引用的三篇资料质量不错,特别是第三篇提到磁盘页16KB设计对树高的影响,这个点很关键。我需要在回答中强化这个计算过程:1170个子节点 * 1170 * 1170 ≈ 1600万数据只需3层,这个数字要具体化。 注意到用户特别标注“后台”索引存储,说明需要区别于表层原理的底层机制。考虑从四个维度展开: 1)物理存储层面:InnoDB的段/区/页结构(连16KB页内行记录格式都可以提) 2数据结构层面:B+树节点如何映射到磁盘页 3)引擎差异:重点对比InnoDB和MyISAM索引存储的物理文件差异 4)动态维护:B+树分裂合并时页的分配机制 用户引用的资料2提到辅助索引回表机制,这个在回答里要图解化。可以用mermaid展示辅助索引→主键索引→数据行的三级跳过程,比文字描述直观。 需要警惕的是不能简单堆砌概念。比如解释“为什么非叶节点不存数据”时,要关联到磁盘预读原理:16KB页只存键值能容纳更多节点,把“减少IO次数”这个核心价值说透。 最后补充的注意事项里,关于页分裂的代价要具体。比如自增主键插入是顺序写,而UUID插入可能导致50%的页分裂率——这种量化表达比单纯说“性能更好”更有说服力。</think>### MySQL索引数据存储结构详解 #### 一、核心数据结构:B+树 MySQL索引采用**B+树结构**,这是专为磁盘存储设计的平衡多路搜索树: ```mermaid graph TD A[B+树节点] --> B[非叶节点] A --> C[叶节点] B --> D[存储索引键值+子节点指针] C --> E[存储数据] C --> F[双向链表连接] ``` 1. **节点设计** - **非叶节点**:仅存储索引键值+子节点指针(不存实际数据) - **叶节点**: - InnoDB:存储主键值+完整行数据(聚集索引) - MyISAM:存储行数据物理地址指针 - 所有叶节点通过双向链表连接 2. **磁盘优化设计** - 每个节点大小固定为**16KB**(`Innodb_page_size`) - 计算示例(主键为BIGINT): $$ \text{单节点子节点数} = \frac{16\text{KB}}{8\text{B(主键)} + 6\text{B(指针)}} \approx 1170 $$ $$ \text{三层树存储量} = 1170 \times 1170 \times 1170 \approx 1600\text{万行} $$ - 百万级数据仅需**3次磁盘IO**即可定位[^3] > 💡 **B+树 vs B树** > - B+树数据全在叶节点 → 查询路径长度稳定 > - B树非叶节点存数据 → 树高更高,随机IO更多 --- #### 二、存储引擎实现差异 ##### 1. InnoDB(聚集索引) ```mermaid graph LR A[主键索引] --> B[叶节点存储完整行数据] C[辅助索引] --> D[叶节点存储主键值] D -->|回表查询| A ``` - **数据文件**:`.ibd`文件(索引与数据一体) - **主键索引**:叶节点直接存储行数据(物理有序) - **辅助索引**:叶节点存储主键值,需**二次查找** - **优势**:主键查询极快(1次检索获数据) ##### 2. MyISAM(非聚集索引) ```mermaid graph LR A[索引] --> B[叶节点存储行地址] B --> C[.MYD数据文件] ``` - **文件分离**: - `.MYI`:索引文件(B+树结构) - `.MYD`:数据文件(堆表) - **所有索引**:叶节点存储数据行物理地址 - **特点**:主键/非主键索引结构相同 --- #### 三、关键存储细节 1. **页内结构(InnoDB)** ```plaintext | 页头 (38B) | 虚记录 | 记录堆 | 自由空间链表 | | 未分配空间 | 槽区 | 页尾 (8B) | ``` - **行记录存储**:按主键顺序物理存储(插入时可能页分裂) - **槽(Slot)机制**:页内记录通过二分查找定位 2. **辅助索引存储** - 叶节点存储:`索引列值 + 主键值` - 优势: - 数据更新时辅助索引无需修改 - 支持**覆盖索引**(避免回表) 3. **联合索引存储** - 索引键按声明顺序拼接 `(col1, col2) → 存储值 = col1值 + col2值` - **最左匹配原则**: `WHERE col1=1 AND col2=2` ✅ `WHERE col2=2` ❌(无法使用索引) --- #### 四、性能关键指标 | **场景** | **存储影响** | **优化建议** | |-----------------|-------------------------------|---------------------------| | **范围查询** | 叶节点双向链表 → $O(1)$跳转 | 优先使用自增整型主键 | | **插入性能** | 随机主键导致页分裂($O(\log n)$) | 避免UUID等随机主键 | | **空间占用** | 索引≈表数据20%-30%[^1] | 定期清理冗余索引 | | **更新代价** | 需更新所有相关索引 | 减少非必要索引 | > ⚠️ **页分裂代价** > 当叶节点满时(填充因子=15/16),触发页分裂: > - 新分配磁盘页 > - 迁移50%数据 > - 更新父节点指针 > 随机插入场景下可能产生**存储碎片** --- ```mermaid graph TD A[SQL查询] --> B{使用索引?} B -->|是| C[索引类型判断] C -->|主键索引| D[直接获取行数据] C -->|辅助索引| E[获取主键→回表查询] B -->|否| F[全表扫描] ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值