一、达梦中死锁与阻塞的解释

阻塞和死锁是会与并发事务一起发生的两个事件,它们都与锁相关。当一个事务正在占用某个资源的锁,此时另一个事务正在请求这个资源上与第一个锁相冲突的锁类型时,就会发生阻塞。被阻塞的事务将一直挂起,直到持有锁的事务放弃锁定的资源为止。

死锁与阻塞的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。

例如事务 1 给表 T1 上了排他锁,第二个事务给表 T2 上了排他锁,此时事务 1 请求 T2 的排它锁,就会处于等待状态,被阻塞。若此时 T2 再请求表 T1 的排他锁,则T2 也处于阻塞状态。此时这两个事务发生死锁,DM会选择牺牲掉其中一个事务。

二、达梦中的锁模式

锁模式指定并发用户如何访问锁定资源。DM数据库使用4种不同的锁模式:共享锁、排他锁、意向共享锁和意向排它锁。

共享锁(Shared Lock,简称S锁)模式用于只读操作。这种模式允许并发事务同时读取相同的资源,但是不允许任何事务修改这个资源。

排它锁(Exclusive Lock,简称X锁)用于修改数据的操作,如插入、更新和删除。当某个事务占用某个资源上的排他锁时,其他事务就不能修改此数据,这种锁模式防止并发用户同时更新相同的数据,否则会造成数据不一致或者不正确的现象。

共享锁和排他锁的相容性是比较差的,如果我们在表粒度上上这两种锁,就会严重影响数据库的并发性能,例如:表是由行组成的,一个事务更新一张表的记录时,如果在表上上排他锁,那么就会禁止另一个事务更新同一张表的其他记录,系统的性能将会受到影响。为了解决这个问题,可以在表级引入新的锁类型来表示其所属行的加锁情况,这就引出了“意向锁”的概念。

意向共享锁(Intent Shared Lock,简称IS锁):如果要读取一张表中的记录,首先要对表加IS锁;

意向排它锁(Intent Exclusive Lock,简称IX锁):如果要写一张表中的记录,首先要对表加IX锁。

三、达梦中锁的类型

按照封锁对象的不同,锁可以分为 TID 锁(事务锁)和对象锁。TID锁TID锁以事务号为封锁对象,系统为每个活动事务生成一把TID锁,代替了其他数据库行锁的功能,防止多个事务同时修改同一行记录。每个事务启动时创建一把独占的TID锁,锁模式为X锁,并持有到事务结束。DM实现的是行级多版本,每一行记录隐含一个 TID 字段,用于事务可见性判断,协调行级的多版本并发控制。

对象锁是 DM新引入的一种锁,通过统一的对象 ID 进行封锁,将对数据字典的封锁和表锁合并为对象锁,以达到减少封锁冲突、提升系统并发性能的目的。表锁表锁用来保护表数据的完整性。DML语句执行时,对表进行上锁,协调表级的并发访问。隐含上IS,IX锁,X锁(列存储表)。字典锁防止在DML/DDL的过程中,访问对象的定义被修改。四种锁类型均采用。

四、如何解除死锁与阻塞

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

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

 五、死锁

死锁是两个事务都在等待对方持有的资源锁,要等对方释放持有的资源锁之后才能继续工作,他们互不相让,坚持到底,双方都要等到对方完成之后才能继续工作,而以目前这种状态,双方都完成不了,陷入死循环了。

5.1 遇到死锁怎么办?

数据库的机制是当发生有死锁时会牺牲掉其中的一个进程来让其它进程继续执行下去。

这种情况是应用程序BUG产生的,需要调整程序的逻辑结构。在对多表进行操作的时候,尽量按照相同的顺序进行处理,避免同时锁定两个资源,必须同时锁定两个资源的时候,要保证在任何时候都应该按照相同的顺序来锁定资源。

select o.name,l.* from v$lock l,sysobjects o where l.table_id=o.id and blocked=1;

用这个语句:定位锁头

select distinct(wait_for_id) from v$trxwait where wait_for_id not in (select id from v$trxwait);

然后

select * from v$sessions where trx_id=883531324 ;-- (883531324 是上一句的wait_for_id)

确定是什么操作。在哪里执行的,如果是程序里面执行的,排查下程序代码,看下对应的事务有没有提交。

如果是管理工具执行的,就手动提交一下,或者杀一下会话。

sp_close_session(sess_id);--(上一句的sess_id)

由于死锁会被数据库系统自动识别并终止,所以从会话视图中是感受不到的,程序会反馈一个死锁错误,通过死锁历史动态视图V$DEADLOCK_HISTORY,可以查询到发生过的死锁信息。

select * from V$DEADLOCK_HISTORY;

select dh.trx_id,sh.sess_id,wm_concat(top_sql_text) from V$DEADLOCK_HISTORY dh,V$SQL_HISTORY shwhere dh.trx_id=sh.trx_id and dh.sess_id=sh.sess_idgroup by dh.trx_id,sh.sess_id

通过事务ID去sql日志中查找对应的事务,确定完整的事务信息。根据完整的事务信息和死锁规则,去找其他影响的事务操作,确定业务功能设计,从而优化处理逻辑,消除或者降低死锁频率。

六、阻塞

 阻塞是第一个事务占有资源没有释放,而第二个事务需要获取这个资源。如果第一个事务没有提交或者回滚,第二个事务会一直等待下去,直到第一个事务释放该资源为止。

6.1遇到阻塞怎么办?

被阻塞的事务会一直挂起,直到持有锁的事务放弃锁定的资源为止(提交/回滚)。

这种情况是应用程序BUG产生的,需要调整程序的逻辑结构,尽量形成短事务,快速提交,避免阻塞时间过长。不要将其他无关操作放到容易引起阻塞的模块。

查询阻塞语句:

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;

锁查询

select * from v$lock ;

当出现锁占用时,查找此对象上的非共享锁,根据TRX_ID在V$SESSIONS视图中查找具体会话,便可以找到具体占用锁的会话,用SP_CLOSE_SESSION(SESS_ID);可关闭会话

阻塞查询

with locks as 
     ( select o.name, 
             l.*, 
             s.sess_id, 
             s.sql_text, 
             s.clnt_ip, 
             s.last_send_time 
        from v$lock l, 
             sysobjects o, 
             v$sessions s 
       where l.table_id=o.id 
         and l.trx_id=s.trx_id 
     ) 
     , 
     lock_tr as 
     ( select trx_id wt_trxid,row_idx blk_trxid from locks where blocked=1 
     ) 
     , 
     res as 
     ( select sysdate stattime, 
             t1.name, 
             t1.sess_id wt_sessid, 
             s.wt_trxid, 
             t2.sess_id blk_sessid, 
             s.blk_trxid, 
             t2.clnt_ip, 
             SF_GET_SESSION_SQL(t1.sess_id) fulsql, 
             datediff(ss,t1.last_send_time,sysdate) ss, 
             t1.sql_text wt_sql 
        from lock_tr s, 
             locks t1, 
             locks t2 
       where t1.ltype='OBJECT' 
         and t1.table_id<>0 
         and t2.ltype='OBJECT' 
         and t2.table_id<>0 
         and s.wt_trxid=t1.trx_id 
         and s.blk_trxid=t2.trx_id 
     ) 
select distinct wt_sql,clnt_ip,ss,wt_trxid,blk_trxid from res;






 

更多推荐