MySQL同表两列均建索引仅一列查询快,PHPMyAdmin中如何排查?
1. 确认索引实际创建状态
对任意异常表执行以下命令,检查cnic对应的索引是否存在,以及索引配置是否符合预期:
SHOW INDEX FROM 你的表名;
重点核对:
- Key_name列是否存在你创建的cnic相关索引
- Column_name列是否为
cnic - Sub_part列是否为NULL(如果是前缀索引,确认前缀长度是否足够区分数据)
- Non_unique列是否为1(普通索引正常,唯一索引为0,不影响索引使用)
2. 分析查询执行计划
对慢查询语句执行EXPLAIN,确认是否触发了索引:
EXPLAIN SELECT * FROM 你的表名 WHERE cnic='测试值';
重点查看输出字段:
type:正常走索引的等值查询应该为ref,如果是ALL说明触发了全表扫描key:如果值为你创建的cnic索引名说明走了索引,为NULL说明没走rows:扫描行数如果和表总数量级接近,说明没用到索引
3. 排查最常见原因:字符集/排序规则不匹配
如果30-40张表统一出现该问题,90%以上的概率是字符集/排序规则不匹配导致的隐式转换,触发索引失效:
- 查看cnic列的字符集和排序规则:
SHOW FULL COLUMNS FROM 你的表名 WHERE Field='cnic'; - 查看当前数据库连接的字符集和排序规则:
SHOW VARIABLES LIKE '%collation%'; SHOW VARIABLES LIKE '%character_set%';
如果列的排序规则和连接排序规则不一致(比如列是utf8mb4_general_ci,连接是latin1_swedish_ci),MySQL会自动把cnic列的所有值转成连接对应的字符集再比较,索引完全失效。
这种情况只要统一列和连接的字符集排序规则即可解决,同时注意n列的字符集大概率和连接是匹配的,所以n的索引正常生效。
4. 排查索引区分度问题
如果cnic列的重复值占比极高,MySQL优化器会判定全表扫描比走索引回表成本更低,主动放弃使用索引:
- 执行以下命令查看cnic索引的基数(区分度):
SHOW INDEX FROM 你的表名 WHERE Column_name='cnic';
Cardinality字段的值越接近表的总行数,说明索引区分度越高,如果该值远小于总行数,说明重复值太多,索引实际使用价值很低。
可以手动统计重复值占比验证:
SELECT COUNT(DISTINCT cnic)/COUNT(*) FROM 你的表名;
结果小于0.1的话,说明区分度过低,优化器大概率不会选择走索引。
5. 验证优化器执行计划选择是否正确
可以强制指定走索引测试查询速度:
SELECT * FROM 你的表名 FORCE INDEX(你的cnic索引名) WHERE cnic='测试值';
如果强制走索引后速度明显提升,说明是MySQL的表统计信息不准确导致优化器判断错误,执行以下命令更新统计信息即可:
ANALYZE TABLE 你的表名;
注意:5亿行的大表执行ANALYZE会有短时间锁表,建议在业务低峰期操作。
6. 排查隐式类型转换问题
如果cnic列是字符串类型,但实际查询时传入的变量是数值类型,即使加了单引号也可能触发隐式转换,比如cnic列存的是带前导零的字符串,传入的是数值类型的12345,会导致索引失效。可以检查业务代码中传入的$VARIABLE变量类型是否和cnic列定义的类型完全一致。
内容的提问来源于stack exchange,提问作者Muhammad Ahmed

