注册
达梦数据库的死锁和阻塞
技术分享/ 文章详情 /

达梦数据库的死锁和阻塞

何处惹尘埃 2026/08/07 119 0 0

问题13:达梦数据库的死锁和阻塞,结合例子说明

一、概念区分

阻塞(Blocking):一个事务持有某资源的锁,另一个事务请求同一资源时被阻塞,等待持有者释放锁。阻塞是暂时的,持有者提交或回滚后,等待者可以继续。

死锁(Deadlock):两个或多个事务互相持有对方需要的资源,形成循环等待,导致所有事务都无法继续。达梦会自动检测死锁,并选择回滚其中一个事务(通常是代价较小的事务)。

二、实操环境

  • 实例:TESTDB(端口 5239)
  • 测试表:TEST.T_DEADLOCK
  • 需要两个独立的 disql 会话(会话A、会话B)

三、实操准备:创建测试表

会话A执行

CREATE TABLE TEST.T_DEADLOCK ( ID INT PRIMARY KEY, NAME VARCHAR(50), VAL INT ); INSERT INTO TEST.T_DEADLOCK VALUES (1, 'Row1', 100); INSERT INTO TEST.T_DEADLOCK VALUES (2, 'Row2', 200); COMMIT; SELECT * FROM TEST.T_DEADLOCK;

输出

行号  ID  NAME  VAL
----- --- ----- ---
1     1   Row1  100
2     2   Row2  200

四、模拟阻塞

第一步:会话A更新ID=1,不提交

-- 会话A执行 UPDATE TEST.T_DEADLOCK SET VAL = 150 WHERE ID = 1; -- 不提交

第二步:会话B尝试更新同一行,被阻塞

-- 会话B执行 UPDATE TEST.T_DEADLOCK SET VAL = 160 WHERE ID = 1; -- 会话B会卡住,等待会话A提交

第三步:在会话B等待期间,会话A查询锁信息

-- 会话A执行 SELECT * FROM V$LOCK WHERE BLOCKED = 1; SELECT * FROM V$TRXWAIT; SELECT SESS_ID, USER_NAME, STATE, CLNT_IP, TRX_ID FROM V$SESSIONS WHERE STATE = 'ACTIVE';

V$TRXWAIT 输出

行号  ID     WAIT_FOR_ID  WAIT_TIME  THRD_ID
----- ------ ------------ ---------- -------
1     96396  96393        8686       25239

各列含义:

  • ID:等待事务的ID(会话B,TRX_ID=96396
  • WAIT_FOR_ID:持有锁的事务ID(会话A,TRX_ID=96393
  • WAIT_TIME:等待时间(毫秒)

V$LOCK WHERE BLOCKED=1 输出

行号  TRX_ID    LTYPE  LMODE  BLOCKED  TABLE_ID  ROW_IDX
----- --------- ------ ------ -------- --------- --------
1     96396     TID    X      1         1074      1

各列含义:

  • BLOCKED=1:该锁被阻塞
  • LTYPE=TID:行锁(事务ID锁)
  • LMODE=X:排他锁

V$SESSIONS 输出

行号  SESS_ID              USER_NAME  STATE  CLNT_IP   TRX_ID
----- -------------------- ---------- ------ --------- ------
1     139766298248672      SYSDBA     ACTIVE ::1:45578 96393   -- 会话A(持有锁)
2     139765950845296      SYSDBA     ACTIVE ::1:45580 96396   -- 会话B(等待)

第四步:解除阻塞

-- 会话A执行 COMMIT;

会话B的更新自动完成。

五、模拟死锁

第一步:会话A更新ID=1,不提交

-- 会话A执行 UPDATE TEST.T_DEADLOCK SET VAL = 150 WHERE ID = 1; -- 不提交

第二步:会话B更新ID=2,不提交

-- 会话B执行 UPDATE TEST.T_DEADLOCK SET VAL = 250 WHERE ID = 2; -- 不提交

第三步:会话A尝试更新ID=2,被会话B阻塞

-- 会话A执行 UPDATE TEST.T_DEADLOCK SET VAL = 160 WHERE ID = 2; -- 会话A会卡住,等待会话B提交

第四步:会话B尝试更新ID=1,触发死锁

-- 会话B执行 UPDATE TEST.T_DEADLOCK SET VAL = 260 WHERE ID = 1;

输出

[-6403]:死锁.

达梦自动检测到死锁,回滚了会话B的事务。

第五步:查看死锁历史

SELECT * FROM V$DEADLOCK_HISTORY;

输出

行号  SEQNO  TRX_ID   SESS_ID              HAPPEN_TIME               DEADLOCK_CYCLE
----- ------ -------- -------------------- ------------------------- --------------
1     0      96402    139765950845296      2026-08-03 08:52:32.408249 self -> (96401, ...) -> self

各列含义:

  • TRX_ID=96402:被回滚的事务(会话B)
  • HAPPEN_TIME:死锁发生时间
  • DEADLOCK_CYCLE:循环等待关系

六、结果分析

6.1 两个会话查询结果不一致的原因

死锁触发后,会话B的事务被自动回滚,会话A的事务继续执行并提交。

会话A查询结果

行号  ID  NAME  VAL
----- --- ----- ---
1     1   Row1  150   -- 会话A的修改
2     2   Row2  160   -- 会话A的修改

会话B查询结果

行号  ID  NAME  VAL
----- --- ----- ---
1     1   Row1  100   -- 回滚前的原始值
2     2   Row2  200   -- 回滚前的原始值

原因:会话B的事务被回滚,其所有修改(VAL=250VAL=260)被撤销,因此查询到的是原始数据。会话A的事务成功提交,其修改(VAL=150VAL=160)永久生效。

6.2 阻塞与死锁对比总结

对比项 阻塞 死锁
定义 事务等待锁释放 循环等待,互相持有对方需要的锁
是否自动解决 需持有者提交/回滚 达梦自动回滚其中一个事务
系统报错 无(正常等待) [-6403]:死锁
相关视图 V$LOCK(BLOCKED=1)、V$TRXWAIT V$DEADLOCK_HISTORY
解决方式 提交或回滚持有锁的事务 系统自动回滚,无需人工干预

七、核心要点

  1. 阻塞是正常的等待现象,通过 V$LOCKV$TRXWAIT 可以定位阻塞关系。
  2. 死锁是异常现象,达梦会自动检测并回滚其中一个事务,被回滚的事务会报错 [-6403]:死锁
  3. 被回滚的事务所做的所有修改会被撤销,这就是会话A和会话B查询结果不一致的原因。
  4. 死锁历史记录在 V$DEADLOCK_HISTORY,可用于事后分析。

八、完整操作命令清单

-- 1. 创建测试表 CREATE TABLE TEST.T_DEADLOCK (ID INT PRIMARY KEY, NAME VARCHAR(50), VAL INT); INSERT INTO TEST.T_DEADLOCK VALUES (1, 'Row1', 100); INSERT INTO TEST.T_DEADLOCK VALUES (2, 'Row2', 200); COMMIT; -- 2. 查看锁阻塞信息(第三个会话) SELECT * FROM V$LOCK WHERE BLOCKED = 1; SELECT * FROM V$TRXWAIT; SELECT SESS_ID, USER_NAME, STATE, CLNT_IP, TRX_ID FROM V$SESSIONS WHERE STATE = 'ACTIVE'; -- 3. 查看死锁历史 SELECT * FROM V$DEADLOCK_HISTORY; -- 4. 清理测试表 DROP TABLE TEST.T_DEADLOCK;
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服