做MySQL开发或者运维的朋友,大概率都被“慢查询”折磨过。一条明明很简单的SQL,数据量一上来就慢得出奇,老板在旁边盯着屏幕,你额头上的汗比查询耗时还显眼。这时候十有八九的问题出在索引上。给MySQL表添加索引,听起来就是一条ALTER TABLE或者CREATE INDEX的事,但真正落地的时候,你会发现里面有太多讲究:选什么类型的索引、加在哪些列上、什么时候加、加了之后执行计划到底走没走索引、会不会把表的写性能拖垮,每一步都藏着坑。这篇文章我就把你可能遇到的所有环节都拆开了讲,从索引原理到实操SQL,再到问题排查,尽量用我这些年实际踩坑的经验,帮你一次性把“给MySQL表添加索引”这件事彻底搞明白。
先说一个我最近处理的真实案例。线上有个订单表,数据量在3000万行左右,业务方反馈某个列表页接口越来越慢,高峰期经常超过5秒。我上去一看,慢查询日志里全是同一条SQL,WHERE条件里查的是user_id和status两个字段,但表上只有主键索引,没有任何二级索引。结果每次查询都是全表扫描,3000万行数据,不慢才怪。后来的处理方式也不复杂:给user_id和status加了一个联合索引,再加一个create_time的索引用于排序优化,接口响应时间直接从5秒降到了50毫秒以内。整个过程前后不到十分钟,但效果是立竿见影的。这个案例就是典型的“索引没加对”导致的性能问题,也是我写这篇文章的动机——索引不是越多越好,而是要加得准、加得巧。
1. 内容整体设计与思路拆解:索引到底解决了什么问题
1.1 索引的本质:把“翻书”变成“查目录”
在动手加索引之前,你得先想明白一件事:索引到底在解决什么问题?我见过不少刚入行的同事,一遇到查询慢就无脑加索引,结果越加越乱,磁盘空间占了不少,查询性能却没有本质提升。这就是典型的“知其然不知其所以然”。
用一个生活化的例子来解释。想象一下你去图书馆找一本书,如果图书馆没有目录系统,你就得从第一排书架开始,一本一本地翻,直到找到目标为止。在MySQL里,这种查找方式叫“全表扫描”(Full Table Scan),时间复杂度是O(n),数据量越大越慢。而索引就相当于图书馆的目录卡片,它告诉你某本书在哪个区、哪个架、哪个位置,你直接走过去拿就行了。在InnoDB存储引擎里,索引的底层实现是B+树,查找时间复杂度是O(log n),效率提升是指数级的。
MySQL默认的InnoDB引擎中,表实际上是以索引的形式组织的,这种结构叫索引组织表(Index-Organized Table)。主键索引的叶子节点直接存储整行数据,二级索引的叶子节点存储的是主键值。当你通过二级索引查数据的时候,需要先查到主键值,再通过主键去主键索引里拿完整行数据,这个过程叫“回表”。理解这个底层机制,你才能明白为什么索引列的选择、索引顺序、以及覆盖索引这些概念如此重要。
1.2 添加索引前必须回答的四个问题
我给生产环境的表加索引前,都会强迫自己回答四个问题:
第一,这条查询的频率有多高? 如果是一条只跑一次的后台任务,全表扫描就全表扫描吧,没必要为了它加索引。但如果是用户高频访问的接口,那索引几乎是刚需。
第二,这张表是读多写少还是写多读少? 索引不是免费的午餐。每次插入、更新、删除数据时,MySQL不仅要维护表数据,还要同步维护所有相关的索引树。索引越多,写入时的开销就越大。对于高并发写入的核心表,每加一个索引都要慎之又慎。
第三,WHERE条件里到底有哪些列? 这是最核心的问题。SQL的WHERE子句决定了索引的列选择。你要找出那些在WHERE、JOIN、ORDER BY、GROUP BY子句中高频出现的列,这些才是索引的候选列。
第四,能不能用覆盖索引减少回表? 如果查询所需要的所有列都在同一个二级索引里,MySQL就可以直接从这个索引中返回数据,不需要回表,这就是覆盖索引。性能提升非常显著。
这四个问题想清楚了,加索引的大方向就不会偏。接下来才进入具体的索引类型选型和SQL编写阶段。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心细节解析与实操要点:索引类型选型与SQL编写规范
2.1 常用索引类型对比:普通索引、唯一索引、联合索引、前缀索引
MySQL支持多种索引类型,但日常工作中用到最多的就这几种。我整理了它们在语法、适用场景和注意事项上的对比,你在选型的时候可以直接参考。
| 索引类型 | 创建语法 | 核心特点 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| 普通索引 | CREATE INDEX idx_name ON table(col) |
仅加速查询,不限制值唯一 | 大部分查询加速场景 | 是最基础的索引,冗余索引的高发区 |
| 唯一索引 | CREATE UNIQUE INDEX idx_name ON table(col) |
索引列值必须唯一,兼有约束和加速双重功能 | 业务上需要唯一性保证的字段,如手机号、邮箱 | 插入重复值会报错,需要先清理存量重复数据 |
| 联合索引 | CREATE INDEX idx_multi ON table(col1, col2) |
多列组合成一个索引树,遵循最左前缀原则 | WHERE条件中多列组合查询、排序分组 | 列顺序至关重要,最左列必须包含在查询条件中 |
| 前缀索引 | CREATE INDEX idx_prefix ON table(col(10)) |
只对列的前N个字符建立索引,节省空间 | 字段很长但前几个字符区分度高的列,如VARCHAR(255)的URL | 无法用于ORDER BY和GROUP BY,会损失一部分区分度 |
| 全文索引 | FULLTEXT INDEX |
用于全文检索,支持中文分词 | 文章内容、商品描述的模糊搜索 | 有专门语法,日常LIKE '%xx%'不走全文索引,性能一般 |
| 哈希索引 | USING HASH |
等值查询极快,范围查询无效 | 仅适合精确等值匹配(Memory引擎) | InnoDB不支持显式创建,自适应哈希是自动的内部优化 |
实际选型建议: 80%的场景用普通索引和联合索引就够了。唯一索引只在确实需要业务约束时使用,不要把所有普通索引都改成唯一索引,那样会给写入带来不必要的开销。前缀索引在字段非常长且区分度足够时可以考虑,但要注意一个坑:索引列的区分度不够会导致优化器放弃索引。我一般会用COUNT(DISTINCT LEFT(col, N)) / COUNT(*)这个比例来评估前缀长度N是否合适,比例越接近1越好,0.9以上基本可以接受。
2.2 联合索引的列顺序:把区分度最高的放最前面
联合索引是日常开发中最常用的多条件查询优化手段,但它的列顺序如果设计不合理,索引效果会大打折扣。联合索引遵循最左前缀原则:MySQL会按照索引定义时的列顺序,从左到右逐列匹配查询条件,一旦遇到范围条件(>, <, BETWEEN)或者最左边的列不在查询条件中,后续的索引列就无法生效。
举个实战例子。有个用户行为表,常用查询条件是WHERE user_id = ? AND action_type = ? AND create_time >= ?。如果把索引建为(action_type, user_id, create_time),而查询条件里user_id排第二,那么当单独用user_id查询时,这个索引就用不上,因为action_type才是最左列,它没有被条件约束。正确的做法是把选择性最高的列放最前面。所谓选择性,就是该列的重复值比例,重复值越少,选择性越高。通常user_id作为高基数列,应该排在action_type前面,所以更合理的联合索引是(user_id, action_type, create_time)。
还有一个细节容易被忽略:范围条件后面的列无法走索引。如果查询条件中有create_time >= ?这样的范围条件,那么create_time之后再加其他索引列,后续列在本次查询中基本用不上索引。所以在设计联合索引时,通常把等值条件列放前面,范围条件列放后面。
2.3 添加索引的SQL语法与可视化操作
给MySQL表添加索引,最常用的三种方式:
第一种,使用CREATE INDEX语句。
sql复制-- 普通索引
CREATE INDEX idx_user_id ON user_order(user_id);
-- 唯一索引
CREATE UNIQUE INDEX uk_user_phone ON user(phone);
-- 联合索引
CREATE INDEX idx_user_status_time ON user_order(user_id, status, create_time);
-- 前缀索引
CREATE INDEX idx_url_prefix ON url_table(url(20));
第二种,使用ALTER TABLE语句。
sql复制ALTER TABLE user_order ADD INDEX idx_user_id (user_id);
ALTER TABLE user_order ADD UNIQUE INDEX uk_order_no (order_no);
ALTER TABLE user_order ADD INDEX idx_user_status (user_id, status);
CREATE INDEX和ALTER TABLE ADD INDEX本质上是一样的,最终执行的内部操作相同。从可读性和扩展性来看,我更喜欢用ALTER TABLE,因为一个ALTER TABLE语句里可以同时加多个索引,例如:
sql复制ALTER TABLE user_order
ADD INDEX idx_user_id (user_id),
ADD INDEX idx_status (status),
ADD INDEX idx_create_time (create_time);
第三种,使用可视化工具。 如果你用的是Navicat、MySQL Workbench或者DBeaver,直接在表设计界面里找到“索引”选项卡,新增一行,填上索引名、选择索引类型、勾选索引列,点击保存就可以了。工具最终生成的还是前面那两种SQL,胜在直观,适合不熟SQL语法的同学,但线上生产环境的变更,我还是建议用SQL脚本走发布流程,方便留痕、回滚和审计。
2.4 核心操作流程:生产环境给大表加索引的标准动作
如果表的数据量很小,几万行,加索引就是秒级完成,怎么操作都行。但在生产环境,表动辄上千万行,直接执行一条ALTER TABLE很可能会把表锁住,造成长时间的业务阻塞。这是大表加索引最需要重视的问题。
第一步:确认MySQL版本和存储引擎。 MySQL 5.6及以上版本的InnoDB引擎支持在线DDL(Online DDL),大部分加索引的操作可以在不阻塞读写的情况下完成。5.5及以下版本加索引会锁表,必须选在业务低峰期操作。
第二步:检查表的当前状态。
sql复制SHOW TABLE STATUS LIKE 'user_order';
SHOW INDEX FROM user_order;
先了解表有多大、有哪些索引、引擎类型是什么,再决定策略。
第三步:选择合适的DDL算法和锁策略。 MySQL 8.0可以在ALTER TABLE语句里显式指定算法:
sql复制ALTER TABLE user_order
ADD INDEX idx_user_id (user_id),
ALGORITHM=INPLACE, LOCK=NONE;
ALGORITHM=INPLACE表示原地修改表,不需要复制整张表的数据;LOCK=NONE表示执行期间允许并发的读写操作。这两个参数组合起来就是最理想的在线加索引方式。如果MySQL版本不支持这些参数,也要确认默认行为是允许并发DML的。
第四步:如果表实在太大(比如超过1亿行),考虑使用工具平滑变更。 常用的有pt-online-schema-change(Percona Toolkit)和gh-ost(GitHub开源的gh-ost工具)。它们的核心原理都是创建一张影子表,把原表数据分批复制到影子表,同时在原表上建立触发器捕获增量变更,最后在业务低峰期切换表名。这种方式可以把加索引对业务的影响降到最低。我自己在一个5000万行的订单表上加索引时用的就是pt-osc,整个变更过程持续了大概40分钟,期间线上业务几乎无感知。
第五步:变更完成后立即验证。 通过SHOW INDEX FROM user_order确认索引已创建,再用EXPLAIN确认核心SQL正确走了新索引,最后关注变更后半小时内的慢查询和CPU负载,确保没有引入新的问题。
3. 实操过程与核心环节实现:从发现问题到加索引全流程
3.1 如何发现哪些SQL需要索引:慢查询日志与EXPLAIN分析
加索引不是拍脑袋的事,要先定位问题。我常用的排查路径是“慢查询日志 + EXPLAIN执行计划”两步走。
首先,确保慢查询日志开启并合理配置。通过下面的命令查看当前设置:
sql复制SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
在生产环境,我通常建议把long_query_time设为1秒,即超过1秒的查询就会被记录到慢查询日志。同时开启log_queries_not_using_indexes,这样那些明明没走索引的查询也会被记录下来,便于主动发现潜在的性能隐患。配置方式:
sql复制SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';
注意,long_query_time的修改对已存在的连接不生效,需要新连接才会使用新的阈值。
找到慢SQL之后,用EXPLAIN分析执行计划:
sql复制EXPLAIN SELECT * FROM user_order
WHERE user_id = 12345 AND status = 1
ORDER BY create_time DESC LIMIT 20;
执行计划里需要重点看这几列:
- type列:如果显示
ALL,就是全表扫描,通常意味着索引没建或者建了没用上。如果显示ref、range、const等,说明走了索引,性能相对较好。 - possible_keys列:可能被优化器选择使用的索引。
- key列:实际使用的索引。如果这一列是NULL,说明没用索引。
- rows列:预估扫描的行数。这个数字越大,查询越慢。
3.2 一个完整的加索引实操案例:5秒到50毫秒的优化过程
回到开头提到的那个订单表案例,我把完整的排查和优化过程一步一步还原给大家。
原SQL(线上慢查询日志抓到的典型语句):
sql复制SELECT id, order_no, user_id, status, amount, create_time
FROM user_order
WHERE user_id = 12345 AND status = 1
ORDER BY create_time DESC
LIMIT 20;
第一步,查看表结构:
sql复制SHOW CREATE TABLE user_order\G
表结构显示只有主键索引PRIMARY KEY (id),没有其他任何二级索引。这个表的数据量当时在3000万行左右。
第二步,用EXPLAIN分析执行计划:
sql复制EXPLAIN SELECT id, order_no, user_id, status, amount, create_time
FROM user_order
WHERE user_id = 12345 AND status = 1
ORDER BY create_time DESC
LIMIT 20;
结果里type=ALL,rows=30000000左右,也就是说每次查询都要扫描全表3000万行,难怪接口慢。
第三步,设计索引。 查询条件涉及user_id(等值)和status(等值),排序涉及create_time(降序)。按照联合索引设计原则,等值条件的两个列放前面,排列顺序按区分度:user_id的区分度远高于status,所以放在第一位;status放第二位;由于ORDER BY create_time需要排序,把create_time加在联合索引的尾部,这样索引天然有序,可以避免额外的文件排序(filesort)。
sql复制ALTER TABLE user_order
ADD INDEX idx_user_status_time (user_id, status, create_time),
ALGORITHM=INPLACE, LOCK=NONE;
第四步,再次EXPLAIN验证:
sql复制EXPLAIN SELECT id, order_no, user_id, status, amount, create_time
FROM user_order
WHERE user_id = 12345 AND status = 1
ORDER BY create_time DESC
LIMIT 20;
这次结果里type=ref,key=idx_user_status_time,rows=5(该用户符合条件的记录只有几行)。查询耗时从5秒左右下降到50毫秒以内。接口响应也同步恢复正常。
第五步,把新索引纳入监控。 加完索引之后,我在第二天和一周后分别看了两次慢查询日志,确认没有出现由于索引选择变化导致的其他SQL劣化,同时也观察了数据库的写性能指标,没有明显波动,这次变更才算真正关单。
3.3 关于索引体积和维护开销:加索引不是免费的
索引加快查询,但代价是存储空间和写放大,这一点很多刚接触索引的人会忽略。我见过一个极端的例子:有人在一张表上加了十几个索引,索引加起来的总空间比表数据本身还大。虽然表空间翻倍对大多数公司来说不是致命问题,但每次写入要维护十几棵索引树,写性能的退化是实实在在的,尤其是在高并发写入场景,影响会非常明显。
所以在设计索引时,我有一个习惯:每次加索引之前,先查一下这个索引能否复用已有的联合索引,避免建立重复索引。举个例子,已经有联合索引(user_id, status),再单独建立一个(status)索引就是冗余的,因为(user_id, status)的最左前缀已经覆盖了status列的查询场景(如果查询条件里没有user_id,那(status)才有独立价值,需要结合业务SQL具体判断)。冗余索引不仅浪费空间,还会拖慢写入。
另一个思路是尽量用覆盖索引来优化高频查询。所谓覆盖索引,就是索引中已经包含了查询所需的所有列,不需要再回表读取完整行。比如你的业务只需要SELECT user_id, status FROM user_order WHERE user_id = 123,那么(user_id, status)这个联合索引就是覆盖索引,查询时直接从索引树返回结果,省去了回表的IO开销。这种优化在高频小查询里收益非常可观。
4. 常见问题与排查技巧实录:索引失效与冲突处理
4.1 为什么加了索引查询还是慢:索引失效的8个典型场景
这个问题几乎每周都会有人来问我。索引建了,EXPLAIN一看,key是NULL或者type=ALL,索引压根没生效。我把最常见的8个失效场景列个表,你对照排查:
| 场景 | 示例 | 失效原因 | 解决方案 |
|---|---|---|---|
| 对索引列使用函数 | WHERE DATE(create_time) = '2024-01-01' |
函数导致索引列的值被加工,无法匹配B+树中的原始值 | 改为范围查询:WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02' |
| 对索引列隐式类型转换 | WHERE phone = 13812345678(phone为VARCHAR) |
MySQL会把字符串列和数字比较时进行隐式转换 | 保持类型一致,数字加引号 |
| 索引列参与运算 | WHERE clicks + 1 > 100 |
运算导致索引列值变化 | 把运算移到非索引列上:WHERE clicks > 99 |
| LIKE以通配符开头 | WHERE name LIKE '%张%' |
前导通配符导致无法利用B+树有序性 | 改用全文索引或前缀匹配:WHERE name LIKE '张%' |
| 联合索引违反最左前缀 | 索引(a, b),条件WHERE b = 1 |
查询条件没包含最左列a | 调整联合索引列顺序或增加新索引 |
| OR条件中有非索引列 | WHERE a = 1 OR b = 2(b无索引) |
需要全表扫描才能保证结果完整 | 为b加索引,或改写为UNION |
| 索引选择性太低 | WHERE sex = 'male'(sex只有两个值) |
优化器认为全表扫描比走索引更快 | 通常与高选择性列组合成联合索引 |
| 优化器统计信息错误 | 索引字段数据分布发生大变化 | 基数估计不准,优化器选错执行计划 | ANALYZE TABLE table_name 重新收集统计信息 |
我个人的排查心得: 遇到索引没生效,第一步先用SHOW INDEX FROM table_name确认索引确实存在且列正确;第二步查看字段字符集和排序规则是否与表一致(JOIN时两张表的字段字符集不一致会导致索引失效);第三步用EXPLAIN看执行计划,一步一步对照上面的表格排除;最后实在不行可以试试用FORCE INDEX强制走索引来做对比测试,但这是临时手段,本质还是要找出优化器弃用索引的根本原因。
4.2 添加唯一索引失败:如何处理存量重复数据
在已经存在重复数据的表上加唯一索引,是最常见的一个“撞墙”场景。执行:
sql复制ALTER TABLE user ADD UNIQUE INDEX uk_phone (phone);
如果phone字段有重复值,MySQL会直接报错:Duplicate entry '13800138000' for key 'uk_phone',整个DDL瞬间失败,不会创建任何索引。
处理方式分三步走。
第一步,找出重复记录:
sql复制SELECT phone, COUNT(*)
FROM user
GROUP BY phone
HAVING COUNT(*) > 1;
第二步,和业务方确认保留哪一条记录。 通常的规则是按主键保留最新一条(比如id最大的那一条),其余的历史重复数据进行合并或逻辑删除。这一步必须谨慎,因为删数据是不可逆的,一定要先备份或者把结果集导出确认。
第三步,删除重复数据后重新创建唯一索引。 如果业务允许,我自己更推荐一个“温和”的方案:先加一个普通索引,让查询能走索引,等数据治理完成后再把普通索引升级为唯一索引。虽然多一次DDL操作,但风险可控。
还有一个相关的热点问题:MySQL 8.0及以上版本执行ALTER TABLE默认是原子的,失败不会残留部分索引;但MySQL 5.7及以下版本在执行DDL时如果中途失败,有可能留下部分状态,需要手动清理。升级到8.0或者用专业工具执行DDL,能省掉不少麻烦。
4.3 复制表结构和数据后忘了索引,怎么快速对齐
另一个高频场景是:你用CREATE TABLE new_table LIKE old_table复制表结构,或者用INSERT INTO new_table SELECT * FROM old_table复制数据之后,发现新表没有索引(或者索引不完整)。CREATE TABLE ... LIKE确实会复制索引,但如果你是通过其他方式(比如批量导入、从逻辑备份恢复)建的表,忘记加索引是完全可能的。
快速对齐索引的方法是这样。先查看源表的索引定义:
sql复制SHOW CREATE TABLE old_table\G
拿到完整的索引定义SQL后,改成new_table再执行一遍。如果索引较多,手改容易出错,也可以借助pt-table-sync或者Navicat的“对象同步”功能自动对比并生成ALTER语句。还有一个小技巧:从旧的逻辑备份(mysqldump)文件里把CREATE TABLE语句和ALTER TABLE ... ADD INDEX单独抠出来执行,比手写SQL更可靠。
4.4 索引被优化器舍弃怎么办:用统计信息和FORCE INDEX兜底
有时候索引建了,执行计划却显示不走这条索引,优化器觉得全表扫描更“划算”。这通常发生在索引选择性不高,或者表的统计信息过时的时候。处理思路有两种。
第一,更新统计信息:
sql复制ANALYZE TABLE user_order;
这个命令会重新统计索引的基数(cardinality),让优化器基于最新数据分布做出选择。大多数情况下,执行完这个命令后优化器就能正确识别出索引的价值。这个操作成本不高,在数据量发生大变化后建议定期执行。
第二,如果更新统计信息后优化器仍然选择全表扫描,而你很确定这条SQL走索引更快,可以临时用FORCE INDEX验证:
sql复制SELECT *
FROM user_order FORCE INDEX (idx_user_status_time)
WHERE user_id = 12345 AND status = 1;
通过对比FORCE INDEX和不带FORCE INDEX的耗时,确认是不是优化器误判。确认无误后,再进一步分析为什么误判——通常是索引选择性不足、数据分布偏斜、或者查询条件存在范围查询导致成本估算畸高。要意识到FORCE INDEX只是排查手段,不建议永久写到业务代码里,因为一旦索引更名或删除,SQL会直接报错。
5. 索引设计实践总结与扩展
5.1 一张表加多少索引合适:我的经验阈值
很多人在索引数量上拿不准,我给出一个经验值供参考:单表二级索引数量建议控制在5个以内,最多不要超过7个。这不是硬性规定,而是基于写入维护成本和查询收益的平衡。
对于核心交易类表,读写并发都很高,索引每多一个,写入放大的问题就严重一分。我有一个淘汰冗余索引的习惯:每季度用performance_schema或者sys.schema_unused_indexes视图找出那些长时间没有被使用的索引,和业务方确认后清除。下面这条SQL可以直观看到哪些索引从未被使用:
sql复制SELECT * FROM sys.schema_unused_indexes;
这个视图会列出所有表上未使用的索引,非常实用。清理冗余索引和加索引同样重要,这能让索引设计始终保持精简高效。
5.2 利用performance_schema定位索引选择问题
如果你觉得EXPLAIN不够直观,可以借助performance_schema把SQL执行过程中的索引使用情况量化出来。MySQL 5.7以上的版本,默认开启了performance_schema的大部分采集项,我们可以通过下面这条SQL查看某条语句的实际执行统计:
sql复制SELECT DIGEST_TEXT, COUNT_STAR, SUM_ROWS_EXAMINED, SUM_ROWS_AFFECTED
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT LIKE '%user_order%'
ORDER BY SUM_ROWS_EXAMINED DESC
LIMIT 10;
SUM_ROWS_EXAMINED就是执行时扫描的行数总和。如果你在慢查询日志里看到某条SQL每执行一次扫描几十万行,但返回的数据只有几十行,那就说明索引设计还有优化空间。用数据说话,比拍脑袋猜要靠谱得多。
5.3 其他工具的锦上添花:pt-duplicate-key-checker
最后推荐一个小工具。Percona Toolkit里的pt-duplicate-key-checker可以自动扫描数据库中的所有索引,找出冗余和重复的索引,并给出删除建议。我在做数据库全面健康检查的时候,总会在每一台实例上跑一遍:
bash复制pt-duplicate-key-checker --host=localhost --user=root --password=xxx --databases=your_db
这个工具不会直接改库,只输出分析和建议,安全可靠。你会发现,在一次大版本升级或者多轮业务迭代之后,表里积累的冗余索引数量往往比你想象的多得多。
内容到这里,索引的核心知识基本覆盖完了。最后分享一点个人体会:索引这东西,深入进去就是一个完整的知识体系,从B+树数据结构到查询优化器怎么选择执行计划,每一层都有关联。平时遇到慢查询,别急着加索引,先花五分钟搞清楚SQL的访问模式,再决定索引怎么设计,这个习惯能让你的优化工作事半功倍。实际中如果拿不准,就在测试环境把数据量灌到接近生产规模,多跑几个EXPLAIN,踩过几次坑之后,你对每个索引的判断力会越来越准。
