MySQL 索引失效的 12 种场景与解决方案(附执行计划分析)

准备测试表

SQL
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `phone` varchar(20) NOT NULL,
  `age` int NOT NULL,
  `city` varchar(20) NOT NULL,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name_age` (`name`, `age`),
  KEY `idx_phone` (`phone`),
  KEY `idx_created` (`created_at`)
) ENGINE=InnoDB;

1. 隐式类型转换

SQL
-- phone 是 varchar,用数字查询导致全表扫描
EXPLAIN SELECT * FROM user WHERE phone = 13800138000;  -- ❌ type=ALL

-- 正确
EXPLAIN SELECT * FROM user WHERE phone = '13800138000'; -- ✅ type=ref

2. 对索引列使用函数

SQL
SELECT * FROM user WHERE YEAR(created_at) = 2026;  -- ❌

-- 改写成范围查询
SELECT * FROM user
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';  -- ✅

3. 违反最左前缀

SQL
-- 联合索引 idx_name_age (name, age)
WHERE age = 18                -- ❌ 跳过 name
WHERE name LIKE '%Tom'        -- ❌ 左模糊
WHERE name = 'Tom' AND age > 10 -- ✅

4. OR 连接非索引列

SQL
WHERE name = 'Tom' OR city = '北京'  -- city 无索引 → 全表
-- 解决:给 city 加索引,或改成 UNION

5. != / NOT IN / NOT EXISTS

SQL
WHERE age != 18   -- 通常不走索引
-- 改写:age > 18 OR age < 18(仍需评估选择性)

6. IS NULL / IS NOT NULL

SQL
WHERE name IS NOT NULL  -- 数据量大时不走索引
-- 建议:用默认值 '' 代替 NULL

7. LIKE 左模糊

SQL
WHERE name LIKE '%abc'   -- ❌
WHERE name LIKE 'abc%'   -- ✅
-- 需要全文检索请用 FULLTEXT 或 ES

8. 范围查询后的列失效

SQL
-- idx(a, b, c)
WHERE a = 1 AND b > 10 AND c = 5
-- c 无法用于索引过滤(只能用到 a, b)

9. 字符集/排序规则不一致

SQL
-- 两表 JOIN 字段字符集不同 → 索引失效
ALTER TABLE t1 CONVERT TO CHARACTER SET utf8mb4;

10. 数据分布导致优化器放弃索引

SQL
-- 当匹配行数超过总数约 30% 时,优化器可能选全表
-- 可用 FORCE INDEX 强制,但应先确认统计信息准确
ANALYZE TABLE user;

11. 隐式字符编码转换

SQL
-- 表 utf8mb4,连接用 utf8 → 转换导致失效
SET NAMES utf8mb4;

12. 使用了 SELECT *

SQL
SELECT * FROM user WHERE name = 'Tom';
-- 需回表。改成覆盖索引可避免:
-- KEY idx_name_age_city (name, age, city)
SELECT name, age FROM user WHERE name = 'Tom';  -- Using index

排查工具

SQL
-- 查看执行计划
EXPLAIN FORMAT=JSON SELECT ...;

-- 查看实际执行
EXPLAIN ANALYZE SELECT ...;

-- 慢查询
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
打赏作者 已有 0 人打赏,共 ¥0.00
我的打赏
DBA老赵
DBA老赵
Lv7 学习会员 原创 2 粉丝 603

MySQL / Redis 调优,数据库就是我的战场

  • 2文章
  • 4.9万总阅读
  • 1716获赞
  • 603粉丝

评论(2)

DBA老赵・澳大利亚
是的,这个错误在开发阶段很难发现,因为数据量小时看不出区别。
24天前 👍 24 回复 举报
Java架构沉思录・澳大利亚
隐式类型转换这个坑我踩过,varchar 字段用数字查,慢查询日志里全是全表扫描。
1个月前 👍 20 回复 举报