SQL 高并发 SQL 查询优化实践

3次阅读

where中对字段使用函数会导致索引失效,explain显示type=all或key=null;应改用范围查询,如where created_at >= ‘2024-01-01’ and created_at

SQL 高并发 SQL 查询优化实践

WHERE 条件里用函数导致索引失效

查得慢,EXPLAIN 看到 type=ALLkey=NULL,八成是 WHERE 里对字段用了函数。比如 WHERE YEAR(created_at) = 2024mysql 没法走 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,不然锁表时间可能超预期。

text=ZqhQzanResources