注册
DM8数据库阻塞与死锁分析处理
专栏/技术分享/ 文章详情 /

DM8数据库阻塞与死锁分析处理

BigQ 2026/08/28 202 0 0
摘要

一 数据库锁基础知识

1.1 数据库锁基本原理

DM8数据库采用多版本并发控制(MVCC)机制,兼顾并发性能与数据一致性,同时通过锁机制管控事务资源竞争。数据库事务执行DML(INSERT/UPDATE/DELETE)、SELECT … FOR UPDATE 语句时,会自动对数据行、表资源加锁,事务未提交/回滚前,锁资源不会释放。

在 DM8 的封锁机制中,核心由锁粒度(锁定对象)和锁模式(访问权限)两大相互独立又协同联动的子体系共同构成。两大体系各司其职、互为补充,共同规范了多事务并发场景下的数据锁定规则,有效规避脏读、幻读、数据覆盖等并发问题,保障数据库高效稳定运行。

1.2 数据库锁模式

锁模式用于控制多事务对同一锁定对象的并发访问规则,是判断阻塞、锁冲突的核心依据。DM8 标准锁模式共 4 种:意向共享锁(IS)、意向排他锁(IX)、共享锁(S)、排他锁(X),各模式标准定义如下:

  1. 共享锁(S):行级或对象级只读锁。多个事务可同时对同一资源加 S 锁读取数据,但一旦资源被 S 锁占用,其他事务的 X 锁请求必须等待。兼容规则:兼容 IS、S;互斥 IX、X。
  2. 排他锁(X):行级或对象级独占锁。最高权限封锁,仅当前事务可读写对象,其他所有事务无法进行任何读写与加锁操作。兼容规则:与全部锁模式互斥。
  3. 意向共享锁(IS):对象意向共享锁。事务意图对表内数据行加共享读锁时,需先在表上加 IS 锁。兼容规则:兼容 IS、S;互斥 IX、X。作用为提前标记读意向,防止其他事务对整张表加排他锁,提升锁检测效率。
  4. 意向排他锁(IX):对象意向排他锁。事务意图对表内数据行进行修改、加行级排他锁时,需先在表上加 IX 锁。兼容规则:仅兼容 IX;与 IS、S、X 全部互斥。

不同事务之间锁兼容情况如下:

锁模式 IS S IX X
IS 兼容 兼容 兼容 冲突
S 兼容 兼容 冲突 冲突
IX 兼容 冲突 兼容 冲突
X 冲突 冲突 冲突 冲突

1.3 数据库锁粒度

锁粒度用于定义事务锁定的数据库对象范围,决定锁冲突范围与系统并发能力,粒度越精细,并发吞吐量越高。锁粒度分为:TID锁、对象锁、显示锁定表。

  1. TID 锁DM8 数据库无传统数据库行锁概念,完全通过 TID 锁实现记录级并发管控。事务执行 INSERT/UPDATE/DELETE 数据修改操作时,不会申请、生成任何传统行锁,仅将当前唯一事务ID(TID)写入数据行头部隐式字段,以此标记记录归属事务。依靠 TID 唯一性实现记录级互斥访问,替代传统行锁的所有功能,避免了大量行锁对系统资源的消耗。
  2. 对象锁:DM8 对象锁统一通过对象ID实现封锁,同时包含表对象锁、数据字典锁两部分,既管控表数据读写并发,也保护数据表、视图等对象的元数据字典一致性。对象锁从封锁逻辑上分为四类标准封锁动作,适配不同事务并发场景,对应不同锁模式与权限控制:
    • 独占访问(EXCLUSIVE ACCESS)基于X排他锁实现。 权限最高、隔离性最强。禁止其他所有事务对该对象进行任何读写、访问、修改操作,完全独占对象资源,主要用于表结构修改、数据重置、全表维护等DDL及高危运维操作。
    • 独占修改(EXCLUSIVE MODIFY)基于S + IX 方式实现。 仅允许当前事务修改对象,禁止其他事务修改对象,但允许其他事务共享查询访问对象,兼顾数据修改安全性与读并发。
    • 共享修改(SHARE MODIFY)基于IX意向排他锁实现。允许其他事务共享访问对象、也允许其他事务并行修改对象,仅禁止其他事务对表执行独占类操作。
    • 共享访问(SHARE ACCESS)基于IS意向共享锁实现。允许其他事务共享修改对象、同时允许其他事务共享访问对象,支持多事务并发读取、并发修改表内数据,仅限制全局独占类封锁操作,兼顾数据访问一致性与高并发读写能力。
  3. 显示锁定表:DM8 支持手动通过 LOCK TABLE 语句主动对数据表施加表级锁定,属于人工干预的显式锁粒度,多用于数据批量维护、离线报表统计、数据迁移等需要人工管控表并发的场景。手动锁定可通过 lock_mode 参数指定官方四种锁定模式:INTENT SHARE(意向共享)、INTENT EXCLUSIVE(意向排他)、SHARE(共享)、EXCLUSIVE(排他),可根据业务场景精准控制表级读写并发权限,有效规避并发数据错乱、DDL与DML冲突问题。

