中间件专栏之MySQL篇——MySQL的索引原理

MySQL的索引是MySQL这个体系中很重要的一环,并且它的概念很多人理解的都有偏差,因此本文对其做一个梳理。

一、索引是什么

首先,索引是数据库表中的一种数据结构 ,用于加速查询。索引类似于书籍的目录,可以快速找到所需的数据,提高查询效率。表面上索引就是用户自己选择和设置的一个或多个字段,但实际在创建索引时,MySQL会为我们创建一棵b+树(默认情况,也有其他存储结构),用于存储我们创建的索引。

二、索引的作用

加快查询速度 :减少数据扫描,提高 SELECT 语句性能。

提高排序效率 :支持 ORDER BY 更快排序。

优化 WHERE 语句 :快速找到匹配的数据行。

缺点

  • 占用存储空间
  • 插入、更新、删除操作变慢(需要维护索引)。

通俗来讲,如果我们只创建了数据表但没有创建索引,能查询到我们指定的语句吗?可以,但是通过全表扫描得到的结果,如果数据多则非常耗时,而如果我们创建了索引,则直接去该索引的b+树查询,查询效率得到提高。

三、索引的分类

1. 按功能分类

索引类型 作用 SQL 语句
普通索引 (INDEX) 加速查询 CREATE INDEX idx_name ON users(name);
唯一索引 (UNIQUE INDEX) 确保列值唯一 CREATE UNIQUE INDEX idx_email ON users(email);
主键索引 (PRIMARY KEY) 作为表的唯一标识 ALTER TABLE users ADD PRIMARY KEY (id);
全文索引 (FULLTEXT INDEX) 支持全文搜索 CREATE FULLTEXT INDEX idx_content ON articles(content);

2. 按存储结构分类

索引类型 存储结构 特点
B-Tree 索引 平衡树结构 默认索引类型,适用于大多数查询
Hash 索引 哈希映射 适用于等值查询(如 =),不支持范围查询
Full-Text 索引 倒排索引 用于文本搜索
R-Tree 索引 空间索引 适用于 GIS(地理信息系统)数据

3. 按字段数分类

索引类型 特点 SQL 语句
单列索引 针对单个字段 CREATE INDEX idx_name ON users(name);
多列索引 (组合索引) 针对多个字段,提高复合查询效率 CREATE INDEX idx_name_age ON users(name, age);

4. 按索引创建方式分类

索引类型 作用 SQL 语句
自动索引 主键会自动创建索引 PRIMARY KEY(id)
手动索引 手动添加索引 CREATE INDEX idx_name ON users(name);
隐式索引 唯一键会自动创建索引 UNIQUE(email)

还有很重要的索引是聚簇索引和辅助索引 ,这两种索引属于 存储结构分类,具体来说:

索引类型 分类 特点
聚簇索引 (Clustered Index) 存储结构索引 - 数据和索引存储在同一个结构中,按照索引顺序存储数据。 - 每张表只能有一个聚簇索引,通常是主键索引 。 - Innodb 引擎默认使用聚簇索引。
辅助索引 (Secondary Index) 存储结构索引 - 也称 非聚簇索引 ,索引存储的是指向主键的指针 。 - 可以有多个辅助索引。 - 需要通过主键索引回表查询获取完整数据(回表查询会影响性能)。

四、索引的存储结构

如前面提到的,每一个索引对应一棵b+树。

为什么使用b+树数据结构?b+树是多叉树,可以降低层高,减少磁盘io的访问数(redis是红黑树,是二叉树),并且b+树的叶子节点依次相连,方便范围查询。

b+树的特征:1、非叶子节点只存储索引信息;2、叶子节点还存储数据信息;3、叶子节点之间依次相连;4、节点大小为16kb,映射的是连续的磁盘页。

五、索引优化与索引失效

1.1 使用合适的索引

索引类型 适用场景 示例 SQL
主键索引 (PRIMARY KEY) 唯一标识记录,查询高效 ALTER TABLE users ADD PRIMARY KEY (id);
唯一索引 (UNIQUE INDEX) 需要保证字段唯一性,如邮箱 CREATE UNIQUE INDEX idx_email ON users(email);
普通索引 (INDEX) 提高查询速度 CREATE INDEX idx_name ON users(name);
组合索引 (Composite Index) 适用于多字段查询 CREATE INDEX idx_name_age ON users(name, age);
全文索引 (FULLTEXT INDEX) 适用于文本搜索 CREATE FULLTEXT INDEX idx_content ON articles(content);

优化建议

  • 优先使用主键索引和唯一索引,它们查询效率最高。
  • WHERE 条件涉及多个字段时,考虑组合索引,避免多个单列索引带来的回表查询。
  • LIKE 查询尽量使用全文索引,提高匹配速度。

1.2 覆盖索引(Covering Index)

定义 :如果索引已经包含查询所需的所有字段,就可以直接从索引中返回数据,而不需要回表查询,提高性能。

优化示例

CREATE INDEX idx_name_email ON users(name, email);
SELECT name, email FROM users WHERE name = '张三';
  • idx_name_email 包含 nameemail,查询时直接从索引中获取数据,无需回表,查询更快

1.3 最左前缀匹配

规则组合索引 查询时,必须按照索引字段的从左到右顺序使用,否则索引可能失效。

优化示例

CREATE INDEX idx_name_age ON users(name, age);
SELECT * FROM users WHERE name = '张三';  -- ✅ 能用索引
SELECT * FROM users WHERE name = '张三' AND age = 30;  -- ✅ 能用索引
SELECT * FROM users WHERE age = 30;  -- ❌ 索引失效(未使用最左前缀)

