阻塞概述
定义:阻塞指有障碍而不能通过,无法畅通
产生原因:通常形容多线程间的相互影响,比如一个线程占用了临界区资源,那么其它所有需要这个资源的线程就必须在这个临界区中进行等待,等待会导致线程挂起,这种情况就是阻塞。此时,如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞在这个临界区上的线程都不能工作。
死锁与阻塞
阻塞是第一个事务占有资源没有释放,而第二个事务需要获取这个资源。如果第一个事务没有提交或者回滚,第二个事务会一直等待下去,直到第一个事务释放该资源为止。
遇到阻塞怎么办?
-
被阻塞的事务会一直挂起,直到持有锁的事务放弃锁定的资资源为止(提交/回滚)
-
这种情况是应用程序BUG产生的,需要调整程序的逻辑结构,尽量形成短事务,快速提交,避免阻塞时间过长。不要将其他无关操作放到容易引起阻塞的模块。
死锁是两个事务都在等待对方持有的资源锁,要等对方释放持有的资源锁之后才能继续工作,他们互不相让,坚持到底,双方都要等到对方完成之后才能继续工作,而以目前这中状态,双方都完成不了,陷入死循环了。
遇到死锁怎么办?
-
数据库的机制是当发生有死锁时会牺牲掉其中的一个进程来让其它进程继续执行下去。
-
这种情况是应用程序BUG产生的,需要调整程序的逻辑结构。在在对多表进行操作的时候,尽量按照相同的顺序进行处理,避免同时锁定两个资源,必须同时锁定两个资源的时候,要保证在任何时候都应该按照相同的顺序来锁定资源。
死锁检测与解决:
数据库系统通常会使用死锁检测机制来识别和解决死锁。当系统检测到死锁时,通常会采取以下策略之一:
-
主动回滚:系统选择一个事务回滚(通常是代价较小的事务),以打破死锁循环。
-
超时回滚:如果一个事务等待的时间超过某个阈值,系统会认为可能发生了死锁,并回滚该事务。
如何排查死锁
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 sh where dh.trx_id=sh.trx_id and dh.sess_id=sh.sess_id group by dh.trx_id,sh.sess_id;
由于死锁会被数据库系统自动识别并终止,所以从会话视图中是感受不到的,程序会反馈一个死锁错误,通过死锁历史动态视图V$DEADLOCK_HISTORY,可以直询到发生过的死比锁信息
通过事务ID去sql日志中查找对应的事务,确定完整的事务信息。
根据完整的事务信息和死锁规则,去找其他影响的事务操作,确定业务功能设计,从而优化处理逻辑,消除或者降低死锁频率。
通过事务ID去 sql日志中查找对应的事务,确定完整的事务信息。和查询中被阻塞的事务sql进行对照确认
如何解除死锁与阻塞
死锁不用人工干涉,数据库系统自动识别并解除。但是死锁是非正常情况,需要找到死锁原因后,从应用
逻辑层解决。
阻塞可能是应用流程涉及提交慢导致,但是最终也会提交或者回滚,所以一般情况下也会自动消除。如果
阻塞很长时间没有消除,在数据库层可以通过SP_CLOSE_SESSION(SESS_ID);来强制终止阻塞源头的会话,
使其回滚,来解除阻塞,但是这个需要用户确认,不能私自操作
如何跟踪是否有阻塞发生了
SELECT * FROM V$SYSTEM_EVENT ORDER BY TIME_WAITED DESC; --丛系统时间中可以查看锁等待事件和事件发生的次数和时间来评估是否存在死锁和阻塞
创建辅助记录表
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) );
创建辅助存储过程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;
创建作业定时收集阻塞数据
call SP_CREATE_JOB('GETTRXWAIT‘,1,0,",0,0,",0,"); call SP_JOB_CONFIG_START('GETTRXWAIT'); call SP_ADD_JOB_STEP('GETTRXWAIT', 'GETTRXWAIT', O, 'GETTTRXWAIT, 1, 2, 0, 0, NULL, 0); call SP_ADD_JOB_SCHEDULE('GETTRXWAIT', 'GETTRXWAIT, 1,1, 1, 0, 1, 00:00:00', 23:59:59', '2024-08-08 23:08:36', NULL, "); call SP_JOB_CONFIG_COMMIT('GETTRXWAIT');
达梦社区地址:https://eco.dameng.com