二 数据库常见锁等待

DM8 事务并发冲突主要分为普通锁等待(阻塞)与死锁两大类。其中锁等待(阻塞)可根据触发场景、锁粒度、业务表现细分为多种类型,包含 TID 记录锁等待、主键/唯一约束冲突等待、表级对象锁等待、数据字典锁等待等,各类场景特征、诱因、解决方案均不相同。

2.1 数据库阻塞

阻塞是单向锁等待场景:事务A持有某数据资源的锁且未提交,事务B需要竞争该资源的锁,无法获取资源则进入阻塞等待状态,持续挂起直至事务A释放锁(提交/回滚)。

2.1.1 阻塞核心特征

阻塞是DM8数据库最常见的并发异常,由事务间锁模式不兼容引发单向资源等待,具备固定的故障特征与运行机制。

具体核心特点如下:

  • 单向等待,无资源循环依赖关系;
  • 数据库无法自动解除,会永久持续等待;
  • 长期阻塞会导致业务超时、数据库连接堆积、CPU及事务并发下降,严重引发业务雪崩;
  • 核心根源:长事务未及时提交、锁粒度扩大、约束冲突、DDL与DML并发冲突等;

2.1.2 常见阻塞类型

  1. TID 锁等待
    • 触发场景:两个事务同时 UPDATE / DELETE / INSERT 同一行,且前序事务未提交。
    • 数据库表现:V$LOCK 中 LTYPE=TID、LMODE=X、BLOCKED=1;等待事件 trxid lock wait 。

示例:

##1. 会话 1 更新员工表 ID 为1004的工资到10000,该事务不提交 SQL> UPDATE DMHR.EMPLOYEE SET SALARY = 10000 where EMPLOYEE_ID=1004; 影响行数 1 SQL> <---未执行提交 ##2. 会话 2 更新员工表 ID 为1004的部门ID为103 SQL> UPDATE DMHR.EMPLOYEE SET DEPARTMENT_ID = 104 where EMPLOYEE_ID=1004; <---长时间处于执行状态,不访问状态 ##3. 查询对应的锁状态 SQL> SELECT L.BLOCKED,L.LTYPE,L.LMODE,S.SQL_TEXT,S.STATE,S.TRX_ID FROM V$SESSIONS S JOIN V$LOCK L ON S.TRX_ID = L.TRX_ID WHERE L.LTYPE='TID'; 行号 BLOCKED LTYPE LMODE SQL_TEXT STATE TRX_ID ---------- ----------- ----- ----- -------------------------------------------------------------------- ------ ------- 1 1 TID X UPDATE DMHR.EMPLOYEE SET DEPARTMENT_ID = 104 where EMPLOYEE_ID=1004; ACTIVE 262754 2 0 TID X UPDATE DMHR.EMPLOYEE SET SALARY = 10000 where EMPLOYEE_ID=1004; IDLE 262753
  1. 主键/唯一约束冲突等待
    • 触发场景:多事务向含 PRIMARY KEY / UNIQUE 表插入相同键值,先执行的会话未提交,后执行的会话处于被阻塞状态。
    • 数据库表现:后执行会话处于挂起状态,根据后续处理结果,分为两种情况:
      (1)先执行的会话进行提交,后执行会话在提交瞬间报唯一约束冲突;
      (2)先执行的会话进行回滚,后执行会话执行成功;
      示例:
##1. 会话 1 向EMP表插入 ID 为8000的数据 SQL> insert into APP_USER.EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(8000, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30); 影响行数 1 SQL> <---未执行提交 ##2. 会话 2 向EMP表插入 ID 为8000的数据 SQL> insert into APP_USER.EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(8000, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30); <---长时间处于执行状态,不返回执行结果 行号 BLOCKED LTYPE LMODE SQL_TEXT STATE TRX_ID ---------- ----------- ----- ----- ----------------------------------------------------------------------------------------------------------------------------------------------------------- ------ -------------------- 1 1 TID X insert into APP_USER.EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(8000, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30); ACTIVE 262924 2 0 TID X insert into APP_USER.EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(8000, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30); IDLE 262919
  1. SELECT FOR UPDATE 引发的阻塞
    • 触发场景:执行SELECT ... FOR UPDATE 语句引发锁冲突。
    • 数据库表现: 该操作阻塞 UPDATE、DELETE、DDL语句,不阻塞INSERT 、SELECT 语句。

示例:

##1. 会话 1 执行 for update语句 SQL> SELECT * FROM APP_USER.EMP FOR UPDATE; ##2. 会话 2 执行 insert语句,可以执行insert并进行提交 SQL> insert into APP_USER.EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(8001, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30); 影响行数 1 SQL> commit; 操作已执行 ##2. 会话 3 执行 delete 语句,该会话被阻塞 SQL> delete from APP_USER.EMP where EMPNO=7900; <---长时间处于执行状态,不返回执行结果
  1. DML导致DDL锁超时
    • 触发场景:表上有未提交 DML,另一会话执行 ALTER TABLE / CREATE INDEX / DROP COLUMN 等 DDL。
    • 数据库表现:DDL 申请对象级 X 锁,受 DDL_WAIT_TIME(默认 10s)控制,超时报锁超时,语句自动终止。

