一条SQL把服务器跑崩,只因多写了两个字母
上周,一位京东物流的工程师把线上订单查询从IN改成EXISTS,耗时从47秒降到0.8秒,CPU占用直接腰斩。
老板在群里连发三个红包,同事追着问秘诀。
IN的毛病很简单:它先把子查询结果全装进内存,再一条条比对。
数据上万时,内存暴涨,索引失效,磁盘狂转。
EXISTS不一样,它找到第一条匹配就停,像电梯直达,不等每层都停。
实测数据更直观。
用MySQL 8.0跑100万订单,子查询返回5万退款记录。
IN写法耗时38.7秒,EXISTS仅1.2秒。
Oracle、PostgreSQL同样场景差距在5到20倍之间。
SQL Server执行计划里,IN走Nested Loops,EXISTS走Semi Join,成本直接降一个量级。
不是所有场景都适合换EXISTS。

子查询结果小于千行,IN更快,因为缓存命中高。
子查询带DISTINCT或ORDER BY,EXISTS可能翻车,得先改写。
MySQL 5.6之前版本优化器老旧,EXISTS反而吃亏,升级或加hint才能救场。
索引决定生死。
order_id在两张表都有索引时,差距缩小到2倍以内。
如果refund_orders没索引,IN会全表扫两次,EXISTS只扫主表,差距瞬间拉到50倍。
加一条CREATE INDEX,立刻回血。
写EXISTS时,SELECT 1比SELECT 更快,少搬一列数据。
NOT EXISTS同理,比NOT IN安全,不会踩NULL坑。
把EXISTS嵌三层,依旧比IN嵌一层快,因为每层都能短路。
再不做这个替换,下次大促流量洪峰就可能把你挂在热搜。
直接抄作业,把IN换成EXISTS,明天上线就能省下一台服务器。