Home

【DB2 数据库】11 模拟故障排查系列:锁等待与死锁(SQL0911N)实验

一、这篇文章要解决什么问题

前面几篇我已经把表空间、日志、实例启动这几条故障线跑通了。

这次我继续往并发问题走,专门验证两类很常见的现场:

  • 锁等待(一个会话被另一个会话卡住)
  • 死锁(两个会话互相等待,DB2 自动回滚其中一个)

这篇实验我想回答 4 个问题:

  • Lock-wait 在 DB2 里到底怎么观察
  • db2 list applications show detail 里哪些字段最关键
  • SQL0911NReason code "2" 到底代表什么
  • 死锁发生后,数据是否能恢复到一致状态

二、实验环境

  • 操作系统:SUSE Linux Enterprise Server 12 SP5
  • DB2 版本:DB2 v9.7.0.6 Fix Pack 6
  • 实例:db2inst1
  • 数据库:TESTDB
  • 实验表:TLOCK11
  • 表空间:USERSPACE1

三、实验前准备

我先建好实验表并插入两行基线数据,用来做交叉加锁。

# 删除旧表(若不存在会报 SQL0204N,属于正常)
db2 "drop table TLOCK11"

# 新建实验表
db2 "create table TLOCK11(id int not null primary key, c1 varchar(20)) in USERSPACE1"

# 初始化两行数据
db2 "insert into TLOCK11 values (1,'ROW-1')"
db2 "insert into TLOCK11 values (2,'ROW-2')"

# 验证初始化结果
db2 "select * from TLOCK11 order by id"

我当时拿到的初始化结果是:

ID          C1
----------- --------------------
          1 ROW-1
          2 ROW-2

  2 record(s) selected.

四、第一阶段:先做锁等待

这一阶段我开了三个终端:

  • 终端 A:先更新 id=1 并不提交,持有行锁
  • 终端 B:再更新同一行 id=1,进入等待
  • 终端 C:做监控观察

1. 终端 C 看到的应用状态

我在等待窗口里执行:

db2 list applications show detail

关键行是:

DB2INST1  db2bp  47  *LOCAL.db2inst1.260511012524  ...  Lock-wait  ...  TESTDB

这说明确实存在会话在等锁。

2. 锁快照证据

我继续执行:

db2 get snapshot for locks on TESTDB

快照里关键字段是:

Agents currently waiting on locks          = 1

并且可以看到一个会话持有 TLOCK11Row X 锁,另一个会话处于 Lock-wait

3. 释放后现象

我让持锁会话 rollback 后,等待会话返回成功。

这个结果符合锁等待的标准行为:不是报错,而是等到锁释放后继续执行。

五、第二阶段:构造死锁并触发 SQL0911N

第二阶段我用“交叉更新”的方式造死锁:

  • 会话 A:先改 id=1
  • 会话 B:先改 id=2
  • 会话 A:再改 id=2(等待 B)
  • 会话 B:再改 id=1(形成环路)

1. 实际报错

在会话 B 的第二次更新,我拿到了目标报错:

SQL0911N  The current transaction has been rolled back because of a deadlock
or timeout.  Reason code "2".  SQLSTATE=40001

这条报错就是这篇实验的核心证据。

Reason code "2" 对应死锁裁决,DB2 选中了一个事务作为 victim 并自动回滚。

2. 死锁后的监控快照

我在终端 C 复查:

db2 list applications show detail
db2 get snapshot for locks on TESTDB

死锁后快照里有两个关键点:

Applications currently connected           = 2
Agents currently waiting on locks          = 0

这说明 DB2 已经完成死锁裁决,等待环路被打破了。

另外我还能看到:

  • handle 59(db2bp)持有 TLOCK11 的两个 Row X
  • handle 74(db2bp)Locks held = 0

这和死锁自动回滚其中一个事务的行为是一致的。

六、实验收尾与一致性验证

死锁实验结束后,我让两个会话都执行了 rollbackterminate,最后再查表:

db2 connect to TESTDB
db2 "select * from TLOCK11 order by id"
db2 terminate

最终结果是:

ID          C1
----------- --------------------
          1 ROW-1
          2 ROW-2

  2 record(s) selected.

这说明两件事:

  • 死锁中的 victim 事务已被 DB2 回滚
  • 我手工收尾后,数据回到了实验前的基线状态

七、这次实验我记住的结论

第一,锁等待和死锁不是一回事。

锁等待通常是“等一会儿就过”,死锁是“谁都过不去,必须裁决一个回滚”。

第二,db2 list applications show detailStatus 很关键。

看到 Lock-wait 基本就能确认现场正在等锁。

第三,SQL0911N + Reason code 2 是死锁的典型信号。

排障时看到这一组,就要优先往事务访问顺序、并发更新路径去查。

第四,做并发实验时,收尾一定要显式 rollback + terminate

否则下一个实验很容易被上一个会话的锁和事务状态污染。

Linux DB2 数据库