
简介这份文档面向DB2数据库运维DBA与性能调优学习者取材于某银行DB2系统的一次真实故障SQL执行时间变长、活动会话数异常攀升常规CPU、内存、I/O与锁等待排查均无收获最终锁定系统临时表空间TEMPSPACE1膨胀至10GB这一根源。资源包仅含1个doc文件约607KB完整记录了从现象确认、指标监控到根因定位的全过程并给出LATCH分析方法、STACK收集技巧以及用db2trc暂停实例抓取诊断信息等实操手段同时附有SQL优化与参数调整建议。已有5903人学习下载适合希望积累真实生产案例排错思路、提升DB2性能诊断能力的中高级读者参考。1. 一次“查不出原因”的变慢从 ACTIVE SESSION 异常说起业务侧反馈系统变慢多数人的第一反应是查 CPU、内存、I/O、锁等待、缓冲池命中率这套动作本身没错但这次它失效了。某银行 OLTP 系统平时 ACTIVE SESSION 长期在 10 以内某天起频繁冲到 50 以上db2top 里活动会话清一色 UOW ExecutingCPU 正常、内存正常、无锁等待、缓冲池命中率正常、I/O 时间也正常。常规指标全部“健康”SQL 却实实在在慢了 3 到 6 秒。这类问题最考验 DBA 的地方在于监控大盘没红但用户已经在骂。这篇方案把整个排查链路拆开——从 db2top 定位活动会话到 MON_GET_PKG_CACHE_STMT 拆解等待时间再到 db2pd 抓 LATCH 与 STACK、analyzestack 汇总、db2trc 做验证实验最后落到 TEMPSPACE1 暴涨 10G 的根因与 deactivate/activate 的处置。适合正在做 DB2 性能诊断毕设、课设或需要交一份数据库运维方案的人直接照着走。2. 问题定位链路设计从监控大盘到单条 SQL 的四层收敛排查性能问题最怕“东一榔头西一棒子”今天看锁、明天看排序最后什么都没抓住。我一般会把定位过程设计成四层收敛系统层 → 会话层 → 语句层 → 内部机制层每一层都有明确的“通过条件”和“退出条件”不满足就往下走满足了就停手。这套分层不是为了好看而是为了在时间压力下不重复劳动。2.1 第一层系统层快速排除先证明“不是资源问题”系统层要回答的问题只有一个CPU、内存、I/O、锁、缓冲池这五项里有没有一项明显异常。做法是 db2top 挂上去看实时指标同时用db2 LIST APPLICATIONS SHOW DETAIL统计活动会话状态分布。这一步的价值在于排除法——如果五项都正常就可以把“扩容”“加内存”“换存储”这类建议先压下去避免在错误方向上花钱。# 连接数据库并进入 db2top 交互界面 db2top -d dbname # 交互指令说明 # d - 查看 ACTIVE SESSION 数量与整体指标 # l - 进入 SESSIONS 页面看每个会话的状态 # D - 查看 PACKAGE CACHE 中的动态 SQL参数上要注意db2top 默认 2 秒刷新一次观察 ACTIVE SESSION 不能只看一眼至少连续看 5 分钟因为这类问题往往是“间歇性冲高”。会话状态里UOW Executing 表示正在执行语句UOW Waiting 表示事务中两条语句之间的空档后者不计入 ACTIVE SESSION。如果冲高的会话全是 UOW Executing基本可以判定是语句执行变慢而不是连接堆积。2.2 第二层会话层抓现行用 MON_CURRENT_SQL 锁定慢语句系统层确认“资源没问题、语句确实慢”之后就要抓现行。MON_CURRENT_SQL管理视图能同时看到动态 SQL 和静态 SQL按已运行时间倒序排一眼就能看出谁在拖后腿。SELECT COORD_MEMBER AS MEMBER, VARCHAR(SESSION_AUTH_ID, 10) AS USER, VARCHAR(CLIENT_APPLNAME, 10) AS APPNAME, ELAPSED_TIME_SEC AS ELAPSEDTIME, VARCHAR(ACTIVITY_STATE, 10) AS STATE, TOTAL_CPU_TIME, ROWS_READ, ROWS_RETURNED, QUERY_COST_ESTIMATE, DIRECT_READS, DIRECT_WRITES, VARCHAR(STMT_TEXT, 2000) AS STMT_TEXT FROM SYSIBMADM.MON_CURRENT_SQL ORDER BY ELAPSED_TIME_SEC DESC;这条语句的关键在ELAPSED_TIME_SEC排序和ACTIVITY_STATE字段。前者帮你把“已经跑了很久还没结束”的语句顶到最上面后者告诉你它卡在编译、执行还是锁等待。需要提醒的是抓取时语句往往还没执行完所以看到的 ELAPSED TIME 比完整执行时间短通常要连续抓多次才能拼出全貌。这次抓下来接近 30 条语句同时在跑和 ACTIVE SESSION 数量对得上说明问题不是个别语句而是有一批语句集体变慢。2.3 第三层语句层拆时间分布判断“慢在算还是慢在等”抓到慢语句只是第一步真正有价值的是知道时间花在哪。MON_GET_PKG_CACHE_STMT表函数能返回每条语句的等待时间、读时间、锁等待、日志等待、排序时间把它们换算成占总执行时间的百分比慢的“成分”就一目了然。SELECT MEMBER, NUM_EXECUTIONS, STMT_EXEC_TIME, DECIMAL((TOTAL_ACT_WAIT_TIME / DOUBLE(STMT_EXEC_TIME)) * 100, 5, 2) AS pct_wait, DECIMAL((POOL_READ_TIME / DOUBLE(STMT_EXEC_TIME)) * 100, 5, 2) AS pct_read, DECIMAL((LOCK_WAIT_TIME / DOUBLE(STMT_EXEC_TIME)) * 100, 5, 2) AS pct_lock, DECIMAL((LOG_DISK_WAIT_TIME / DOUBLE(STMT_EXEC_TIME)) * 100, 5, 2) AS pct_log, DECIMAL((TOTAL_SECTION_SORT_TIME / DOUBLE(STMT_EXEC_TIME)) * 100, 5, 2) AS pct_sort, CAST(FLOAT(STMT_EXEC_TIME) / FLOAT(NUM_EXECUTIONS) AS DECIMAL(10, 3)) AS AVG_EXEC_TIME, VARCHAR(STMT_TEXT, 500) AS STMT_TEXT FROM TABLE(MON_GET_PKG_CACHE_STMT(NULL, NULL, NULL, -2)) WHERE NUM_EXECUTIONS 1 AND STMT_EXEC_TIME 0;这里-2表示取所有成员上的语句NUM_EXECUTIONS 1过滤掉只跑过一次的语句避免平均值失真。结果里发现 5 条 with 开头的语句pct_wait超过 95%但pct_read、pct_lock、pct_log、pct_sort都很低——等待的不是 I/O、不是锁、不是日志、不是排序。这个“四不像”的等待分布就是关键线索顺着它加查TOTAL_EXTENDED_LATCH_WAIT_TIME才把矛头指向 LATCH。2.4 第四层内部机制层LATCH 与 STACK 联合取证到了 LATCH 这一层常规监控已经帮不上忙了。DB2 文档里对 LATCH 的介绍极少只能把它理解成“保护内部共享数据结构的轻量锁”。做法是先用db2pd -latches -rep 2持续观察看有没有某个 EDU 长时间持有 LATCH 而一堆 EDU 在等。这次观察到pool_table_latch被一个 EDU 持有、23 个 EDU 排队和 ACTIVE SESSION 冲高完全吻合。# 每 2 秒采样一次 LATCH 状态持续输出 db2pd -latches -rep 2 # 收集 STACK每 3 秒一次共 10 次输出到指定目录 mkdir -p /tmp/stack0531 db2pd -stack all dumpdir/tmp/stack0531 -rep 3 10 # 在结果中检索目标 LATCH grep pool_table_latch /tmp/stack0531/*.stack.txt-rep 3 10的含义是间隔 3 秒、共采 10 轮因为 LATCH 等待往往几秒就消失单次采样大概率扑空。STACK 文件里同时记录了每个 EDU 当时持有的 LATCH 和等待的 LATCHHolder 的调用栈能看到sqlbSnapshotTablespace → sqlbMonSMSGetContainerPages说明它在对 SMS 表空间做快照、读容器数据页Waiter 的调用栈能看到sqldTableCreateTemp → sqldInternalCreateTable → sqlbGetSysTempDatapool说明它在创建内部临时表、要用系统临时表空间。两条栈一对照因果链就出来了监控代理定期对表空间做快照快照在 SMS 表空间上耗时过长、占着 pool_table_latch把正在用临时表空间的查询全堵住了。3. 根因确认与处置方案TEMPSPACE1 从 10G 回到正常定位到 LATCH 只是找到了“堵点”还得回答“为什么快照会慢”。答案在表空间本身db2 LIST TABLESPACES SHOW DETAIL要跑好几秒查下来 TEMPSPACE1 竟然有 10G而正常情况下系统临时表空间应该很小。这一章讲怎么确认根因、怎么处置、怎么防止复发。3.1 用表空间详情和文件列表坐实“异常膨胀”确认 TEMPSPACE1 状态重点看总页数、页大小、容器数再配合操作系统层面看容器目录里的文件。-- 查看所有表空间详情重点看 TEMPSPACE1 LIST TABLESPACES SHOW DETAIL;# 进入 TEMPSPACE1 的容器目录观察 TDA 文件 ls -al /db2data/instance/NODE0000/dbname/T0000001/结果里Total pages 354661、Page size 32768乘起来约 10G和监控历史曲线对得上——某个时间点从 0 直接涨到 10G。容器目录里能看到大量SQL0xxxx.MEMBER0000.TDA文件每个 1MB 左右。TDA 是临时数据文件正常用完应该被回收这里却大量遗留说明曾经有一波极端并发把临时表空间撑爆后没有正常释放。翻监控历史果然在那个时间点 ACTIVE SESSION 瞬间冲到 800 多大量 SQL 同时用临时表空间语句都执行成功了但留下了一地 TDA 文件成了后来踩到的雷。3.2 处置动作deactivate/activate 而不是重启实例确认根因后处置反而简单但有个关键细节容易翻车必须 deactivate/activate 数据库只重启实例释放不掉这些临时表空间。# 先断开所有应用连接再执行 db2 deactivate db dbname db2 activate db dbname # 处置后复查表空间 db2 LIST TABLESPACES SHOW DETAILdeactivate 会把数据库从活动状态彻底拉下来释放所有临时段和 TDA 文件activate 再拉起来。执行前要安排维护窗口、确认没有长事务在跑执行后立刻复查 TEMPSPACE1 的 Total pages 是否回到正常量级并持续监控一段时间确认 ACTIVE SESSION 回落到个位数。这次处置后LIST TABLESPACES SHOW DETAIL秒回性能问题完全消失。3.3 防复发把临时表空间纳入日常巡检处置完不等于结束这类问题的复发成本很高。我一般会在巡检脚本里加三条硬指标TEMPSPACE1 使用页数超过阈值告警、容器目录 TDA 文件数量异常告警、ACTIVE SESSION 突增告警。同时把“监控代理对表空间的快照频率”纳入评估——快照本身是必要的但在 SMS 表空间上高频快照会放大 LATCH 竞争频率和时段要结合业务低峰来定。另外应用侧尽量用变量绑定代替拼 SQL减少硬解析带来的临时对象创建这条对 OLTP 系统是长期收益。4. 避坑与排查这类问题最容易翻车的五个点4.1 只盯 CPU 内存 I/O忽略“等待型”慢查询现象是常规资源指标全绿但用户持续报慢。原因是慢在 LATCH 这类内部等待上不体现在 CPU 和 I/O 上。解决办法是把MON_GET_PKG_CACHE_STMT的等待时间占比作为固定检查项看到“等待高但读、锁、日志、排序都低”的组合直接往 LATCH 方向查。4.2 单次采样 LATCH 扑空就放弃现象是db2pd -latches跑一次没发现异常就判定“没有 LATCH 问题”。原因是 LATCH 等待往往只持续几秒单次采样命中率很低。解决办法是用-rep参数持续采样至少覆盖一个业务高峰周期同时配合 STACK 收集两者时间点对齐才能拿到 Holder 和 Waiter 的完整证据。4.3 生产环境无节制收集 STACK现象是收集 STACK 期间数据库更慢了。原因是 STACK 收集会挂起 EDU、消耗资源繁忙系统上影响明显。解决办法是控制采集轮次和间隔选业务低峰执行采集前评估影响面采集后及时清理 dump 文件。这条是血泪经验别在高峰期图省事。4.4 以为重启实例就能释放临时表空间现象是重启实例后 TEMPSPACE1 还是很大。原因是 TDA 文件的释放依赖数据库 deactivate实例重启不触发这个动作。解决办法是走db2 deactivate dbdb2 activate db并在维护窗口内执行、执行后复查页数。4.5 看到慢 SQL 就急着加索引现象是加了一堆索引慢查询没改善。原因是这次慢的根因不在访问路径而在 LATCH 竞争加索引解决不了等待。解决办法是先做时间分布分析确认慢在“算”还是“等”再决定优化方向。方向错了努力全白费。5. 验收与答辩用 db2trc 做一次可复现的验证实验方案交上去评审最常问的一句是“你怎么证明 pool_table_latch 就是元凶而不是巧合”光靠现象吻合不够得有一个可复现的实验把因果链钉死。我一般会用db2trc做一次受控实验让 DB2 在特定函数入口处暂停人为制造“快照占着 LATCH”的场景然后观察其他会话是否被堵住。# 终端一连接数据库 db2 CONNECT TO dbname # 终端一开启 trace在 sqlbSMSGetContainerPages 入口处挂起 db2trc on -crash DB2.SQLB.sqlbSMSGetContainerPages.entry -suspend # 终端二此时发起一条会用到临时表空间的查询观察其是否被阻塞 db2 SELECT ... FROM ... ORDER BY ... # 终端一恢复执行关闭 trace db2trc off-suspend的作用是在命中指定函数入口时把进程挂起相当于人为把“快照读容器页”这一步拉长复现出 LATCH 被长期持有的状态。如果此时终端二的查询被卡住而恢复执行后立刻返回因果链就闭环了。这个实验的价值在于它把“事后推断”变成了“事前可演示”答辩时现场跑一遍比讲十页 PPT 都有说服力。验收清单可以按这个顺序过一遍系统层五项指标是否都有记录、慢语句是否有 MON_CURRENT_SQL 抓取结果、等待时间分布是否有量化数据、LATCH 是否有 Holder/Waiter 对照、STACK 是否定位到具体函数、TEMPSPACE1 处置前后页数是否有对比、处置后 ACTIVE SESSION 是否回落到基线。每一项都对应一个可展示的证据文件或截图评审问哪一项都能翻出来。从那以后我每次处理“指标全绿但就是慢”的问题都强制先跑一遍等待时间分布再决定往哪个方向深挖绝不凭直觉加索引。希望这套链路能帮你在自己的项目里少走几个弯路。本文还有配套的精品资源点击获取