示例:

##1. 会话 1 对 EMP 执行 insert 语句 SQL> insert into APP_USER.EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values(8002, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30); 影响行数 1 SQL> <--不进行提交 ##2. 会话 1 对 EMP 表创建索引 SQL> create index APP_USER.IDX_ENAME on APP_USER.EMP(ENAME) ; <--长时间执行,不返回结果,超过 DDL_WAIT_TIME 时间后,报锁超时后,自动终止 执行失败(语句1) -6407: 锁超时
  1. 数据字典锁等待
    • 触发场景:对表DDL执行未完成,阻塞后续所有表访问事务。
    • 数据库表现:表结构变更期间,所有DML、查询语句全部阻塞,DDL执行卡顿、超时,业务读写完全不可用。

示例:

##1. 会话 1 对 BIGTAB 添加 C1 列并增加默认值 SQL> ALTER TABLE APP_USER.BIGTAB ADD COLUMN c1 INT DEFAULT 0; ##2. 会话 2 对 BIGTAB 更新操作 SQL> SQL> UPDATE APP_USER.BIGTAB SET name='x' WHERE id=1; <--长时间执行,不返回结果,等待 DDL 语句执行完成释放锁后,才执行成功 影响行数 3

2.1.3 阻塞解决方案

针对各类单向锁等待阻塞场景,优先从事务规范、运维规范等方向入手。日常需严格控制事务生命周期,杜绝长事务驻留;运维操作主动规避业务高峰,严格隔离DDL与DML并发执行场景。出现阻塞故障时,可通过系统视图快速定位阻塞源头,联动业务确认事务状态,督促完成事务提交或回滚并优化整改对应业务逻辑;紧急故障场景可强制终止阻塞会话,快速恢复业务正常。

2.2 数据库死锁

死锁是双向循环锁等待场景:两个或多个事务互相持有对方需要的锁资源,同时互相等待对方释放锁,形成闭环依赖,所有事务永久阻塞。

2.2.1 死锁核心特征

死锁是数据库极端的并发锁异常,由多事务资源循环竞争、锁依赖闭环引发,具备区别于普通阻塞的独有故障特征与数据库处理机制。

具体核心特点如下:

  • 双向/多向循环等待,资源依赖闭环;
  • DM8内置死锁检测线程,可自动识别死锁并主动终止其中一个事务,释放资源打破闭环;
  • 报错提示:[-6403]: 死锁,业务侧可捕获该异常;
  • 根源:业务SQL执行顺序混乱、交叉更新资源、事务逻辑不合理;

2.2.2 死锁解决方案

死锁等待,数据库层面只能被动处理,无法根治,必须依赖业务优化。统一多资源、多表、多记录的更新执行顺序,彻底消除循环锁依赖;拆分复杂大事务,精简事务内执行逻辑,最大限度缩短锁持有时长;业务代码捕获 [-6403]:死锁 异常,增加自动重试机制提升容错性;事后通过死锁历史视图溯源问题SQL,迭代优化事务并发逻辑。

2.3 数据库阻塞和死锁的区别

结合锁机制原理,阻塞与死锁均为事务锁不兼容引发的并发故障,但二者在等待逻辑、数据库处理机制、业务影响、解决方式上存在本质区别,两者的差异如下:

对比维度 阻塞(锁等待) 死锁
等待方式 单向等待 循环双向等待
自动恢复 无法自动恢复 数据库自动检测、自动破除
业务影响 事务卡顿、连接堆积、服务超时 事务直接失败、业务报错
核心诱因 长事务、锁范围过大 SQL执行顺序交叉、事务逻辑混乱

三 数据库锁问题排查与处理

3.1 锁处理相关视图

DM8提供多张系统动态视图,可实时查询锁状态、阻塞关系、死锁历史、会话事务信息,是故障排查的核心工具。

以下为常用核心视图:

视图名称 分类 核心用途 关键字段
V$LOCK 锁资源 实时查询活动的事务锁信息,包含锁类型、事务ID、阻塞状态 TRX_ID, LTYPE, LMODE, BLOCKED, TABLE_ID, TID
V$SESSIONS 会话信息 查询在线会话、执行SQL、事务状态、客户端地址等会话级信息 SESS_ID, SQL_TEXT, TRX_ID,STATE
V$TRX 事务信息 查询活跃事务、事务开启时间、锁持有数量等事务级信息 ID, STATUS,SESS_ID
V$TRXWAIT 事务等待 精准展示阻塞等待关系,定位等待事务与阻塞源头事务 TRX_ID, WAIT_FOR_ID
V$DEADLOCK_HISTORY 死锁历史 留存历史死锁详情,用于事后溯源分析与问题定位 TRX_ID,SESS_ID,SQL_TEXT,HAPPEN_TIME,DEADLOCK_CYCLE
V$SQL_HISTORY SQL历史 记录历史执行的SQL语句,用于事后溯源、慢SQL排查与性能分析。默认保留最近10000条数据,由SQL_HISTORY_CNT 参数控制,最大保留100000 条记录 SEQ_NO,SESS_ID,TRX_ID,TOP_SQL_TEXT,START_TIME