*1.4 避免 SELECT ,只查询需要的列

优化示例

SELECT name, email FROM users WHERE name = '张三';

不推荐

SELECT * FROM users WHERE name = '张三';  -- 可能导致不必要的回表查询

2. 索引失效的常见原因

可参考下图

六、bufferpool 的设计、空闲页与脏页的管

理策略、刷盘时机

MySQL 中 Buffer Pool 的设计及管理策略

在 MySQL InnoDB 存储引擎中,Buffer Pool(缓冲池)是用于缓存数据页、索引页的核心组件,能大幅提高数据库的性能。下文将详细介绍 Buffer Pool 的设计、空闲页与脏页的管理策略、以及刷盘时机


1. Buffer Pool 的设计

Buffer Pool 是 InnoDB 的内存缓存区,主要作用是减少磁盘 I/O 操作,加速查询性能。其结构如下:

  • 数据页缓存:缓存表的数据页,减少磁盘读取。
  • 索引页缓存:缓存 B+ 树索引,提高查询速度。
  • Undo 页缓存:用于事务回滚和 MVCC 版本管理。
  • Adaptive Hash Index(AHI):自适应哈希索引,提高查询效率。
  • Flush List:存储脏页信息,决定刷盘策略。

Buffer Pool 结构

Buffer Pool 由多个 Page(页) 组成,每个页的大小通常是 16KB(可配置为 8KB 或 4KB)。当查询数据时,InnoDB 先从 Buffer Pool 查找,若未命中,则从磁盘读取,并可能淘汰旧数据。


2. 空闲页与脏页的管理策略

2.1 页面分类

页面类型 描述
Free Pages(空闲页) 还未使用或被释放的页,可直接用于存放新数据。
Clean Pages(干净页) 仅用于缓存磁盘数据,与磁盘数据一致。
Dirty Pages(脏页) 被修改但尚未写回磁盘的数据页。

2.2 页面管理策略

InnoDB 采用 LRU(最近最少使用)+ young/old 子列表 机制:

  1. 新加载的页 先放入 LRU 列表的 middle 部分(old 列表),而非头部。
  2. 访问次数多的页 被移动到 young 列表,减少被淘汰的概率。
  3. 当 Buffer Pool 需要空间时,优先淘汰 old 列表的页(避免新加载的数据瞬间淘汰旧数据)。

此外,LRU 机制还会结合 Adaptive Flushing 进行优化,防止 Buffer Pool 被高频访问的数据填满。


3. 刷盘时机

脏页必须写回磁盘(即刷盘)以保证数据一致性,InnoDB 采用以下几种刷盘策略:

3.1 被动刷盘(Lazy Write)

  • 当 Buffer Pool 空间不足,需要淘汰旧数据时,会将脏页写入磁盘。
  • 触发条件:
    • innodb_max_dirty_pages_pct 限制脏页比例。
    • innodb_lru_scan_depth 影响 LRU 淘汰深度。

3.2 主动刷盘

刷盘时机 触发原因 说明
Checkpoint(检查点刷盘) 防止重启时数据丢失 InnoDB 定期执行,保证 redo log 覆盖范围内的脏页已落盘。
Redo Log 写满 Redo Log Buffer 满了 刷新部分脏页,以保证日志可复用。
事务提交刷盘(flush at commit) innodb_flush_log_at_trx_commit=1 事务提交后,立即刷盘,提高数据安全性(但影响性能)。
后台线程刷盘 Master Thread 定期执行 后台线程周期性刷盘,保证脏页不会过多积累。

3.3 刷盘方式

MySQL 提供以下几种刷盘策略(通过 innodb_flush_log_at_trx_commit 参数控制):

参数值 刷盘策略 适用场景
0 事务提交时不刷盘,靠后台刷盘 适用于高性能但数据可能丢失的场景。
1(默认) 事务提交时写入 redo log 并同步磁盘 最安全,保证数据一致性。
2 事务提交时写入 redo log,但不立即同步磁盘 适用于高并发,部分数据丢失可接受的情况。

相关推荐
Watink Cpper34 分钟前
[MySQL初阶]MySQL(1)MySQL的理解、库的操作、表的操作
linux·运维·服务器·数据库·c++·后端·mysql
尘世壹俗人40 分钟前
spark写数据库用连接池找不到driver类
大数据·数据库·spark
计算机学长大白1 小时前
Redis是什么?如何使用Redis进行缓存操作?
数据库·redis·缓存
焱焱枫2 小时前
PostgreSQL全页写机制深度解析:如何平衡WAL性能与数据可靠性
数据库·postgresql·wal性能
nicepainkiller3 小时前
go 分布式redis锁的实现方式
数据库·redis·redis分布式锁
007php0073 小时前
企微审批中MySQL字段TEXT类型被截断的排查与修复实践
大数据·开发语言·数据库·后端·mysql·重构·golang
苹果酱05673 小时前
利用机器学习进行信用风险评估
java·vue.js·spring boot·mysql·课程设计
Elastic 中国社区官方博客3 小时前
使用 Elasticsearch 进行集成测试初始化数据时的注意事项
大数据·数据库·elasticsearch·搜索引擎·集成测试·可用性测试
J不A秃V头A3 小时前
表关联查询:utf8mb4_general_ci和utf8mb4_0900_ai_ci两种不同的校对规则在操作符‘=‘时混用了
sql·mysql
V+zmm101343 小时前
美食推荐系统的微信小程序+论文源码调试讲解
java·数据库·微信小程序·小程序·毕业设计