WHERE中对字段使用函数会导致索引失效,EXPLAIN显示type=ALL或key=NULL;应改用范围查询,如WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01'。

WHERE 条件里用函数导致索引失效
查得慢,EXPLAIN 看到 type=ALL 或 key=NULL,八成是 WHERE 里对字段用了函数。比如 WHERE YEAR(created_at) = 2024,MySQL 没法走 created_at 上的索引。
改成范围查询更安全:
WHERE created_at >= '2024-01-01' AND created_at- 如果必须按月查,建生成列 + 索引:
ALTER TABLE orders ADD COLUMN ym CHAR(7) STORED AS (DATE_FORMAT(created_at, '%Y-%m'));,再在ym上建索引 - 避免
LIKE '%abc',前导通配符会让索引完全失效;LIKE 'abc%'还能用上索引
JOIN 多表时驱动表选错
高并发下 JOIN 慢,不是因为表多,而是 MySQL 选错了驱动表(即外层循环表)。EXPLAIN 显示 rows 值特别大、且出现在第一行的表,大概率就是被当成了驱动表。
让小结果集做驱动表:
- 加
STRAIGHT_JOIN强制顺序:SELECT STRAIGHT_JOIN ... FROM small_table s JOIN big_table b ON s.id = b.small_id - 给 JOIN 条件字段补索引:确保
b.small_id有索引,否则即使small_table小,big_table也会全表扫 - 别依赖优化器自动判断——它只看统计信息,而统计信息可能过期,
ANALYZE TABLE定期跑一次
ORDER BY + LIMIT 在分页深度大时卡死
比如 SELECT * FROM orders ORDER BY id DESC LIMIT 100000, 20,MySQL 得先排序出前 100020 行,再扔掉前 100000 行。并发一高,CPU 和 I/O 都顶不住。
改用游标分页(cursor-based pagination):
- 第一次查:
SELECT id, name, created_at FROM orders ORDER BY id DESC LIMIT 20,记下最后一条的id(比如id=98765) - 下一页查:
SELECT id, name, created_at FROM orders WHERE id - 不能用
OFFSET做分页,尤其在写入频繁的表上,OFFSET越大,越容易读到重复或跳过的数据
高并发 UPDATE 按非主键条件更新锁表
执行 UPDATE users SET status = 1 WHERE email = 'x@y.z' 却发现整个表被锁住、其他事务堵死,说明 email 没索引,MySQL 只能走聚簇索引全扫,每行都加 X 锁。
必须保证 WHERE 字段有有效索引:
- 确认索引存在:
SHOW INDEX FROM users,检查email是否在某索引的最左前缀上 - 复合索引要注意顺序:如果已有
(status, email),它对WHERE email = ?是无效的;得是(email)或(email, status) - UPDATE 前先
SELECT ... FOR UPDATE?没必要——UPDATE 本身就会加行锁,但前提是能定位到具体行;没索引,就升级成表锁
索引不是越多越好,但 WHERE、JOIN、ORDER BY、GROUP BY 涉及的字段,没索引就是给自己埋雷。线上加索引记得用 ALGORITHM=INPLACE,不然锁表时间可能超预期。

















