1. MySQL索引失效的核心原理与影响分析
索引失效的本质是查询优化器判断全表扫描比使用索引更高效。当出现以下情况时,MySQL会放弃使用索引:
- 成本估算偏差:优化器认为需要回表的数据量超过表记录的30%(InnoDB引擎阈值)
- 索引统计信息过期:ANALYZE TABLE未及时执行导致基数(cardinality)计算不准确
- 查询条件无法形成有效的扫描区间(range)
实测案例:某用户表500万数据,对phone字段建立单列索引。执行SELECT * FROM users WHERE phone LIKE '%8888'时,执行计划显示type=ALL(全表扫描),因为前导通配符导致无法利用B+树的有序性。
2. 高频索引失效场景详解
2.1 违反最左前缀原则
联合索引(a,b,c)的实际生效情况:
- ✅
WHERE a=1 AND b=2 - ✅
WHERE a=1 ORDER BY b - ❌
WHERE b=2(跳过了a列) - ❌
WHERE a=1 AND c=3(中间缺失b列)
注意:MySQL8.0+支持Index Skip Scan优化,但仅当首列不同值较少时有效
2.2 对索引列使用函数或运算
sql复制-- 失效案例
SELECT * FROM orders WHERE DATE_FORMAT(create_time,'%Y-%m')='2024-01';
SELECT * FROM products WHERE price+100 > 500;
-- 优化方案
SELECT * FROM orders
WHERE create_time BETWEEN '2024-01-01' AND '2024-01-31';
2.3 隐式类型转换
常见陷阱:
- VARCHAR字段存储数字时:
WHERE code=123(应改为WHERE code='123') - DATETIME与字符串比较:
WHERE create_time='2024-01-01'(低版本可能失效)
2.4 LIKE前导通配符
sql复制-- 全表扫描
SELECT * FROM articles WHERE title LIKE '%数据库%';
-- 可以使用索引
SELECT * FROM articles WHERE title LIKE 'MySQL%';
2.5 OR条件连接非索引列
sql复制-- 失效案例(age无索引)
SELECT * FROM users WHERE name='张三' OR age=25;
-- 优化方案
SELECT * FROM users WHERE name='张三'
UNION ALL
SELECT * FROM users WHERE age=25 AND name!='张三';
3. 高级失效场景与优化策略
3.1 IN列表值过多
当IN列表超过一定数量(默认200-300),优化器可能选择全表扫描。解决方案:
sql复制-- 分批查询
SELECT * FROM products WHERE id IN (1,2,3...100);
SELECT * FROM products WHERE id IN (101,102...200);
3.2 索引列参与排序但无筛选
sql复制-- 文件排序(Using filesort)
SELECT * FROM logs ORDER BY create_time DESC;
-- 优化:添加where条件或limit
SELECT * FROM logs WHERE create_time > '2024-01-01' ORDER BY create_time DESC;
3.3 数据分布不均时的优化器误判
当某个值占比过高(如status=1占90%),优化器可能放弃索引。解决方案:
- 使用FORCE INDEX强制索引
- 添加更精确的查询条件
4. 索引使用最佳实践
4.1 监控工具推荐
sql复制-- 查看索引使用情况
SELECT * FROM sys.schema_index_statistics
WHERE table_schema='your_db';
-- 执行计划分析
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE...;
4.2 索引设计原则
- 区分度高的列优先(cardinality/rows比值越大越好)
- 频繁查询的列考虑覆盖索引
- 避免过度索引(每个额外索引增加写操作开销)
4.3 参数调优建议
ini复制# my.cnf关键参数
optimizer_switch='index_merge=on'
optimizer_search_depth=4
range_optimizer_max_mem_size=8M
5. 面试深度问题解析
5.1 为什么IS NULL有时能用索引?
取决于NULL值在索引中的存储方式:
- InnoDB将NULL视为最小值存储
- 当NULL占比低时可能走索引
- 可配合
ALTER TABLE t MODIFY col INT NOT NULL DEFAULT 0优化
5.2 联合索引(a,b)条件下WHERE b=?会扫描整个索引吗?
是的,但比全表扫描快:
- 遍历索引树的所有叶子节点
- 只比全表扫描少一次回表操作
- 8.0+版本可通过
SET optimizer_switch='skip_scan=on'尝试优化
5.3 如何强制使用特定索引?
sql复制-- 语法示例
SELECT * FROM users FORCE INDEX(idx_phone) WHERE phone LIKE '138%';
-- 查看可用索引
SHOW INDEX FROM users;
实际项目中,建议先通过EXPLAIN分析,再考虑使用FORCE INDEX。某电商平台在促销期间通过强制索引使QPS从200提升到1500。
