注册
DM8事务管理与锁等待排查
专栏/培训园地/ 文章详情 /

DM8事务管理与锁等待排查

DM_KSL 2026/09/16 97 0 0
摘要

DM事务管理与锁等待问题排查

达梦数据库的事务管理与Oracle类似,是自动开启的。理解其工作机制,关键是掌握事务的隐式开启、显式结束以及自动提交模式。

一、 事务的四大特性

原子性 (Atomicity):事务中的所有操作要么全部成功,要么全部失败。
一致性 (Consistency):事务执行前后,数据库从一个一致状态变到另一个一致状态。
隔离性 (Isolation):多个并发事务之间互不干扰。
持久性 (Durability):一旦事务提交,对数据的修改就是永久性的。

二、 事务的开始与结束

事务的开始:隐式开启
在达梦中,通常不需要显式地“开启”一个事务。事务会在执行第一条可执行的SQL语句(如INSERT, UPDATE, DELETE)时自动隐式开始
事务的结束:显式提交或回滚
事务必须通过以下命令之一来显式结束:
COMMIT; 提交事务,将当前事务的所有更改永久保存到数据库。
ROLLBACK; 回滚事务,撤销当前事务的所有未提交更改,数据库恢复到事务开始前的状态

三、 自动提交模式

AUTOCOMMIT = OFF(默认):需要手动执行COMMIT来提交,或ROLLBACK来撤销更改
AUTOCOMMIT = ON:每条SQL语句执行后都会自动提交,无法回滚。
查看当前状态
SHOW AUTOCOMMIT;
image.png
修改

--关闭自动提交(进入手动提交模式)
SET AUTOCOMMIT OFF;
--开启自动提交
SET AUTOCOMMIT ON;

隐式提交的情况
无论AUTOCOMMIT如何设置,执行CREATE、ALTER、DROP、TRUNCATE、GRANT、REVOKE等DDL语句时,都会触发隐式提交

四、 保存点(SAVEPOINT)

在复杂事务中,可以使用保存点实现部分回滚。
创建保存点:SAVEPOINT savepoint_name;
回滚到保存点:ROLLBACK TO SAVEPOINT savepoint_name;
释放保存点:RELEASE SAVEPOINT savepoint_name;

SET AUTOCOMMIT OFF;
INSERT INTO products (id, name) VALUES (1, 'Product A');
SAVEPOINT before_insert_b;				-- 设置保存点
INSERT INTO products (id, name) VALUES (2, 'Product B');
--发现 Product B 插入有误,回滚到保存点
ROLLBACK TO SAVEPOINT before_insert_b;
COMMIT; -- 提交后,数据库中只有 Product A

五、 事务隔离级别

DM数据库支持三种事务隔离级别:读未提交、读提交和串行化,(另外 DM 数据库还支持只读事务,只读事务只能访问数据,但不能修改数据)读提交是 DM 数据库默认使用的事务隔离级别。用户在事务开始时,使用以下语句可设定事务隔离级别。
查询事务隔离级别

select isolation from v$trx;--0:读未提交,1:读提交(默认)、2:可重复读、3:串行化

设定事务隔离级别

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
隔离级别 脏读 (Dirty Read) 不可重复读 (Non-Repeatable Read) 幻读 (Phantom Read) 说明
读未提交 (READ UNCOMMITTED) 可能 可能 可能 可能读到未提交的“脏”数据,并发最高
读已提交 (READ COMMITTED) 不可能 可能 可能 默认级别,保证只读到已提交的数据
串行化 (SERIALIZABLE) 不可能 不可能 不可能 最严格,完全串行执行,性能最低

脏读(DirtyRead)
所谓脏读就是对脏数据的读取,而脏数据所指的就是未提交的已修改数据。也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被称为脏读。如果一个事务在提交操作结果之前,另一个事务可以看到该结果,就会发生脏读。
举例:小张本月奖金 500 块,财务错误地加在小王工资上,还未提交,小王通过事务查询自己的工资多了 500,后续财务核算进行修改,将事务进行回滚,这样员工 B 通过事务看到的是脏读。
不可重复读(Non-RepeatableRead)
一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。如果一个事务在读取了一条记录后,另一个事务修改了这条记录并且提交了事务,再次读取记录时如果获取到的是修改后的数据,这就发生了不可重复读情况。
举例:在事务A中,读取到张三的工资为 5000,操作没有完成,事务还没提交。与此同时,事务 B 把张三的工资改为 8000,并提交了事务。 随后,在事务 A 中,再次读取张三的工资,此时工资变为 8000,两次读取到的结果不一致。
幻像读(PhantomRead)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻像读。
举例:目前公司员工有 10 人,老板通过事务 A 读取公司所有人数为 10 人。此时招聘部通过事务B插入一条新员工的记录。这时,事务 A 再次查看公司的员工人数记录,记录为 11 人。此时产生了幻读。