3.2 数据库阻塞排查与处理

3.2.1 数据库当前阻塞排查

  1. 查询数据库当前阻塞信息
SELECT DATEDIFF(SS, S1.LAST_SEND_TIME, SYSDATE) WAIT_TIME_S, '被阻塞的信息' WAIT_INFO, S1.SESS_ID WAIT_SID, S1.SQL_TEXT WAIT_SQL_TEXT, S1.STATE WAIT_STATE, S1.TRX_ID WAIT_TRX_ID, S1.USER_NAME WAIT_USER_NAME, S1.CLNT_IP WAIT_CLNT_IP, S1.APPNAME WAIT_APPNAME, S1.LAST_SEND_TIME WAIT_LAST_SEND_TIME, 'SP_CLOSE_SESSION(' || S1.SESS_ID || ');' KILL_WAIT_SID, '引起阻塞的信息' BLOCK, S2.SESS_ID BLOCK_SID, S2.SQL_TEXT BLOCK_SQL_TEXT, S2.STATE BLOCK_STATE, S2.TRX_ID BLOCK_TRX_ID, S2.USER_NAME BLOCK_USER_NAME, S2.CLNT_IP BLOCK_CLNT_IP, S2.APPNAME BLOCK_APPNAME, S2.LAST_SEND_TIME BLOCK_LAST_SEND_TIME, 'SP_CLOSE_SESSION(' || S2.SESS_ID || ');' KILL_BLOCK_SID FROM V$SESSIONS S1, V$SESSIONS S2, V$TRXWAIT W WHERE S1.TRX_ID = W.ID AND S2.TRX_ID = W.WAIT_FOR_ID;

通过锁阻塞排查语句获取到的阻塞结果,通常分为两种场景:

  • 第一种,可以清晰看到完整的阻塞等待链路,阻塞会话与被阻塞会话操作的数据对象直接关联,二者 SQL 操作同一张数据表、同一行记录,阻塞诱因一目了然;
行号 WAIT_TIME_S WAIT_INFO WAIT_SID WAIT_SQL_TEXT WAIT_STATE WAIT_TRX_ID WAIT_USER_NAME WAIT_CLNT_IP WAIT_APPNAME WAIT_LAST_SEND_TIME KILL_WAIT_SID BLOCK BLOCK_SID BLOCK_SQL_TEXT BLOCK_STATE BLOCK_TRX_ID BLOCK_USER_NAME BLOCK_CLNT_IP BLOCK_APPNAME BLOCK_LAST_SEND_TIME KILL_BLOCK_SID ---------- ----------- ------------------ -------------------- ------------------------------------------- ---------- -------------------- -------------- -------------------------- ------------ -------------------------- ---------------------------------- --------------------- -------------------- ----------------------------------------------------- ----------- -------------------- --------------- ------------------------- ------------- -------------------------- ---------------------------------- 1 64 被阻塞的信息 139953070740384 delete from app_user.emp where empno=7902; ACTIVE 274598 SYSDBA ::ffff:192.168.56.13:51200 disql 2026-08-07 11:23:13.784807 SP_CLOSE_SESSION(139953070740384); 引起阻塞的信息 139953070300696 update app_user.emp set sal=14600 where empno=7902; IDLE 274597 SYSDBA ::ffff:192.168.56.1:61189 DIsql.exe 2026-08-07 11:23:50.067220 SP_CLOSE_SESSION(139953070300696);
  • 第二种,阻塞会话当前展示的最新执行 SQL,和被阻塞会话操作的数据对象看上去并无关联。该现象的本质是达梦以完整事务作为锁持有单元,事务开启之后执行过的 DML 语句会一直持有行锁、表锁直至事务提交。查询视图抓取到的只是该事务最后一条运行的 SQL,真正产生锁占用、引发阻塞的,是该事务前期执行过的语句。
