1. 背景
在项目实际运行中,可能由于程序逻辑冲突或者人为操作 导致多个会话同时对一张表的同一条数据进行修改( 即第一个连接占有资源没有释放,而第二个连接需要获取这个资源),如果第一个连接没有提交或者回滚,第二个连接会一直等待下去,这就产生了阻塞。对于阻塞,数据库无法处理,所以对数据库操作要及时地提交或者回滚;
另一种情况:第一个连接占有资源没有释放,准备获取第二个连接所占用的资源,而第二个连接占有资源没有释放,
准备获取第一个连接所占用的资源。 这种互相占有对方需要获取的资源的现象叫做死锁。
对于死锁,数据库处理方法:牺牲一个连接,保证另外一个连接成功执行。
2. 目标
通过本地模拟阻塞和死锁场景,查询动态视图,能够快速定位阻塞问题。
3. 模拟阻塞场景
3.1 创建测试表和数据
create table t1_deadlock ( a int );
create table t2_deadlock ( a int );
insert into t1_deadlock values ( 1 );
insert into t2_deadlock values ( 2 );
commit ;
3.2 创建阻塞事务
会话1执行:修改t1表,不提交
会话2执行,修改t2表,不提交
回到会话1,修改t2表,不提交,
此时产生阻塞
来到会话2,修改t1表:
此时产生死锁
注意:达梦在出现死锁后,会自动kill掉死锁的会话(这里是会话2),然后会话1仍处于阻塞状态,需要手动提交或回滚。
4.相关视图查询
此时即产生了阻塞,查询 v$trx可以看到相关信息:
select status,ID,sess_id from v$trx where status = 'LOCK WAIT';
将 sess_id 放到会话视图中查,可以找被阻塞的SQL:
同时,查询v$lock可以看到与锁有关的信息:
select o.name,l.* from v$lock l,sysobjects o where l.table_id=o.id and blocked=1;
其中 TRX_ID,TID 都对应的是v$sessions中的TRX_ID:
下面是排查阻塞常用的SQL语句:
--查询阻塞源头
select sf_get_session_sql(sess_id)as 阻塞源SQL,
datediff(ss,last_recv_time,sysdate) as "阻塞持续时长(秒)",
TRX_ID,*
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 a.id as trx_id,a.sess_id,a.status 事务状态,b.sql_text,a.waiting 事务等待的锁,
b.CLNT_IP 客户端IP,b.state 会话状态
from v$trx a
left join v$sessions b on a.sess_id = b.sess_id
where a.status = 'LOCK WAIT' ;