SELECT * FROM orders WHERE a = 1 AND b > 10 AND c = 3;
实际用到的索引
只用到了a和b,c没用上。
原因
范围查询(>、<、BETWEEN、LIKE)会导致后面的列无法使用索引。
因为在b > 10的范围内,c的值不是有序的。
优化建议
把等值查询的列放前面,范围查询的列放后面:
七、NOT IN和NOT EXISTS
示例
代码高亮:
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM blacklist);
情况
IN通常可以用索引
NOT IN某些情况会导致全表扫描
优化
-- 用LEFT JOIN替代
SELECT u.* FROM users u
LEFT JOIN blacklist b ON u.id = b.user_id
WHERE b.user_id IS NULL;
八、使用!=或<>
示例
SELECT * FROM orders WHERE status != 1;
情况
不等于查询有时候会导致索引失效,取决于数据分布。
如果status != 1的数据占大多数,MySQL可能认为全表扫描更快。
建议
用EXPLAIN看实际执行计划,如果数据分布合适,可以改成:
SELECT * FROM orders WHERE status IN (0, 2, 3, 4);
九、IS NULL的情况
老版本MySQL
IS NULL可能导致索引失效。
MySQL 5.7+
IS NULL可以使用索引:
代码高亮:
SELECT * FROM users WHERE phone IS NULL; -- 可以用索引
建议
尽量不要用NULL,用默认值代替
如果必须用NULL,确保MySQL版本较新
十、ORDER BY没用上索引
联合索引
CREATE INDEX idx_abc ON orders(a, b, c);
能用索引排序的
ORDER BY a
ORDER BY a, b
ORDER BY a, b, c
ORDER BY a DESC, b DESC, c DESC -- 方向一致
不能用索引排序的
ORDER BY b -- 没有a
ORDER BY a ASC, b DESC -- 方向不一致
ORDER BY a, c -- 跳过了b
排查索引问题的流程
EXPLAIN看执行计划
type:ALL是全表扫描,ref/range/const是用了索引
key:实际使用的索引
rows:预估扫描行数
2.看possible_keys和key
possible_keys有值但key是NULL:索引存在但没用上
3.开启optimizer_trace SET optimizer_trace = 'enabled=on'; SELECT * FROM orders WHERE ...; SELECT * FROM information_schema.optimizer_trace\G 可以看到MySQL为什么选择了某个执行计划。