行号 WAIT_TIME_S WAIT_INFO WAIT_SID WAIT_SQL_TEXT WAIT_STATE WAIT_TRX_ID WAIT_USER_NAME WAIT_CLNT_IP WAIT_APPNAME WAIT_LAST_SEND_TIME KILL_WAIT_SID BLOCK BLOCK_SID BLOCK_SQL_TEXT BLOCK_STATE BLOCK_TRX_ID BLOCK_USER_NAME BLOCK_CLNT_IP BLOCK_APPNAME BLOCK_LAST_SEND_TIME KILL_BLOCK_SID ---------- ----------- ------------------ -------------------- ------------------------------------------- ---------- -------------------- -------------- -------------------------- ------------ -------------------------- ---------------------------------- --------------------- -------------------- ------------------------------------------------ ----------- -------------------- --------------- ------------------------- ------------- -------------------------- ---------------------------------- 1 125 被阻塞的信息 139953070740384 delete from app_user.emp where empno=7902; ACTIVE 274598 SYSDBA ::ffff:192.168.56.13:51200 disql 2026-08-07 11:23:13.784807 SP_CLOSE_SESSION(139953070740384); 引起阻塞的信息 139953070300696 select ENAME FROM app_user.emp where empno=7902; IDLE 274597 SYSDBA ::ffff:192.168.56.1:61189 DIsql.exe 2026-08-07 11:25:12.710349 SP_CLOSE_SESSION(139953070300696);
  • 如果事务历史执行语句尚且保存在 V$SQL_HISTORY 内存视图(保留记录条数可参考【锁处理相关视图】关于该视图的说明)内,我们便可查询该视图,检索该阻塞会话完整的 SQL 执行记录,定位出最先占用行锁、引发阻塞的原始语句;
SQL> SELECT START_TIME,SESS_ID,TRX_ID,TOP_SQL_TEXT from V$SQL_HISTORY WHERE TRX_ID=274597 AND SESS_ID=139953070300696 ORDER BY START_TIME ASC; 行号 START_TIME SESS_ID TRX_ID TOP_SQL_TEXT ---------- -------------------------- ------------------ ------- ----------------------------------------------------- 1 2026-08-07 11:23:50.066883 139953070300696 274597 update app_user.emp set sal=14600 where empno=7902; 2 2026-08-07 11:25:12.710306 139953070300696 274597 select ENAME FROM app_user.emp where empno=7902;
  • 如果相关记录已经超出 V$SQL_HISTORY 的缓存保存周期,内存视图的数据已经覆盖,这时就需要依靠 SQL 日志进行溯源。需要注意 SQLLOG 日志不会默认开启,想要留存完整的执行语句,必须事前开启数据库 SQL 日志采集功能。
##根据查询出的事务ID 274597 ,查找SQLLOG日志相关内容 [dmdba@dm8 dmlogcommit]$ cat dmsql_DMSERVER_20260807_112122.log |grep 274597 2026-08-07 11:23:50.065 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:NULL appname:DIsql.exe ip:::ffff:192.168.56.1) TRX: START 2026-08-07 11:23:50.069 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) [UPD] update app_user.emp set sal=14600 where empno=7902; 2026-08-07 11:23:50.069 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) DLCK used time:3(us) 2026-08-07 11:23:50.069 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:NULL appname:DIsql.exe ip:::ffff:192.168.56.1) trx[274597] alloc pseg page[0, 2015], page_lsn[1465964], n_pages[1] 2026-08-07 11:23:50.069 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) [UPD] update app_user.emp set sal=14600 where empno=7902; EXECTIME: 0(ms) ROWCOUNT: 1(rows) EXEC_ID: 601. 2026-08-07 11:25:12.713 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) PREPARE 2026-08-07 11:25:12.713 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) [ORA]: select ENAME FROM app_user.emp where empno=7902; 2026-08-07 11:25:12.713 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) [SEL] select ENAME FROM app_user.emp where empno=7902; 2026-08-07 11:25:12.713 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) DLCK used time:1(us) 2026-08-07 11:25:12.713 (EP[0] sess:0x7f495d0a5e18 thrd:2864 user:SYSDBA trxid:274597 stmt:0x7f495d16ced0 appname:DIsql.exe ip:::ffff:192.168.56.1) [SEL] select ENAME FROM app_user.emp where empno=7902; EXECTIME: 0(ms) ROWCOUNT: 1(rows) EXEC_ID: 602.

可参考如下步骤开启SQLLOG日志:

##1. 编辑SQLLOG配置文件,该文件位于数据库数据目录内 [dmdba@dm8 DAMENG]$ vi sqllog.ini [SLOG_ALL] FILE_PATH = /dmbak/dmlogcommit ## SQL日志存放目录 SWITCH_LIMIT = 128 ## 单个日志文件上限,单位 MB;到达128MB自动新建日志 ASYNC_FLUSH = 1 ## 0 同步刷盘,SQL执行完毕立刻落盘,性能偏低、数据最安全 ## 1 异步刷盘,缓冲区批量写入磁盘,减少IO损耗,生产环境推荐 FILE_NUM = 100 ## 日志文件最大保留份数;超出100个,自动删除最老旧的SQL日志文件 ##2. 数据库开启 SQLLOG 日志功能 SQL> SP_SET_PARA_VALUE(2,'SVR_LOG',1); DMSQL 过程已成功完成 ##3. 重启数据库使配置生效
  1. 与业务确认当前阻塞情况,由业务确认是否清理阻塞
  2. 根据业务提供的信息,清理阻塞源头,释放锁资源
SQL> SP_CLOSE_SESSION(139953070300696); DMSQL 过程已成功完成

3.2.2 数据库历史阻塞排查

达梦内置视图 V$LOCKV$TRXWAIT 只负责展示当下时刻的锁资源与事务等待状态,属于瞬时内存视图;一旦事务提交、会话断开,阻塞链路信息随即释放。

