当我们进行日常巡检或是用户反馈执行SQL语句时卡死或是应用运行缓慢时,首先会想到,在数据库中有没有阻塞的发生,然后找到它,记录它,杀掉它。让应用恢复正常,再来分析SQL的合理性并去优化它。
执行某条DML语句,发现卡住,很长时间都没有执行完成,比如如下SQL:
update test set c1=3; --会话1

猜测可能是某个事务未提交导致相关表锁住,阻塞了本事务的执行,导致等待。
一般运维人员都是从这步开始的,查询v$trxwait视图,发现事务69666被事务69667阻塞了,已经阻塞了312秒。

根据WAIT_FOR_ID,事务号69667查询哪个会话导致的阻塞问题。

通过查询可以看到对应会话的SQL_TEXT显示是一条select语句(会话2),并且该会话的状态还是空闲状态(STATE=‘IDLE’),是不是比较疑惑,update为什么会被一个select语句阻塞呢?带着疑问我们可以查询下相关事务锁的信息。
select * from v$lock where trx_id=69667;

可以看到该事务存在表id为1100的IX锁和TID的X锁,表明table_id=1100的表进行了数据修改。查询对应ID号的表信息。
select * from sysobjects where id=1100 ;

可以看到被上锁的表为TEST。分析到这里我们可以确认trx_id=69667的事务应该是一个混合操作事务,该事务做了数据修改后没有提交,然后又执行了其他的操作(一个事务中可以执行多条SQL),这也就解释了为什么update(会话1)会被select(会话2)SQL语句阻塞,真正阻塞的罪魁祸首应该是与这个select语句在同一事务中的其他修改数据操作。
cat dmsql_DMSERVER_20230110_145721.log | grep “trxid:69667”

注:根据会话2对应的last_recv_time时间,查询事务69667在此时间前执行了哪些SQL。使用日志查询需要开启日志记录。如果日志刷新很快,有很多日志都包含这个事务,
cat dmsql_DMSERVER_20230110_14*.log | grep “trxid:69667” > tmp/sql.log
sp_close_session(139926142266440); --sid
在2.2步查询会话时获得
注:杀掉会话前记得记录问题SQL以及会话的相关信息。
开启SVR_LOG:vi dm.ini --修改如下SVR_LOG=1,执行
SP_REFRESH_SVR_LOG_CONFIG();
--刷新生效
或者执行
SP_SET_PARA_VALUE(1,‘SVR_LOG’,1);
通过修改SVR_LOG的参数为1,
来开启SQL日志功能。
开启svr_log后,默认会在dm.ini的存放目录下生成一个sqllog.ini文件
vi sqllog.ini --具体配置请参考官方文档,包含日志文件存放目录、大小、个数等。
当用户发生执行某条DML语句长时间异常等待时,可以查询是否发生阻塞。类似场景如下,当进行UPDATE或DELETE时,相关对象如果已经被其他的事务修改过,将会发生阻塞直到其他的事务提交或回滚。
经过上述分析,我们可以怀疑与这个select语句在同一事务中还有一个update或delete等操作没有提交。如何查找这个问题SQL,v$sessions中可能看不出来,我们可以通过sql日志去查看(需要开启日志记录功能),根据会话2对应的last_recv_time,我们在sql日志这个时间往前搜索trx_id=69667的事务包含了哪些操作。从而可以告知客户发生问题的根本原因。
最后,查出问题SQL语句并记录相关会话信息后,需要杀掉问题会话,即可解除阻塞。如果是DSC集群,杀会话时需要在相应的问题SQL执行的节点执行sp_close_session(sid)语句。
ZONG JIE
【SQL阻塞排查步骤】
(单机和集群都适用)
select * from v$trxwait order by wait_time desc;
--单机
select * from v$dsc_trxwait order by wait_time desc;
–DSC集群
select sf_get_session_sql(sess_id),* from v$sessions where trx_id=69667;
--单机
select sf_get_session_sql(sess_id),* from gv$sessions where trx_id=69667;
--DSC集群
cat dmsql_xxxxxx.log | grep “trxid:69967”
–查找问题DML SQL,保留
注:需要开启SVR_LOG参数
sp_close_session(139926142266440);
注:对于DSC,需要在相应的节点执行。
其他SQL:
select * from v$lock where trx_id=69667;
–--可用于查询阻塞事务的相关锁信息
阻塞和死锁是会与并发事务一起发生的两个事件,它们都与锁相关。当一个事务正在占 用某个资源的锁,此时另一个事务正在请求这个资源上与第一个锁相冲突的锁类型时,就会 发生阻塞。被阻塞的事务将一直挂起,直到持有锁的事务放弃锁定的资源为止。死锁与阻塞 的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。
在DM数据库中,INSERT、UPDATE、DELETE 是最常见的会产生阻塞和死锁的语句:
(1) INSERT 发生阻塞的唯一情况是,当多个事务同时试图向有主键或 UNIQUE 约束的表中 插入相同的数据时,其中的一个事务将被阻塞,直到另外一个事务提交或回滚。一个事务提交时,另一个事务将收到唯一性冲突的错误;一个事务回滚时,被阻塞的事务可以继续执行。
(2) 当 UPDATE 和 DELETE 修改的记录,已经被另外的事务修改过,还未commit时,将会发生阻塞,直到另一个事务提交或回滚。
新的一年,干货栏目会一如继往的给大家分享达梦数据库的相关知识。如果大家在工作中有好的想法也可以整理成文章发给我们,我们将在公众号分享给大家。新年伊始,让我们一起来学习达梦吧。

原文:引用自达梦在线体验平台,具体请点击“阅读原文”
编辑:crossrainbow
排版:哈哈




