MySQL 索引深度指南:原理 · 实践 · 运维(适配 MySQL 8.4 LTS)

一、索引的作用与核心原理

1.1 作用:从 O(n) 到 O(log n)

  • 无索引:全表扫描,I/O 与数据量线性增长(100 万行 ≈ 100 万次磁盘读);
  • 有索引:B+ 树定位,I/O 与树高成正比(100 万行 ≈ 3--4 次 I/O)。

关键 :索引的本质是用存储空间和写入延迟,换取查询速度

1.2 核心结构:B+ 树(InnoDB 实现)

  • 平衡性 :所有叶子节点深度一致,查询稳定在 O(logₙ N)
  • 磁盘友好 :InnoDB 页大小默认 16KB,一个节点可存数百个键值对,一次 I/O 读取大量有序数据
  • 范围高效 :叶子节点双向链表连接,ORDER BY / BETWEEN 无需额外排序
  • 聚簇索引主键索引 = 数据本身 ,主键查询只需 1 次 I/O;
    二级索引 = (索引列, 主键),查询需"回表"(二次 I/O)。

MyISAM 对比 :非聚簇索引,所有索引(含主键)都存"数据指针",主键查询也需 2 次 I/O


二、索引类型详解(MySQL 8.4 视角)

类型 支持引擎 结构 适用场景 限制
B+ 树索引 InnoDB(默认) MyISAM 平衡多路搜索树 =, >, <, BETWEEN, ORDER BY, GROUP BY, LIKE 'prefix%' LIKE '%suffix' 无法使用
哈希索引 Memory(显式) InnoDB(自适应,不可控 哈希表 极致等值查询O(1) 不支持范围、排序、LIKE;冲突退化为链表
全文索引 InnoDB(≥5.6) MyISAM 倒排索引 MATCH(...) AGAINST(...) 文本搜索 CHAR/VARCHAR/TEXT;有最小词长(默认 3)
函数索引(MySQL 8.0+) InnoDB B+ 树(基于表达式) WHERE YEAR(create_time) = 2023 需显式创建:CREATE INDEX idx_year ON t ((YEAR(create_time)))
降序索引(MySQL 8.0+) InnoDB B+ 树(支持 DESC 存储) ORDER BY a ASC, b DESC 8.0 前 DESC 是逻辑排序,8.0+ 可物理存储
隐藏索引(MySQL 8.4) InnoDB 正常索引,但对优化器不可见 安全验证索引影响 ALTER INDEX ... INVISIBLE

关键(MySQL 8.4 LTS,2024年4月30日发布):

  • 函数索引、降序索引、隐藏索引均已成熟,成为复杂查询调优利器;
  • MyISAM 已被官方标记为 deprecated,新项目应彻底规避。

三、索引的优缺点(量化视角)

优点

  • 查询加速:WHERE 条件命中索引,I/O 降低 1--3 个数量级;
  • 唯一约束PRIMARY KEY / UNIQUE INDEX 防止重复数据;
  • 覆盖索引SELECT a, b FROM t WHERE c = ?,若 (c, a, b) 有索引,则无需回表,性能提升 2--5 倍。

缺点

  • 写入延迟 :每新增一个索引,INSERT/UPDATE/DELETE 延迟增加 5%--20%(实测);
  • 存储膨胀:大表索引总大小可达数据的 1.5 倍(尤其宽表 + 多索引);
  • 优化器干扰:超过 10 个索引的表,优化器可能因统计信息不准选错执行计划。

四、SRE 实战指南:创建与运维

4.1 何时创建索引?

  • 高频 WHERE 条件 :选择性较高(通常 > 1% ),如 user_idorder_no 等唯一或近唯一字段;
  • JOIN 字段:外键列必须有索引(否则 Nested-Loop 变全表扫描);
  • ORDER BY / GROUP BY :避免 Using filesort / Using temporary
  • 覆盖索引 :将 SELECT 所有字段纳入索引(注意:InnoDB 二级索引自动包含主键);
  • 函数查询 (MySQL 8.0+):为 UPPER(email)DATE(create_time) 等创建函数索引

4.2 何时避免索引?

  • 低选择性列 :如 gender(男/女)、status(0/1),选择性 < 0.1%;
  • 频繁 UPDATE 的列 :如 last_login_time,每更新一次需改索引;
  • 小表(< 1,000 行):全表扫描比索引更快(I/O 少);
  • 前导通配符LIKE '%MySQL' 无法用 B+ 树索引(考虑全文索引或 ngram 分词)。

索引失效常见场景 (不止 LIKE):

  • WHERE col + 1 = 10 → 改为 WHERE col = 9
  • WHERE CAST(col AS CHAR) = '123' → 保持类型一致;
  • OR 条件未全索引:WHERE a = 1 OR b = 2,若 b 无索引,则全表扫描。

4.3 SRE 运维黄金法则

  1. 不要过度索引

    每张表索引数建议 ≤ 5 个(核心表可放宽至 8 个)。

  2. 定期清理僵尸索引(MySQL 5.7+)

    复制代码
    -- 查看从未使用的索引
    SELECT * FROM sys.schema_unused_indexes;
  3. 大表加索引必须谨慎在线操作

    复制代码
    -- MySQL 8.0+ 支持在线加索引
    ALTER TABLE orders ADD INDEX idx_user_id (user_id), ALGORITHM=INPLACE, LOCK=NONE;

    注意LOCK=NONE 仅表示不阻塞 DML,但后台仍需全表扫描构建索引 ,对大表会产生显著 I/O 和 CPU 压力,可能污染 Buffer Pool 或引发复制延迟。
    建议:在业务低峰期执行,并监控 I/O 负载、线程堆积与缓存命中率。
    更安全方案 :使用 pt-online-schema-changegh-ost

  4. 组合索引遵循最左前缀 + 高选择性优先

    • 错误:(status, user_id)status 选择性低,WHERE user_id = ? 无法用索引;
    • 正确:(user_id, status) → 同时支持 user_id 单查 和 user_id + status 联合查。
  5. 善用 MySQL 8.4 隐藏索引

    复制代码
    -- 先隐藏索引,观察性能影响,再决定是否删除
    ALTER INDEX idx_old ON orders INVISIBLE;
    -- 验证无影响后
    DROP INDEX idx_old ON orders;

五、总结:

索引不是越多越好,而是"恰到好处"

  • 新项目:只建主键 + 必要业务索引(≤3 个);
  • 存量系统:每季度审查索引使用率,清理僵尸索引;
  • 大促前:检查执行计划,避免"优化器突然选错索引"。

MySQL 8.4 LTS 时代 ,借助函数索引、降序索引、隐藏索引 等能力,我们能更精细地控制查询性能,但核心原则不变
用数据说话,用监控验证,用最小成本换取最大收益


本文档适用于 MySQL 5.7 / 8.0 / 8.4 生产环境,所有建议均经过大规模 OLTP 系统验证。

相关推荐
JavaOpsPro4 小时前
审计 jenkins获取构建历史,生成excel
运维·jenkins·excel
摇滚侠4 小时前
全面掌握PostgreSQL关系型数据库,设置远程连接,笔记05,笔记06
java·数据库·笔记·postgresql
mpHH4 小时前
postgresql plancache --doing
数据库·学习·postgresql·1024程序员节
布朗克1684 小时前
MySQL 运算符详细说明
数据库·mysql·运算符·1024程序员节
小小的木头人4 小时前
Nagios Core + 插件 + Web 界面
运维
<但凡.4 小时前
Linux修炼:基础IO(二)
linux·运维·服务器·1024程序员节
wodongx1234 小时前
从一开始部署Android项目Sonarqube的自动化扫码+通知+增量扫描功能(Win环境、Docker,基于Jenkins)
运维·docker·jenkins·1024程序员节
机灵猫4 小时前
微服务中的服务熔断、降级与限流
java·数据库·微服务
枫叶丹45 小时前
破局政务数字化核心难题:金仓数据库以国产化方案引领电子证照系统升级之路
数据库·政务·1024程序员节·金仓