六、 会话与事务管理相关视图

(一) V$SESSIONS:会话状态监控

核心功能:实时跟踪所有会话的详细信息,包括会话 ID、用户、客户端 IP、执行的 SQL 语句、等待事件等。
核心字段:
SESS_ID: 会话 ID,系统内部标识
SQL_TEST: 取 sql 的头 1000 个字符
STATE: 会话状态。
CREATE 创建,表示会话对象已创建,但还不能使用;
STARTUP 启动,表示会话正在启动中;
IDLE 空闲,表示会话当前没有执行操作;
ACTIVE 活动,表示会话当前正在执行操作;
PENDING 限流等待,当 INI 参数 MAX_CONCURRENT_TRX>0 时,会话可能会因为并行事务限流而处于此状态;
FREEING 正在释放,表示会话正在被释放;
USER_NAME: 当前用户
TRX_ID: 事务 id,为 0 表示事务未开始或事务已结束
CLNT_IP: 客户端 IP
CLNT_HOST: 客户端主机名

(二) V$TRX:事务监控

核心功能:记录当前所有事务的状态,包括事务 ID、开始时间、锁持有情况、回滚段使用等。
核心字段:
ID:当前活动事务的 ID 号
STATUS:当前事务的状态。
NOT START 未开始任何操作;
ACTIVE 活动;
LOCK WAIT 锁等待;
ROLLING 正在回滚;
PRE_COMMIT 两阶段事务的预提交状态;
TO_RELEASE DPC 环境下分布式事务的等待释放状态。分布式事务完成第二阶段提交后转入 TO_RELEASE 状态,分布式事务在所有节点都转入 TO_RELEASE 状态后才允许释放
ISOLATION:隔离级。0:读未提交;1:读提交;2:可重复读;3:串行化
SESS_ID:当前事务的所在会话 ID,系统内部标识
THRD_ID:当前事务对应的线程 ID

等待事务排查:

//查询正在等待的事务列表
SELECT * FROM V$TRXWAIT;

根据查询出来的事务id查询事务详情

SELECT ID,SESS_ID,SQL_TEST,CLNT_IP, SQL_TEXT
FROM V$TRX left join V$SESSIONS B where A.SESS_ID = B.SESSID
WHERE ID = (2613902)

七、 通过动态性能视图排查锁等待

核心思路:

  • 是否存在锁等待
  • 哪个事务再等待,被哪个事务阻塞?
  • 阻塞事务和等待事务执行了什么SQL?
    步骤1:确定是否存在锁等待
    通过V$TRXWAIT视图查询当前活跃的锁等待关系
SELECT ID as 被阻塞的事务id,
WAIT_FOR_ID as 阻塞的事务id,
WAIT_TIME as 等待时间,
THRD_ID as 阻塞事务的线程id
FROM V$TRXWAIT;

image.png

说明当前存在锁等待,事务2613902正在等待事务2613901释放锁,已等待78653902秒。
步骤2:关联事务与会话,定位操作源
通过V$TRX(事务信息)和V$SESSIONS(会话信息)关联,获取事务对于的会话id和操作用户:

SELECT 
  t.ID AS 事务ID,
  t.SESS_ID AS 会话ID,
  s.USER_NAME AS 操作用户,
  s.CLNT_IP AS 客户端IP,
  t.STATUS AS 事务状态
FROM V$TRX t
JOIN V$SESSIONS s ON t.SESS_ID = s.SESS_ID
WHERE t.ID IN (
  SELECT ID FROM V$TRXWAIT
  UNION
  SELECT WAIT_FOR_ID FROM V$TRXWAIT
);

image.png

步骤3:查看事务执行的SQL语句
-- 查看阻塞事务和等待事务执行的SQL

SELECT 
  s.SESS_ID AS 会话ID,
  t.ID AS 事务ID,
  CASE 
    WHEN t.ID IN (SELECT WAIT_FOR_ID FROM V$TRXWAIT) THEN '阻塞方'
    WHEN t.ID IN (SELECT ID FROM V$TRXWAIT) THEN '等待方'
  END AS 角色,
  SUBSTR(s.SQL_TEXT, 1, 200) AS 执行的SQL  
