中间件专栏之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,但不立即同步磁盘 适用于高并发,部分数据丢失可接受的情况。

相关推荐
风景_fengjing5 分钟前
ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
服务器·数据库·oracle
HGW68928 分钟前
基于MyBatis插件实现动态表名解决多环境单一数据库问题
数据库·mybatis
SEO-狼术40 分钟前
ComponentOne Studio Enterprise 2025
数据库
睡觉待开机42 分钟前
2. 库的操作
数据库·oracle·dba
才华是浅浅的耐心1 小时前
Facebook用户信息爬虫技术分析与实现详解
数据库·爬虫·python·facebook
Python之栈2 小时前
Python 隐藏法宝:双下划线 _ _Dunder_ _
数据库·windows·microsoft
码农sssss6 小时前
1130 - Host ‘xxx.x.xx.xxx‘is not allowed to connect to this MySQL server
mysql
FungLeo8 小时前
安装和配置 Nginx 和 Mysql —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录6
服务器·mysql·nginx
heart000_111 小时前
MySQL事务与锁机制详解:确保数据一致性的关键【MySQL系列】
数据库·mysql
一眼青苔11 小时前
MySQL 如何判断某个表中是否存在某个字段
数据库·mysql