You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL统计信息正确时Explain Analyze为何误估行数

问题成因

这个估算偏差本质是PostgreSQL 13优化器的两个固有逻辑限制共同导致的,42这个看似无依据的估算值完全可以按照优化器的计算逻辑复现:

  1. 等价条件传递的边界限制
    当你直接用p.id=53做过滤时,优化器的等价类推导逻辑会直接生成c.id_ref=53的常量过滤条件,扫描child_tb_ten_6分区时可以直接命中id_ref列的高频值(MCV)统计——id_ref=53在该分区的出现频率是1,因此直接算出准确的475759行。
    但当你用p.descr='xxx'做过滤时,优化器虽然能算出父表过滤后仅返回1行、对应唯一的p.id值,却不会把这个未知的id常量值传递给子表的id_ref列做MCV匹配。这个限制的核心是:PostgreSQL不会自动推导跨表、跨列的1:1映射关系,哪怕父表的descr列有唯一索引,优化器也不会在计划阶段把descr对应的id值查出来,再代入子表做行数估算。
  2. 未知值的选择率假设+并行估算修正
    当优化器无法确定子表id_ref要匹配的具体常量值时,会默认假设这个待匹配的值不属于已知的高频值集合,而是落在剩余的低频值区间,按平均低频值选择率计算:
    • 全局child_tb的id_ref列统计信息中,前两个高频值(53、4)的频率和为0.7968+0.0307≈0.8275,剩余59个值总频率为0.1725,平均每个低频值的选择率约为0.1725/59≈0.0029
    • 异常执行计划选择了2个并行worker,算上主进程共3个并行扫描进程,单进程分配到的child_tb_ten_6扫描行数约为475759/3≈158586
    • 单进程的join估算行数经过join去重修正后约为18行,3个进程的估算行数加总后再经过并行成本修正,就得到了总估算值42行。

你做的物化CTE测试得到7799的估算值,是因为物化CTE阻断了优化器的跨表条件推导,优化器不会触发「排除高频值」的逻辑,直接按平坦分布假设(分区总行数/全局id_ref去重值数量=475759/61≈7799)计算,反而得到了量级更合理的结果。

可行的解决方法
  • 最稳妥的方案是调整查询写法,先通过descr查出对应的父表id,再用id做子表过滤,也就是你第一次测试的写法,避免让优化器自己做跨列的值推导。
  • 可以升级到PostgreSQL 14及以上版本,该版本修复了分区表并行join场景下的MCV匹配逻辑,对于唯一索引侧过滤后单值的场景,能正确触发常量传递得到准确的行数估算。
  • 若不方便改语句或升级版本,可以尝试在child_tb的(id_tenant, id_ref)列上创建扩展统计信息,一定程度上修正联合分布的选择率估算,但该方法对跨表条件传递的场景效果有限。

内容的提问来源于stack exchange,提问作者milosla1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 13:54:26