select查询慢八成因索引未用对;where字段无索引必致全表扫描,应优先为高频过滤列建索引,复合索引须遵循最左前缀原则,explain显示type=all即未走索引,隐式转换、函数操作、or含非索引列等均会导致失效。

SELECT查询慢,八成是因为没用对索引——不是语句写得差,而是索引建错、建多、或根本没被用上。
WHERE字段没索引,基本等于全表扫描
数据库遇到 WHERE user_id = 123 这种条件,如果 user_id 没索引,就会硬扫整张表。哪怕只想要 1 行,也要读完几百万行。这不是“慢”,是“必然慢”。
- 用
EXPLAIN SELECT * FROM users WHERE email = 'a@b.com';看执行计划,type=ALL就代表完全没走索引 - 高频过滤列优先建索引:比如
status、created_at、order_no - 复合索引要按查询顺序建:
WHERE status = 'paid' AND created_at > '2025-01-01'应建INDEX idx_status_created ON orders(status, created_at) - 最左前缀原则:这个索引能加速
WHERE status = ?或WHERE status = ? AND created_at > ?,但对WHERE created_at > ?单独用无效
函数、隐式转换、NULL 判断会让索引失效
哪怕字段有索引,只要在 WHERE 里动它一下,就可能白搭。
-
WHERE UPPER(email) = 'A@B.COM'→ 改成WHERE email = 'a@b.com'(确保大小写一致) -
WHERE YEAR(created_at) = 2023→ 改成WHERE created_at >= '2023-01-01' AND created_at -
email是VARCHAR,但传了数字或带空格字符串(如' a@b.com '),触发隐式类型转换,索引跳过 -
WHERE email IS NOT NULL在某些旧版 MySQL 中可能不走索引;若必须用,可配合ANALYZE TABLE users;刷新统计信息
覆盖索引能绕过回表,直接从索引拿数据
如果索引里已经包含查询所需全部字段,数据库就不用再查原表——这叫“覆盖索引”,I/O 直接砍掉一大截。
- 常查
SELECT user_id, name, email FROM users WHERE status = 'active',就建INDEX idx_status_uid_name_email ON users(status, user_id, name, email) - 注意字段宽度:
VARCHAR(500)全长建索引开销大,可考虑前缀索引INDEX (title(100)),但得验证前 100 字符是否真能区分大部分值 -
SELECT *会拖垮本可走索引的查询——尤其表里有TEXT、BLOB大字段时,数据库宁愿放弃索引、直接主键回表取全量
ORDER BY 和 LIMIT 配合索引容易踩坑
排序字段没索引、或和 WHERE 字段不在同一个索引里,就容易触发文件排序或临时表。
-
WHERE a = ? ORDER BY b要走索引,得建联合索引(a, b),而不是分开两个单列索引 -
ORDER BY b DESC和WHERE a = ?混用时,(a, b DESC)只在 MySQL 8.0+ 原生支持;老版本建(a, b)后,DESC可能无法利用排序能力 -
LIMIT 10000, 20这种深分页,即使有索引也要先定位前 10000 行;更适合改用游标分页,比如WHERE id > 12345 ORDER BY id LIMIT 20
真正难的不是“怎么建索引”,而是判断“哪条慢查询值得建、建什么、建几个”。一个 WHERE 带三个字段的查询,建单列索引还是三列复合索引?得看实际执行频率、字段选择性、以及是否常和其他字段组合出现——这些没法靠模板,得靠 EXPLAIN + 业务语义一起推。











