DB2的优化器在生成执行计划时,会依据一系列数学模型对中间结果集的行数进行估算,估算得越准确,最终选择的连接顺序和连接方法就越合理。在复杂的OLAP或者报表类查询中,涉及多表连接的情况非常常见,此时优化器对每一个连接步骤的估算误差会随着连接层数不断放大,最终可能导致一个糟糕的计划。opt_enable_partial_productivity就是与这类估算相关的一个内部优化开关,它允许优化器在基数估算过程中引入部分生产力的建模思路,从而改善某些场景下的估算精度。本文将从原理、启用方法和实际效果三个方面,把这个参数讲清楚。

部分生产力的概念与参数作用机制
所谓部分生产力,来源于关系代数中连接结果估算的一个细节问题。传统的估算方式在计算两表连接的输出行数时,通常假设连接谓词的选择率可以独立相乘,再乘以两个输入的基数。但在存在复杂谓词、范围条件或者多列相关的情况下,这种独立假设往往偏差很大。部分生产力建模的思路是:在构造连接的过程中,允许优化器识别哪些谓词只在连接的某一侧就能被提前应用并产生过滤效果,把这部分过滤收益显式地纳入到连接顺序的搜索空间中,而不是机械地等到连接完成后再统一计算。
opt_enable_partial_productivity启用后,优化器在动态规划搜索连接顺序时,会对每个候选计划的部分谓词下推收益做一次额外的评估。换句话说,它扩展了优化器的搜索空间,让那些看似中间结果较大、但能够提前过滤掉大量数据的计划不会被过早剪枝掉。这也是它的两面性所在:搜索空间变大意味着优化时间可能增加,对于连接表数量非常多的查询,编译耗时的上升需要纳入考虑。
需要注意,这个参数属于优化器内部行为开关,并非所有版本都对外公开文档化,不同版本中默认值也可能不同。在正式环境启用之前,一定要先确认当前版本的默认行为,并在测试环境验证效果。
如何查看与启用该参数
DB2的优化器内部开关大多通过DB2_OPTER_PARAMS注册变量来传递。查看当前设置可以在命令行下执行:
db2set -all
# 输出中查找包含 DB2_OPTER_PARAMS 的行
# 如果没有输出该变量,说明当前使用默认值
db2 "SELECT NAME, VALUE FROM SYSIBMADM.DB2_REG_VARIABLES
WHERE NAME = 'DB2_OPTER_PARAMS'"启用部分生产力建模,可以在注册变量中追加该开关。假设当前变量为空,执行类似下面的命令即可:
db2set DB2_OPTER_PARAMS="OPT_ENABLE_PARTIAL_PRODUCTIVITY=ON" db2stop force db2start # 如果变量中已有其他开关,用分号连接,例如: # db2set DB2_OPTER_PARAMS="OPT_ENABLE_PARTIAL_PRODUCTIVITY=ON;OPT_MAX_JOINS_ENUM=10"
设置DB2_OPTER_PARAMS后必须重启实例才能生效,这一点务必留意,建议安排在维护窗口操作。如果只想对个别查询生效而不影响整个实例,可以考虑在语句级别使用优化指南,把相应的优化行为绑定到指定的语句上,这样风险更可控:
-- 语句级优化指南示例,仅对目标查询启用
SELECT /* <OPTGUIDELINE>
<QBTAG>Q1</QBTAG>
<OPTPARAM NAME='OPT_ENABLE_PARTIAL_PRODUCTIVITY' VALUE='Y'/>
</OPTGUIDELINE> */
o.order_id, c.cust_name, SUM(l.amount)
FROM orders o, customers c, order_lines l
WHERE o.cust_id = c.cust_id
AND o.order_id = l.order_id
AND l.status = 'PAID'
GROUP BY o.order_id, c.cust_name;验证参数是否生效,最直接的办法是对比启用前后的解释快照,观察连接顺序和估计基数是否发生变化。
实际场景验证与注意事项
以一个典型的三表连接为例:订单表orders、客户表customers和订单明细表order_lines,查询按已支付状态汇总金额。在统计信息完整的前提下,启用前优化器可能选择先做orders与customers的哈希连接,再与order_lines连接,因为这个顺序在独立选择率假设下成本最低。但实际数据中已支付订单只占少数明细行,启用部分生产力建模后,优化器会把l.status这个谓词的下推收益计入连接顺序评估,倾向于先过滤order_lines,中间结果明显变小,整体执行时间随之下降。可以用EXPLAIN对比验证:
SET CURRENT EXPLAIN MODE EXPLAIN; -- 执行目标查询 SET CURRENT EXPLAIN MODE NO; db2exfmt -d SAMPLE -1 -o plan_after.txt
对比plan_before.txt与plan_after.txt中的Operator Details,重点看HSJOIN、NLJOIN的排列顺序以及每一步的Cardinality估计值。如果启用后估计基数更接近实际行数,说明该参数对你的数据分布产生了正向作用。
几个注意事项值得强调。第一,该参数发挥作用的前提是统计信息足够准确,如果表从未收集过分布统计,再好的估算模型也无从发挥,建议先执行RUNSTATS ON TABLE ... WITH DISTRIBUTION AND DETAILED INDEXES ALL。第二,对于连接表数量极少(比如两表连接)的简单查询,启用与否基本没有区别,不必为了这个参数专门调整。第三,如果实例中存在大量超复杂的即席查询,启用后编译时间上升可能影响整体吞吐,此时语句级指南比实例级设置更合适。第四,升级版本后要重新确认该开关的默认值和行为,内部参数在不同大版本之间可能发生变化,升级前的验证不可省略。
总结来说,opt_enable_partial_productivity是一个面向复杂连接估算的优化器开关,配合完整的统计信息和合理的验证流程,能够改善多表连接查询的计划质量。调优的正确姿势永远是:先在测试环境用EXPLAIN量化差异,再决定是否推广到生产实例。