应通过sys.schema_table_statistics中rows_full_scanned>100000且非系统库的表,结合慢查询日志中EXPLAIN显示type:ALL的SQL,精准定位需加索引的热点表与字段,而非仅依赖INDEX_LENGTH或TABLE_ROWS判断。

MySQL 缺少索引的表,不能靠猜,得靠 sys.schema_table_statistics 和慢查询日志交叉验证——单看一个容易误判。
查哪些表被频繁全表扫描
真正影响性能的是「被反复全表扫描的表」,不是「没建索引的表」。MySQL 8.0+ 的 sys 库里有现成指标:
-
sys.schema_table_statistics中的rows_full_scanned字段,记录自上次重启以来该表被全表扫描的总行数 - 重点看
rows_full_scanned > 100000且table_schema是业务库的表 - 注意:这个值不区分 SELECT 类型,
SELECT COUNT(*)或带LIMIT 1的查询也可能触发全扫,得结合实际 SQL 看
SELECT table_schema, table_name, rows_full_scanned
FROM sys.schema_table_statistics
WHERE rows_full_scanned > 100000
AND table_schema NOT IN ('sys', 'mysql', 'information_schema')
ORDER BY rows_full_scanned DESC;
用慢查询日志反向定位缺失索引的 SQL
很多表“看起来没被扫”,但某条高频 SQL 每次都走全表——这才是真瓶颈。关键不是表,是执行计划里的 type: ALL:
- 开启
slow_query_log = ON,并设long_query_time = 1(别用 0,否则日志爆炸) - 用
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log找出最耗时的 SQL - 对每条慢 SQL 执行
EXPLAIN FORMAT=TREE,重点看是否出现<not used>或type: ALL且key: NULL - 特别警惕
WHERE条件字段没索引、但ORDER BY字段有索引的情况——排序仍可能回表全扫
INFORMATION_SCHEMA.TABLES 不适合直接判断索引缺失
有人查 INFORMATION_SCHEMA.TABLES 看 TABLE_ROWS 大但 INDEX_LENGTH = 0,这方法问题很大:
-
INDEX_LENGTH包含主键和所有二级索引,但 MyISAM 表的主键不算在内;InnoDB 主键聚簇索引又一定存在,所以INDEX_LENGTH = 0几乎不会发生 - 空表或刚 truncate 的表,
INDEX_LENGTH可能为 0,但这跟“缺索引”无关 -
TABLE_ROWS本身是估算值,误差常达 40% 以上,不能作为是否需要索引的依据
ALTER TABLE 加索引时的真实代价
找到该加索引的表后,别急着 ALTER TABLE ... ADD INDEX。不同版本和引擎行为差异极大:
- MySQL 5.6+ InnoDB 支持
ALGORITHM=INPLACE,但仅限于添加二级索引;加主键或修改列仍要锁表 - 如果表上有大文本字段(
TEXT/BLOB),即使INPLACE也可能触发临时文件写入,IO 尖峰明显 - 线上环境务必先在从库执行,观察
SHOW PROCESSLIST是否卡在copy to tmp table——这是 DDL 正在重建表的信号 - 小表(<10 万行)可直接操作;大表建议用
pt-online-schema-change,但要注意它会额外增加主从延迟
索引不是越多越好,联合索引字段顺序、是否覆盖查询、是否引发回表——这些比“有没有索引”更关键,也更容易被监控工具忽略。










