同样一条 SQL 只改 in 里的 id 数量,5000 个时解析加规划要 800ms,真正执行只要 30ms,慢在规划上。日志里能对上时间点,这种反而好排。

超长 IN 列表让解析和规划成本线性上涨,规划器还把每个值当独立条件估行,计划质量跟着变差,加索引也救不了。这一点先确认清楚,后面就不用反复试。

SELECT o.* FROM orders o
JOIN unnest(ARRAY[1,2,3,4]) AS t(id) ON o.id = t.id;
EXPLAIN (ANALYZE) SELECT id FROM orders
WHERE id = ANY(ARRAY[1,2,3]);

「PG 大 IN 列表」确认了原因之后,改动尽量只碰那一处,顺手改的东西后面最容易出问题。偶发问题先统计频率,一小时几次和一天一次,查法完全不同。

改完顺手把文档补一句,省得下次自己重新推一遍。监控和日志的时间要对上,时区不一致会多绕很大一圈。

临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。

处理上按这个顺序:把 id 列表改成数组参数,用 unnest 一次性传进去;连接条件改写成 join 形式,让规划器只估一次行数;确认数据库驱动支持数组类型绑定,再动业务代码。

结果对不对很好判断,同样五千个 id 总耗时降到 60ms 内,规划时间不再随数量涨。

作者 9823189