where中对字段使用函数会导致索引失效,explain显示type=all或key=null;应改用范围查询,如where created_at >= ‘2024-01-01’ and created_at
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'却发现整个表被锁住、其他事务堵死,说明必须保证 WHERE 字段有有效索引:
- 确认索引存在:
SHOW INDEX FROM users,检查- 复合索引要注意顺序:如果已有
(status, email),它对WHERE email = ?是无效的;得是(email)或(email, status)- UPDATE 前先
SELECT ... FOR UPDATE?没必要——UPDATE 本身就会加行锁,但前提是能定位到具体行;没索引,就升级成表锁索引不是越多越好,但 WHERE、JOIN、ORDER BY、GROUP BY 涉及的字段,没索引就是给自己埋雷。线上加索引记得用
ALGORITHM=INPLACE,不然锁表时间可能超预期。