FROM V$SESSIONS s
JOIN V$TRX t ON s.SESS_ID = t.SESS_ID
WHERE t.ID IN (
  SELECT ID FROM V$TRXWAIT
  UNION
  SELECT WAIT_FOR_ID FROM V$TRXWAIT
);

image.png

其他相关查找阻塞会话语句:
查询活跃会话

SELECT SESS_ID, USER_NAME, CLNT_IP, SQL_TEXT 
FROM V$SESSIONS 
WHERE STATE= 'ACTIVE';

查询阻塞会话

select s.USER_NAME,s.sess_id,s.TRX_ID,s.SQL_TEXT,s.RUN_STATUS from v$sessions s,v$lock l where s.trx_id=l.trx_id and l.blocked=1;

查询阻塞源头

select * from v$sessions where trx_id in (select wait_for_id from v$trxwait where wait_for_id not in (select id from v$trxwait));

查询被阻塞的信息和引起阻塞的信息

SELECT SYSDATE STATTIME,DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) SS,
        '被阻塞的信息'   WT,S1.SESS_ID WT_SESS_ID,S1.SQL_TEXT WT_SQL_TEXT,S1.STATE WT_STATE,S1.TRX_ID WT_TRX_ID,
        S1.USER_NAME WT_USER_NAME,S1.CLNT_IP WT_CLNT_IP,S1.APPNAME WT_APPNAME,S1.LAST_SEND_TIME WT_LAST_SEND_TIME,
        '引起阻塞的信息' FM,S2.SESS_ID FM_SESS_ID,S2.SQL_TEXT FM_SQL_TEXT,S2.STATE FM_STATE,S2.TRX_ID FM_TRX_ID,
        S2.USER_NAME FM_USER_NAME,S2.CLNT_IP FM_CLNT_IP,S2.APPNAME FM_APPNAME,S2.LAST_SEND_TIME FM_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;

查询阻塞关系

with sel_lock as
 (SELECT O.NAME, O.schid, o.pid, L.*
    FROM V$LOCK L, SYSOBJECTS O
   WHERE L.TABLE_ID = O.ID
     AND BLOCKED = 1),
locks as
 (select b.sess_id, b.sql_text, a.name, a.addr, a.schid, b.trx_id
    from sel_lock a, V$sessions b
   where a.trx_id = b.trx_id),
locks_wait as
 (select b.sess_id, b.sql_text, a.addr, b.trx_id
    from sel_lock a, V$sessions b
   where a.tid = b.trx_id)
select a.sess_id 被阻塞会话,
       a.sql_text 被阻塞语句,
       b.sess_id 造成阻塞会话,
       b.sql_text 造成阻塞语句,
       a.name,
       (select name
          from sysobjects
         where ID = a.schid
           and type$ = 'NVWATSTYBZ') 模式名
  from locks a, locks_wait b
 where a.addr = b.addr;

查看数据库的连接信息

SELECT 'sp_close_session('||A.SESS_ID||');',
A.SESS_ID AS 会话id,
A.SQL_TEXT AS SQL语句,
A.STATE AS 会话状态,
A.N_USED_STMT AS 当前会话使用句柄数量,
A.CURR_SCH AS 当前模式,
A.USER_NAME AS 用户名,
A.TRX_ID AS 事务ID,
A.CREATE_TIME AS 会话创建时间,
A.CLNT_TYPE AS 客户端类型,
A.TIME_ZONE AS 时区,
A.OSNAME AS 操作系统名称,
A.CONN_TYPE AS 连接类型,
B.PROTOCOL_TYPE AS 协议类型,
B.IP_ADDR AS 访问ip地址
FROM SYS.V$SESSIONS A ,SYS.V$CONNECT B where A.Sess_id= B.SADDR AND A.USER_NAME = 'NV2' ORDER BY 
SF_GET_EP_SEQNO(A.rowid),A.Sess_id ;

八、 解决锁等待

正常释放锁:通过查询出来的阻塞事务的操作方(通过客户端IP和操作用户定位),让其提交或者回滚事务:

COMMIT;
ROLLBACK;

强制终止阻塞对话:若阻塞事务无法正常释放,可通过SP_CLOSE_SESSION存储过程种植阻塞会话
SP_CLOSE_SESSION(140398304267936); --140398304267936是阻塞方的会话ID
image.png
image.png
不管是阻塞方执行COMMIT;、ROLLBACK;还是其他会话执行SP_CLOSE_SESSION(140398304267936);强制终止会话,等待方的执行语句随之完成。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服