因此如果需要事后回查曾经发生过的锁阻塞、定位历史阻塞源头、阻塞 SQL 以及等待链路,必须提前配置定时 JOB 任务,周期性采样抓取阻塞会话、事务 ID、客户端 IP、正在执行语句、锁类型等字段,持久化存入自建日志数据表,方便故障发生之后回查分析。

--查询 TRX_WAIT_HISTORY 内容,查看历史阻塞信息 SQL> SELECT WAIT_INFO,WAIT_SID,WAIT_SQL_TEXT,WAIT_TRX_ID,BLOCK_INFO,BLOCK_SID,BLOCK_SQL_TEXT,BLOCK_TRX_ID from TRX_WAIT_HISTORY WHERE WAIT_TRX_ID=274598; 行号 WAIT_INFO WAIT_SID WAIT_SQL_TEXT WAIT_TRX_ID BLOCK_INFO BLOCK_SID BLOCK_SQL_TEXT BLOCK_TRX_ID ---------- ------------------ -------------------- ------------------------------------------- -------------------- --------------------- -------------------- ------------------------------------------------ -------------------- 1 被阻塞的信息 139953070740384 delete from app_user.emp where empno=7902; 274598 引起阻塞的信息 139953070300696 select ENAME FROM app_user.emp where empno=7902; 274597

可参考如下内容,创建定时作业获取数据库阻塞信息并记录:

--代理作业设置后,随着表记录增加,表占用空间会逐渐增加,需注意及时清理历史信息 --1. 创建阻塞信息记录表 CREATE TABLE TRX_WAIT_HISTORY ( STATTIME TIMESTAMP, WAIT_TIME_S INTEGER, WAIT_INFO VARCHAR2(30), WAIT_SID BIGINT, WAIT_SQL_TEXT VARCHAR(1000), WAIT_STATE VARCHAR(10), WAIT_TRX_ID BIGINT, WAIT_USER_NAME VARCHAR(128), WAIT_CLNT_IP VARCHAR(128), WAIT_APPNAME VARCHAR(128), WAIT_LAST_SEND_TIME DATETIME(6), BLOCK_INFO VARCHAR2(30), BLOCK_SID BIGINT, BLOCK_SQL_TEXT VARCHAR(1000), BLOCK_STATE VARCHAR(10), BLOCK_TRX_ID BIGINT, BLOCK_USER_NAME VARCHAR(128), BLOCK_CLNT_IP VARCHAR(128), BLOCK_APPNAME VARCHAR(128), BLOCK_LAST_SEND_TIME DATETIME(6) ); --2. 创建存储过程获取阻塞信息并插入到阻塞信息记录表 CREATE PROCEDURE GET_TRX_WAIT AS BEGIN INSERT INTO TRX_WAIT_HISTORY SELECT SYSDATE STATTIME, DATEDIFF(SS, S1.LAST_SEND_TIME, SYSDATE) WAIT_TIME_S, '被阻塞的信息' WAIT_INFO, S1.SESS_ID WAIT_SID, S1.SQL_TEXT WAIT_SQL_TEXT, S1.STATE WAIT_STATE, S1.TRX_ID WAIT_TRX_ID, S1.USER_NAME WAIT_USER_NAME, S1.CLNT_IP WAIT_CLNT_IP, S1.APPNAME WAIT_APPNAME, S1.LAST_SEND_TIME WAIT_LAST_SEND_TIME, '引起阻塞的信息' BLOCK_INFO, S2.SESS_ID BLOCK_SID, S2.SQL_TEXT BLOCK_SQL_TEXT, S2.STATE BLOCK_STATE, S2.TRX_ID BLOCK_TRX_ID, S2.USER_NAME BLOCK_USER_NAME, S2.CLNT_IP BLOCK_CLNT_IP, S2.APPNAME BLOCK_APPNAME, S2.LAST_SEND_TIME BLOCK_LAST_SEND_TIME FROM V$SESSIONS S1, V$SESSIONS S2, V$TRXWAIT W WHERE S1.TRX_ID = W.ID AND S2.TRX_ID = W.WAIT_FOR_ID; COMMIT; END; --3. 通过代理作业,设置每 1 分钟获取一次阻塞信息 --3.1 如未初始化代理作业,需要先初始化代理作业 SQL>SP_INIT_JOB_SYS(1); --3.2 设置每1分钟执行一次 GET_TRX_WAIT 存储过程获取阻塞信息 call SP_CREATE_JOB('GET_TRXWAIT_1MIN',1,0,'',0,0,'',0,''); call SP_JOB_CONFIG_START('GET_TRXWAIT_1MIN'); call SP_ADD_JOB_STEP_EX('GET_TRXWAIT_1MIN', 'GET_TRXWAIT', 0, 'GET_TRX_WAIT', 1, 1, 0, 0, NULL, 0, ''); call SP_ADD_JOB_SCHEDULE('GET_TRXWAIT_1MIN', 'GET_TRX_WAIT_1MIN', 1, 1, 1, 0, 1, '00:00:00', '23:59:59', '2026-08-07 00:00:00', NULL, ''); call SP_JOB_CONFIG_COMMIT('GET_TRXWAIT_1MIN'); --3.3 如有需要可选择参考如下语句,修改代理作业每 2 分钟执行一次 GET_TRX_WAIT 存储过程获取阻塞信息 call SP_JOB_CONFIG_START('GET_TRXWAIT_1MIN'); call SP_ALTER_JOB_SCHEDULE('GET_TRXWAIT_1MIN', 'GET_TRX_WAIT_1MIN', 1, 1, 1, 0, 2, '00:00:00', '23:59:59', '2026-08-07 00:00:00', NULL, ''); call SP_JOB_CONFIG_COMMIT('GET_TRXWAIT_1MIN');

