注册
达梦数据库死锁与阻塞问题分析
技术分享/ 文章详情 /

达梦数据库死锁与阻塞问题分析

DM_Kirin 2025/05/09 152 0 0

引言

在数据库管理系统中,死锁与阻塞是一个常见的问题,特别是在高并发环境下。本文将探讨达梦数据库中的死锁与阻塞的问题,分析其成因、检测方法以及解决方案

阻塞与死锁的简单定义:

1.什么是死锁

  • 死锁:是两个事务都在等待对方持有的资源锁,要等待对方释放有的资源锁之后才能继续工作,两者互不想让,坚持到底,都在等待彼此完成才继续工作,就是这样的状态,双方都完成不了,从而陷入死循环。
    遇到死锁解决策略:
    1)数据库中的机制是当发生死锁时会牺牲其中的一个进程来让其继续执行下去。
    2)那种情况是应用程序BUG产生的,需要调整程序的逻辑结构,在对多表进行操作时,尽量按照相同的顺序执行,避免同时锁定两个资源,必须同时锁定两个资源的时候,要保证在任何时候都要按照相同顺序来锁定资源。

2.什么是阻塞

  • 阻塞:是第一个事务占有资源没有释放,而第二个事务需要获取这个资源,如果第一个事务没有提交或者回滚,第二个事务会一直等待下去,直到第一个事务释放该资源为止
    遇到阻塞解决策略:
    1)被阻塞的事务会一直挂起、直到持有锁的事务放弃锁定的资源为止(提交/回滚)
    2)程序应用bug产生,需要调整程序的逻辑结构,尽量形成短事务,快速提交,避免阻塞时间过长,不要将其他无关操作放到容易引起阻塞的模块。

达梦数据库中的死锁成因

  1. 资源竞争
    在高并发环境下,多个事务同时访问相同的数据资源,导致资源竞争。如果事务的执行顺序不当,可能会形成循环等待,从而引发死锁
  2. 锁的粒度
    达梦数据库支持多种锁粒度,如行级锁、表级锁等。锁的粒度越细,事务之间的冲突越少,但管理复杂度也越高。如果锁的粒度过粗,可能会增加死锁的风险
  3. 事务的执行顺序
    事务的执行顺序对死锁的发生有重要影响。如果事务的执行顺序不合理,可能会导致循环等待,从而引发死锁

达梦数据库中的死锁检测

达梦数据库提供了多种死锁检测机制,包括:

  1. 死锁检测算法
    达梦数据库采用基于图论的死锁检测算法,通过构建事务等待图来检测是否存在死锁。如果检测到死锁,系统会自动选择一个事务进行回滚,以解除死锁
相关动态视图
视图			描述
V$SESSIONS		显示会话的具体信息
V$TRX			显示所有活动事务的信息
V$TRXWAIT		显示事务等待信息
V$TRX_VIEW		显示活动事务视图信息
V$LOCK			显示当前系统中锁的状态
V$DEADLOCK_HISTORY	显示死锁的历史信息

  1. 死锁超时
    达梦数据库还支持设置死锁超时时间。如果事务在等待资源时超过设定的超时时间,系统会自动回滚该事务,以避免长时间的死锁
相关参数
参数		描述
DDL_WAIT_TIME	DDL操作的锁超时时间
BLDR_WAIT_TIME	批量装载时锁超时时间

怎样解锁死锁与阻塞

  • 死锁不用人工干涉,数据库系统自动识别并解除。但是死锁是非正常情况,需要找到死锁原因后,从应用逻辑层解决。

  • 阻塞 可能是应用流程涉及提交慢导致,但是最终也会提交或者回滚,所以一般情况下也会自动消除。如果阻塞很长时间没有消除,在数据库层可以通过 SP_CLOSE_SESSION(SESSID);来强制终阻塞源头的会话使其回滚,来解除阻塞,但是这个需要用户确认,不能私自操作。

1.从系统时间中可以查看锁等待事件和地锁事件发生的次数和时间来评估是否存在死锁和阻塞

查询语句:SELECT * FROM V$SYSTEM_EVENT ORDER BY TIME_WAITED DESC;

2.可以通过辅助表跟踪阻塞

STEP1:创建辅助记录表:

CREATE TABLE TRX_WAIT(
"STATTIME" TIMESTAMP,
"SS" INTEGER,
"WT" VARCHAR2(30),
"WT_SESS_ID" BIGINT,
"WT_SQL_TEXT" VARCHAR(1000),
"WT_STATE" VARCHAR(10),
"WT_TRX_ID" BIGINT,
"WT_USER_NAME" VARCHAR(128),
"WT_CLNT_IP" VARCHAR(128),
"WT_APPNAME" VARCHAR(128),
"WT_LAST_SEND_TIME" DATETIME(6),
"FM" VARCHAR2(30),
"FM_SESS_ID" BIGINT ,
"FM_SQL_TEXT" VARCHAR(1000),
"FM_STATE" VARCHAR(10),
"FM_TRX_ID" BIGINT,
"FM_USER_NAME" VARCHAR(128),
"FM_CLNT_IP" VARCHAR(128),
"FM APPNAME" VARCHAR(128),
"FM LAST SEND TIME"DATETIME(6)
);

STEP2:创建辅助存储过程GET_TX_WAIT

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

STEP3:创建作业定时收集阻塞数据

测试实例演示

1.创建测试表并插入数据
-- 创建测试表
CREATE TABLE test (
    id INT PRIMARY KEY,
    balance DECIMAL(10,2)
);
-- 插入测试数据
INSERT INTO test VALUES (1, 1000.00);
INSERT INTO test VALUES (2, 2000.00);

Commit;
2.模拟死锁场景
开两个会话,依次执行两个会话的第一条修改语句,再依次执行第二条语句
会话1:
UPDATE test SET balance = balance - 100 WHERE id = 1; -- 锁定 id=1 的行

UPDATE test SET balance = balance + 100 WHERE id = 2; -- 尝试获取 id=2 的锁(被 Session 2 占用)
-- 此时 Session 1 会等待 Session 2 释放锁
会话2:
UPDATE test SET balance = balance + 100 WHERE id = 2; -- 锁定 id=2 的行

UPDATE test SET balance = balance - 100 WHERE id = 1; -- 尝试获取 id=1 的锁(被 Session 1 占用)
-- 此时达梦检测到死锁,自动终止其中一个事务

3.死锁解决

达梦自动检测出现死锁情况,并终止会话2中的第二条语句执行
image.pngimage.png
select * from v$lock where blocked=1; – 查看死锁情况

image.png

select * from V$SESSIONS; --显示会话具体信息
image.png
执行call sp_close_session(‘140468046420600’);并提交

再次执行select * from v$lock where blocked=1;确认死锁已被杀
image.png

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服