问题13:达梦数据库的死锁和阻塞,结合例子说明
阻塞(Blocking):一个事务持有某资源的锁,另一个事务请求同一资源时被阻塞,等待持有者释放锁。阻塞是暂时的,持有者提交或回滚后,等待者可以继续。
死锁(Deadlock):两个或多个事务互相持有对方需要的资源,形成循环等待,导致所有事务都无法继续。达梦会自动检测死锁,并选择回滚其中一个事务(通常是代价较小的事务)。
TEST.T_DEADLOCKdisql 会话(会话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:循环等待关系死锁触发后,会话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=250和VAL=260)被撤销,因此查询到的是原始数据。会话A的事务成功提交,其修改(VAL=150和VAL=160)永久生效。
| 对比项 | 阻塞 | 死锁 |
|---|---|---|
| 定义 | 事务等待锁释放 | 循环等待,互相持有对方需要的锁 |
| 是否自动解决 | 需持有者提交/回滚 | 达梦自动回滚其中一个事务 |
| 系统报错 | 无(正常等待) | [-6403]:死锁 |
| 相关视图 | V$LOCK(BLOCKED=1)、V$TRXWAIT |
V$DEADLOCK_HISTORY |
| 解决方式 | 提交或回滚持有锁的事务 | 系统自动回滚,无需人工干预 |
V$LOCK 和 V$TRXWAIT 可以定位阻塞关系。[-6403]:死锁。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;
文章
阅读量
获赞