3.3 数据库死锁排查与处理

数据库具备自动死锁检测能力,检测到循环等待之后会主动牺牲回滚其中一个事务,快速断开死锁阻塞,防止会话长时间僵持等待;所有历史死锁事件都会留存至 V$DEADLOCK_HISTORY,可通过该视图完成事后故障溯源。

  1. 查询死锁历史记录表
SQL> select TRX_ID,SESS_ID,SQL_TEXT,HAPPEN_TIME,DEADLOCK_CYCLE from SYS.V$DEADLOCK_HISTORY order by SEQNO asc; 行号 TRX_ID SESS_ID SQL_TEXT HAPPEN_TIME DEADLOCK_CYCLE ---------- -------------------- -------------------- -------------------------------------------------------------------------------- -------------------------- ----------------------------------------- 1 281496 140710860375712 UPDATE DMHR.DEPARTMENT SET DEPARTMENT_NAME='市场部' WHERE DEPARTMENT_ID=1002; 2026-08-07 14:32:49.257775 self -> (281499, 140710694690984) -> self
  1. 通过查询V$SQL_HISTORY表,查看死锁SQL信息
SQL> SELECT H.SEQ_NO,H.SESS_ID,H.TRX_ID,H.TOP_SQL_TEXT,H.START_TIME from SYS.V$SQL_HISTORY H WHERE H.TRX_ID IN (281496,281499) ORDER BY H.START_TIME ASC; 行号 SEQ_NO SESS_ID TRX_ID TOP_SQL_TEXT START_TIME ---------- ----------- -------------------- -------------------- -------------------------------------------------------------------------------------- -------------------------- 1 670 140710860375712 281496 UPDATE DMHR.EMPLOYEE SET SALARY = 12000 WHERE EMPLOYEE_ID=1002; 2026-08-07 14:32:09.560017 2 672 140710694690984 281499 UPDATE DMHR.DEPARTMENT SET LOCATION_ID = 11 WHERE MANAGER_ID = 10002; 2026-08-07 14:32:24.090754 3 673 140710694690984 281499 UPDATE DMHR.EMPLOYEE SET DEPARTMENT_ID = 101 WHERE IDENTITY_CARD='630103197612261000'; 2026-08-07 14:32:41.250372 4 674 140710860375712 281496 UPDATE DMHR.DEPARTMENT SET DEPARTMENT_NAME='市场部' WHERE DEPARTMENT_ID=1002; 2026-08-07 14:32:48.501083 5 675 140710860375712 281496 commit; 2026-08-07 14:32:55.718433 6 676 140710694690984 281499 commit; 2026-08-07 14:32:59.723992
  1. 如果超过V$SQL_HISTORY表保留记录,可通过 SQLLOG 日志查询相关记录
