JOIN时ON中避免函数计算,应建函数索引或清洗数据;慎用LEFT JOIN后WHERE过滤右表;用STRAIGHT_JOIN指定小表驱动;只SELECT必要字段;注意引擎差异及索引缺失。

JOIN时别在ON里写函数,CPU会疯
数据库执行JOIN前要对连接字段做大量比对,如果ON条件里用了UPPER()、DATE()、CONCAT()这类函数,MySQL/PostgreSQL就得为每一行临时计算一次——不是一次算完再比,是每比一对就调一次函数。CPU直接飙高,执行计划里常看到Using temporary; Using filesort。
- 错误写法:
ON UPPER(a.name) = UPPER(b.name)→ 强制全表函数计算 - 正确做法:提前在字段上建函数索引(如
CREATE INDEX idx_name_upper ON users ((UPPER(name)))),或更稳妥地——清洗数据,让name本身统一大小写后走普通B+树索引 - 特别注意
LEFT JOIN后接WHERE过滤右表字段(如WHERE b.status = 'active'),这会让左连接退化成内连接,还可能让优化器放弃使用索引
用STRAIGHT_JOIN强制驱动表顺序
当多表JOIN时,优化器有时会选错“驱动表”(外层循环表),比如拿100万行的表去循环匹配1000行的表,而不是反过来。这时STRAIGHT_JOIN能绕过优化器,手动指定小表驱动大表,大幅减少嵌套循环次数。
- 适用场景:明确知道某张表结果集极小(如配置表、状态字典表),且JOIN条件上有高效索引
- 写法示例:
SELECT /*+ STRAIGHT_JOIN */ ... FROM small_table s STRAIGHT_JOIN big_table b ON s.id = b.small_id - 风险点:MySQL 8.0+支持
/*+ STRAIGHT_JOIN */提示,但旧版本只认STRAIGHT_JOIN关键字,且一旦驱动表选错,性能反而更差——务必先用EXPLAIN验证rows估算值
避免SELECT * 和冗余字段参与JOIN计算
CPU占用不只来自连接逻辑,还来自后续投影(projection)和传输。如果SELECT *拉回几十个字段,其中包含TEXT、JSON甚至BLOB,不仅内存暴涨,排序、哈希JOIN建桶、网络传输全跟着吃紧。
- JOIN前先
SELECT只取业务必需字段,尤其避开大字段和计算列(如CONCAT(first_name, ' ', last_name) AS full_name) - 如果必须用计算字段做连接,把它物化到临时表或CTE里,确保只算一次:
WITH clean_users AS (SELECT id, UPPER(email) AS email_up FROM users) SELECT ... FROM clean_users u JOIN orders o ON u.email_up = o.user_email_up - PostgreSQL中注意
GROUP BY或ORDER BY若引用了未出现在SELECT中的JOIN字段,可能触发额外排序,间接抬高CPU
小表广播 vs 大表分片:JOIN策略得看引擎
不同存储引擎处理JOIN的方式差异极大。InnoDB靠B+树索引+缓冲池,适合小驱动表+大被驱动表;而ClickHouse、Doris这类OLAP引擎默认把小表广播到所有节点,大表本地分片JOIN——选错策略,CPU和网络都扛不住。
- MySQL下:确认
join_buffer_size是否够用(默认256K),太小会导致多次磁盘扫描;但设太大又挤占InnoDB buffer pool,得权衡 - ClickHouse中:用
JOIN SETTINGS join_algorithm = 'auto'通常够用,但若右表超50MB,显式设'direct'避免广播开销 - 容易忽略的一点:时间范围JOIN(如
ON a.dt BETWEEN b.start_dt AND b.end_dt)无法走传统索引,CPU必高——改用区间树扩展(如INTERSCT函数)或预打宽表
真正卡住CPU的往往不是JOIN语法本身,而是连接字段没索引、隐式类型转换、或者JOIN后立刻GROUP BY一堆未索引字段——这些地方一动不动,光调参数没用。

















