DB2 优化器生成执行计划后,会把访问路径、成本估算、谓词过滤等信息写入多张 EXPLAIN 表中。这些表结构虽然完整,但可读性并不理想,尤其是涉及多表连接、子查询或复杂谓词时,直接查询 EXPLAIN_STREAM、EXPLAIN_OPERATOR 等表很难快速还原优化器的真实决策。db2exfmt 正是 DB2 官方提供的执行计划格式化工具,它将这些原始数据重新组织为树形文本,让访问路径、累计成本、I/O 成本、结果集基数等指标一目了然。

理解 db2exfmt 的使用方法和输出内容,是进行 SQL 调优的基础。下面从生成执行计划数据开始,逐步展开工具参数、输出解读和优化定位。
为什么需要 db2exfmt 而不是直接查询 EXPLAIN 表
EXPLAIN 表是 DB2 存放优化器决策的底层存储,通常包含 EXPLAIN_INSTANCE、EXPLAIN_STATEMENT、EXPLAIN_OPERATOR、EXPLAIN_STREAM、EXPLAIN_OBJECT 等多张表。一条 SQL 执行计划可能分布在几十甚至上百行记录中,需要根据 operator_id、stream_id、object_id 等字段进行关联,才能拼出优化器选择的访问路径。这种工作不仅繁琐,而且容易因为关联条件遗漏或字段含义混淆导致误判。
db2exfmt 会读取这些 EXPLAIN 表,并按照执行计划的层次结构重新组织输出。例如一个嵌套循环连接会以缩进的树形方式展示,子节点和父节点之间的数据流动方向清晰可见。同时它会将成本、基数和谓词信息放在同一个操作符下方,避免来回切换查询。对于复杂 SQL,这种格式化输出可以显著降低分析成本。
此外,db2exfmt 还能输出优化器使用的参数、表和索引的统计信息快照、以及重写后的语句文本。这些附加信息在判断统计信息是否过期、优化器参数是否影响计划选择时非常有用。直接查询 EXPLAIN 表虽然也能获得原始数据,但缺少这种上下文整合能力。
使用 db2exfmt 生成格式化执行计划的步骤
在调用 db2exfmt 之前,需要先让 DB2 为 SQL 生成 explain 数据。常用的做法是将当前会话的 explain 模式设置为 explain,执行需要分析的 SQL,然后再恢复为 no。下面是一个完整示例:
db2 connect to sample db2 set current explain mode explain db2 "select e.empno, e.lastname, d.deptname from employee e, department d where e.workdept = d.deptno and e.salary > 50000" db2 set current explain mode no
执行上述命令后,DB2 不会真正返回结果集,而是将优化器生成的计划写入 EXPLAIN 表。接下来使用 db2exfmt 读取这些数据并格式化为文本文件:
db2exfmt -d sample -g TIC -w -1 -n % -s % -# 0 -o emp_plan.txt
参数说明如下:-d 指定数据库名;-g 控制显示哪些图形信息,常用值为 TIC,表示表和索引列;-w 设置输出宽度,-1 表示不限制换行;-n 和 -s 分别过滤节点名和模式名,% 表示全部;-# 指定每个操作符最大显示行数,0 表示全部;-o 指定输出文件。生成后可以直接打开 emp_plan.txt 查看完整执行计划。
如果需要分析已经存在的 explain 数据,也可以根据时间戳、语句文本或 explain 模式过滤。db2exfmt 还支持 -e 参数指定 explain 模式,-t 参数按语句文本模糊匹配。掌握这些参数后,可以更精准地提取目标 SQL 的计划,而不是每次输出全部内容。
执行计划输出中的关键信息解读
打开 db2exfmt 生成的文本文件,通常最先看到的是基本信息区域,包括数据库名称、解释时间、SQL 文本、优化器参数等。真正需要重点关注的是 Access Plan 部分,它以树形结构展示优化器选择的访问路径。一个典型的两表连接计划可能如下:
Access Plan:
-----------
Total Cost: 67.82
Query Degree: 1
Rows
RETURN
( 1)
|
5
NLJOIN
( 2)
/ \
5 10
TBSCAN FETCH
( 3) ( 4)
| / \
20 10 10
TABLE: IXSCAN TABLE:
EMPLOYEE ( 5) DEPARTMENT
|
INDEX:
XDEPT
上面的片段中,NLJOIN 表示嵌套循环连接,左侧 TBSCAN 表示全表扫描 employee 表,右侧 FETCH 配合 IXSCAN 表示先通过索引 XDEPT 扫描 department 表,再回表取数据。括号中的数字是操作符编号,旁边的数字是估算行数。从这个计划可以看出,如果 employee 表较大,全表扫描可能会成为瓶颈,此时应该考虑在 employee 的 workdept 和 salary 上建立合适索引。
每个操作符下方还会列出累计成本、I/O 成本、CPU 成本、结果集基数和谓词信息。累计成本是优化器估算的该操作符及其子树的总代价,I/O 成本反映磁盘读取开销,CPU 成本反映计算开销。比较不同操作符的成本变化,可以判断优化器在每一步花费了多少资源。谓词信息则显示过滤条件是在索引扫描阶段应用还是在表扫描阶段应用,这与索引选择性密切相关。
db2exfmt 还会输出优化器使用的统计信息,例如表的行数、列的不同值数量、索引的聚簇率等。如果这些统计信息与实际数据偏差较大,优化器可能选择错误的访问路径。例如统计信息显示某列只有 10 个不同值,优化器倾向于使用全表扫描;但实际数据已经有上万个不同值,这时索引扫描可能更高效。通过查看统计信息快照,可以快速判断是否需要执行 RUNSTATS 更新统计信息。
基于 db2exfmt 输出定位性能问题的常见思路
拿到格式化执行计划后,可以先从成本最高的操作符开始分析。通常累计成本最高的节点就是计划中的主要开销来源,例如大表的全表扫描、排序操作、哈希连接等。如果发现 TBSCAN 操作符出现在大表上,需要检查该表是否有合适的索引,或者谓词是否存在隐式类型转换导致索引失效。例如 where int_col = '123' 这种写法可能导致优化器放弃索引,因为字符常量需要转换为整数,转换可能影响索引使用。
接着观察连接顺序和连接方式。DB2 优化器会根据统计信息选择嵌套循环连接、哈希连接或合并连接。嵌套循环连接适合小表驱动大表,并通过索引访问内表;哈希连接适合两个大表等值连接,但需要额外的内存来构建哈希表。如果 db2exfmt 显示一个很大的表作为嵌套循环外表,而内表又走了全表扫描,那么连接策略可能不理想。此时可以尝试调整查询写法、增加索引,或者通过优化配置文件影响连接顺序。
再关注排序和临时表操作。SORT 操作符通常出现在 ORDER BY、GROUP BY、DISTINCT 或合并连接中。如果排序行数很大且无法借助索引避免排序,可以考虑为排序列建立索引,或者调整查询避免不必要的排序。db2exfmt 会显示排序是否需要溢出到磁盘,如果出现 SORT 后带 TEMP 字样,说明排序空间不足,可能需要增加排序堆内存或优化查询。
最后,将 db2exfmt 输出与 SQL 文本对照,检查是否存在谓词下推不充分、子查询未改写为连接、冗余的 DISTINCT 等情况。这些语义层面的问题不一定能在成本数字中直接体现,但结合计划树可以辅助判断。通过不断调整 SQL 或索引,再重新生成执行计划对比,就能逐步逼近最优方案。
熟练使用 db2exfmt 后,性能分析不再依赖猜测,而是有明确的量化依据。工具本身并不改变优化器行为,但它把隐藏在 EXPLAIN 表中的决策过程清晰地呈现出来,为数据库调优提供了可靠的数据支撑。