--1. 事务 281496 的信息,可以看到死锁信息 [ERR(-6403)]: UPDATE DMHR.DEPARTMENT SET DEPARTMENT_NAME='市场部' WHERE DEPARTMENT_ID=1002; [dmdba@dm8 dmlogcommit]$ cat dmsql_DMSERVER_20260807_132900.log|grep 281496 2026-08-07 14:32:09.563 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:NULL appname:disql ip:::1) TRX: START 2026-08-07 14:32:09.563 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [UPD] UPDATE DMHR.EMPLOYEE SET SALARY = 12000 WHERE EMPLOYEE_ID=1002; 2026-08-07 14:32:09.563 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) DLCK used time:1(us) 2026-08-07 14:32:09.571 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:NULL appname:disql ip:::1) trx[281496] alloc pseg page[0, 1887], page_lsn[1468561], n_pages[1] 2026-08-07 14:32:09.571 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [UPD] UPDATE DMHR.EMPLOYEE SET SALARY = 12000 WHERE EMPLOYEE_ID=1002; EXECTIME: 4(ms) ROWCOUNT: 1(rows) EXEC_ID: 10801. 2026-08-07 14:32:48.503 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) PREPARE 2026-08-07 14:32:48.503 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [ORA]: UPDATE DMHR.DEPARTMENT SET DEPARTMENT_NAME='市场部' WHERE DEPARTMENT_ID=1002; 2026-08-07 14:32:48.507 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [UPD] UPDATE DMHR.DEPARTMENT SET DEPARTMENT_NAME='市场部' WHERE DEPARTMENT_ID=1002; 2026-08-07 14:32:48.507 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) DLCK used time:1(us) 2026-08-07 14:32:49.263 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:NULL appname:disql ip:::1) trx[281496] LOCK_TID (mode:X, table id:1050) wait for 1 trxs, trx[281499] used time:757(ms) 2026-08-07 14:32:49.263 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [ERR(-6403)]: UPDATE DMHR.DEPARTMENT SET DEPARTMENT_NAME='市场部' WHERE DEPARTMENT_ID=1002; EXECTIME: 757(ms) ROWCOUNT: 0(rows) EXEC_ID: 10802. 2026-08-07 14:32:55.723 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) PREPARE 2026-08-07 14:32:55.723 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [ORA]: commit; 2026-08-07 14:32:55.723 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:0x7ff9cd4cd238 appname:disql ip:::1) [DML] commit; 2026-08-07 14:32:55.723 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:NULL appname:disql ip:::1) TRX: COMMIT 2026-08-07 14:32:55.720 (EP[0] sess:0x7ff9c2f920a8 thrd:2709 user:SYSDBA trxid:281499 stmt:NULL appname:DIsql.exe) trx[281499] LOCK_TID (mode:X, table id:1052) wait for 1 trxs, trx[281496] used time:14469(ms) 2026-08-07 14:34:56.131 (EP[0] sess:NULL thrd:NULL user:NULL trxid:NULL stmt:NULL) trx[281496]: purg2_page free pseg page( 0, 1887), page_lsn =1468623 [dmdba@dm8 dmlogcommit]$
--2. 事务 281499 的信息 [dmdba@dm8 dmlogcommit]$ cat dmsql_DMSERVER_20260807_132900.log |grep 281499 2026-08-07 14:32:24.084 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:NULL appname:DIsql.exe ip:::ffff:192.168.56.1) TRX: START 2026-08-07 14:32:24.084 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [UPD] UPDATE DMHR.DEPARTMENT SET LOCATION_ID = 11 WHERE MANAGER_ID = 10002; 2026-08-07 14:32:24.084 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) DLCK used time:2(us) 2026-08-07 14:32:24.084 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:NULL appname:DIsql.exe ip:::ffff:192.168.56.1) trx[281499] alloc pseg page[0, 2447], page_lsn[1468575], n_pages[1] 2026-08-07 14:32:24.084 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [UPD] UPDATE DMHR.DEPARTMENT SET LOCATION_ID = 11 WHERE MANAGER_ID = 10002; EXECTIME: 0(ms) ROWCOUNT: 1(rows) EXEC_ID: 11101. 2026-08-07 14:32:41.240 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) PREPARE 2026-08-07 14:32:41.240 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [ORA]: UPDATE DMHR.EMPLOYEE SET DEPARTMENT_ID = 101 WHERE IDENTITY_CARD='630103197612261000'; 2026-08-07 14:32:41.248 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [UPD] UPDATE DMHR.EMPLOYEE SET DEPARTMENT_ID = 101 WHERE IDENTITY_CARD='630103197612261000'; 2026-08-07 14:32:41.248 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) DLCK used time:2(us) 2026-08-07 14:32:49.263 (EP[0] sess:0x7ff9ccd946a0 thrd:2709 user:SYSDBA trxid:281496 stmt:NULL appname:disql ip:::1) trx[281496] LOCK_TID (mode:X, table id:1050) wait for 1 trxs, trx[281499] used time:757(ms) 2026-08-07 14:32:55.720 (EP[0] sess:0x7ff9c2f920a8 thrd:2709 user:SYSDBA trxid:281499 stmt:NULL appname:DIsql.exe) trx[281499] LOCK_TID (mode:X, table id:1052) wait for 1 trxs, trx[281496] used time:14469(ms) 2026-08-07 14:32:55.716 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) DLCK used time:6(us) 2026-08-07 14:32:55.716 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [UPD] UPDATE DMHR.EMPLOYEE SET DEPARTMENT_ID = 101 WHERE IDENTITY_CARD='630103197612261000'; EXECTIME: 14470(ms) ROWCOUNT: 1(rows) EXEC_ID: 11102. 2026-08-07 14:32:59.720 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) PREPARE 2026-08-07 14:32:59.720 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [ORA]: commit; 2026-08-07 14:32:59.720 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:0x7ff9ccacd0f0 appname:DIsql.exe ip:::ffff:192.168.56.1) [DML] commit; 2026-08-07 14:32:59.720 (EP[0] sess:0x7ff9c2f920a8 thrd:2710 user:SYSDBA trxid:281499 stmt:NULL appname:DIsql.exe ip:::ffff:192.168.56.1) TRX: COMMIT 2026-08-07 14:35:00.136 (EP[0] sess:NULL thrd:NULL user:NULL trxid:NULL stmt:NULL) trx[281499]: purg2_page free pseg page( 0, 2447), page_lsn =1468624 [dmdba@dm8 dmlogcommit]$
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服