opt_enable_partial_intersect 是 DB2 LUW 优化器中的一个控制参数,它决定优化器能否为同时包含多个 IN 谓词、多个 OR 条件或组合谓词的 SQL 生成部分交集访问计划。实际业务中,一条查询的 WHERE 子句经常会对不同列分别给出过滤条件,比如两个列各有自己的 IN 列表,而这些列又分别建有索引。优化器传统的做法之一是分别扫描相关索引,得到多个 RID 集合,再对这些 RID 集合做完整交集运算,最后回表读取数据。如果其中某个 IN 列表的匹配行数特别大,完整交集会产生巨大的排序和临时空间开销。

开启 opt_enable_partial_intersect 之后,优化器可以采用部分交集策略:先选择选择性更好的索引作为驱动,然后在扫描该索引的 RID 过程中,利用表中其他谓词对 RID 做逐步过滤。这样不需要把所有 RID 集合完整物化后再求交集,而是以流式方式减少候选记录。这样做通常能降低排序内存消耗,减少回表次数,尤其适合多个高选择性条件并存的场景。理解这一机制,有助于 DBA 判断在哪些情况下应该保持该参数开启,在哪些情况下需要手动调整。
部分交集优化的工作机制
在 DB2 的索引访问中,RID 是行标识符,可以唯一指向数据页中的一行记录。当查询包含多个可以使用索引的条件时,优化器会估算每个条件单独匹配的 RID 规模,并决定是否采用索引 ANDing 或 ORing 访问路径。索引 ANDing 的典型做法是先扫描两个或多个索引,得到各自的 RID 列表,再对这些列表做排序和归并交集。如果某个索引的选择性很低,扫描出大量 RID,排序归并的成本会非常高。
部分交集的核心思路是不要求所有 RID 集合都在同一时间完整生成。优化器可以选择一个启动成本低且选择性高的索引作为驱动,然后每获取一个 RID,就立即用剩余条件中的其他谓词做验证,或与其他索引的 RID 集合做探测。例如某查询有 col1 IN (1,2,3) 和 col2 IN (4,5,6,7,8),col1 索引选择性高而 col2 索引选择性低,部分交集会优先扫描 col1 索引得到较小的 RID 集合,再仅对这些 RID 检查 col2 条件是否成立。这样避免了先扫描 col2 索引产生大量 RID 并排序的开销。
opt_enable_partial_intersect 参数并不直接改变 SQL 语义,它只影响优化器是否考虑这类部分交集访问路径。参数默认在大多数 DB2 LUW 版本中为开启状态,但如果实例之前做过特殊调优,或者升级时注册表变量没有被正确保留,就可能处于关闭状态。关闭后优化器只能选择完整索引交集,甚至因为成本估算过高而放弃索引路径,直接走全表扫描,这对某些在线事务查询会造成显著性能下降。
启用参数的步骤与验证
该参数属于 DB2 实例级注册表变量,变量名为 DB2_OPT_ENABLE_PARTIAL_INTERSECT。要启用它,需要在实例用户下执行 db2set 命令。设置完成后必须重启实例,新的优化器行为才会生效。下面给出典型的启用命令:
db2set DB2_OPT_ENABLE_PARTIAL_INTERSECT=YES db2stop force db2start
执行 db2set 后,可以用 db2set -all 查看当前实例所有已设置的注册表变量,确认 DB2_OPT_ENABLE_PARTIAL_INTERSECT 是否已经出现在列表中。如果列表里没有这一项,说明实例使用的是产品默认值;如果想强制开启,就必须显式设置。某些环境下可能有多个实例,需要注意命令是在目标实例的拥有者账号下执行,而不是数据库用户或客户端用户。
验证配置是否真正生效,不能只看 db2set 输出,因为实例可能没有重启。重启之后,可以用 db2pd -db 数据库名 -features 或 db2 get db cfg 中的相关输出确认优化器状态,但更直接的方法是抓取一条包含多个 IN 谓词的 SQL 的执行计划,观察是否出现部分交集访问操作。执行计划中如果看到 ITQ 或 RID 相关操作符发生明显变化,通常说明参数已经作用于优化器。
执行计划对比实例
假设有一张订单表 orders,字段包括 customer_id、product_id、order_date 等,分别在 customer_id 和 product_id 上建有索引。某业务查询需要同时筛选一组客户和一组产品:
SELECT order_id, order_date FROM orders WHERE customer_id IN (100, 101, 102, 103, 104) AND product_id IN (200, 201, 202, 203, 204, 205, 206, 207);
在 opt_enable_partial_intersect 关闭时,优化器可能评估 customer_id 索引和 product_id 索引都要扫描,然后对两个 RID 集合排序求交集。如果 product_id 列的数据分布比较集中,product_id IN 列表会扫描出大量 RID,此时排序和归并成本很高。执行计划会显示两个索引扫描和一个 RID 排序操作,总成本可能超过全表扫描,因此优化器有时会直接选择全表扫描避免排序开销。
开启参数后,优化器会对比两个索引的选择性,选择 customer_id 索引作为驱动,因为客户 ID 列表匹配的行数较少。在扫描 customer_id 索引时,直接对命中的每一行再评估 product_id IN 条件,无需先扫描 product_id 索引。执行计划中会出现 IXSCAN 后紧跟 FETCH,并在 FETCH 阶段携带 product_id 过滤条件。这样总的 RID 数量大幅减少,回表次数和排序成本同步降低。实际测试中,这类改造常能把秒级查询压缩到几十毫秒。
要查看执行计划,可以使用 db2 explain plan for 语句,再结合 db2exfmt 输出访问路径。对比启用前后的计划,重点观察是否有多个索引扫描合并、RID 排序是否消失、以及 FETCH 阶段是否包含剩余谓词。执行计划中的成本估算值虽然不能完全代表真实耗时,但结合 db2batch 跑出的实际响应时间和缓冲池命中率,能够更准确地判断参数是否带来了收益。
适用场景与禁用条件
部分交集优化最适合那些同时存在多个可索引谓词,且其中一个或几个索引选择性较高的查询。例如按客户和产品同时过滤的订单检索、按日期范围和状态过滤的工单查询等。开启该参数后,优化器能够根据统计信息灵活选择驱动索引,减少 RID 集合体积。对于数据量达到千万级且索引分布正常的表,这种访问路径的变化往往比全表扫描或完整索引交集更加高效。
然而并非所有场景都适合开启。如果多个索引的选择性都比较差,或者数据存在严重倾斜,例如 product_id 列表恰好覆盖了 80% 的产品,那么部分交集扫描驱动索引后仍然要评估大量行,性能可能不如直接扫描选择性较低的索引后再求交集。此外,如果表和索引的统计信息过期,优化器可能错误估计 RID 规模,导致部分交集反而选择不适合的驱动索引。此时应先执行 runstats 更新统计信息,再观察执行计划变化,而不是盲目依赖参数开关。
生产环境调整该参数前,建议在测试环境中复现典型 SQL,使用相同的数据分布和索引结构进行对比。通过 db2batch 或应用侧压测记录响应时间、CPU 和排序内存使用情况,确认开启后没有引入新的性能瓶颈。如果发现某些 SQL 出现计划回归,可以在语句级使用优化器提示或优化轮廓固定原有计划,同时保持参数开启,这样能兼顾整体优化和局部稳定。

