MySQL IN超1000值时执行计划崩、解析卡死或报错,因eq_range_index_dive_limit默认200触发估算降级致索引失效;Oracle则硬限1000字面量,超限直接ORA-01795报错,二者均需改用临时表+JOIN规避。

不是“慢一点”,是执行计划崩、解析卡死、甚至直接报错。MySQL 和 Oracle 都会在 IN 列表超量时触发机制性退化,而不是线性变慢。
MySQL 超 1000 个值后优化器放弃精确索引探测
MySQL 的 eq_range_index_dive_limit 默认为 200,当 IN 后值数量 ≥ 1000 时,优化器大概率跳过实际索引统计(index dive),改用粗略的表级行数估算 → 执行计划选错,比如该走 ref 却变成 ALL 全表扫描。
- 实测中,IN(1..999) 可能走主键索引,IN(1..1001) 就突然变成全表扫
- 即使字段有索引,也救不了——因为优化器压根没打算用它
- 调大
eq_range_index_dive_limit治标不治本,且会拖慢其他查询的预估耗时
Oracle 直接报 ORA-01795:列表表达式超限
Oracle 硬编码限制 1000 个字面量,超过就拒绝解析,报 ORA-01795: maximum number of expressions in a list is 1000,不是性能问题,是语法错误。
- 哪怕你只多一个值(1001),SQL 就根本不会进执行阶段
- 动态拼接、占位符生成都绕不过这个 parser 层硬检查
- 某些 JDBC 驱动在 prepare 阶段就会提前拦截并抛异常,甚至不发到数据库
两者共有的网络与内存瓶颈
无论 MySQL 还是 Oracle,超长 IN 列表都会撞上 max_allowed_packet(MySQL)或 PGA 内存上限(Oracle),导致“Packets too large”或“ORA-04030: out of process memory”。
- 10000 个整型 ID 拼成字符串,轻松突破 4MB 默认
max_allowed_packet - SQL 文本长度每增一倍,解析耗时非线性增长,实测 5000 项时解析占总耗时 70%+
- 连接池里一条慢查询可能卡住整个连接,HikariCP 的
connection-timeout常比 SQL 执行还先触发
真正危险的不是“超了1000会慢”,而是你没意识到:一旦跨过那个阈值,系统行为就从“可预测的慢”切换成“不可控的崩”。临时表 + JOIN 是唯一能把控制权拿回来的方式,但很多人还在徒劳地调 batch_size=999。


















