一、什么是阻塞?

阻塞是指障碍而不能通过,无法畅通

阻塞通常形容多线程间的相互影响,比如一个线程占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中进行等待,等待会导致线程挂起。这种情况就是阻塞。如果这个线程不释放资源,那么其他所在阻塞在这个临界区上的线程都不能工作。

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

遇到阻塞怎么办?

  1. 被阻塞的事务会一直挂起,直到持有锁的事务放弃锁定的资源为止(提交/回滚)
  2. 这种情况是应用程序BUG产生的,需要调整程序的逻辑结构,尽量形成短事务,快速提交,避免阻塞时间过长。不要将其他无关操作放到容易引起阻塞的模块

二、什么是死锁?

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

遇到死锁怎么办?

  1. 数据库的机制是当发生有死锁时牺牲掉其中的一个进程来让其他进程继续执行下去
  2. 应用程序BUG产生的,需要调整程序的逻辑结构,在对多表进行操作的时候,尽量按照形同的顺序进行处理,避免同时锁定两个资源,必须同时锁定两个资源的时候,要保证在任何时候都应该按照相同的顺序来锁定资源。

三、数据库相关定义

java 达梦数据库 表加锁和释放锁SQL_达梦数据库

阻塞

模拟阻塞场景

建表sql

create table lock_test01(id int primary key, name varchar(20));
create table lock_test02(id int primary key, name varchar(20));

insert into lock_test01(id, name) values(1, '1cheng');
insert into lock_test01(id, name) values(2, '1gao');

insert into lock_test02(id, name) values(1, '2cheng');
insert into lock_test02(id, name) values(2, '2gao');

阻塞场景

-- Session A 执行insert 不提交
insert into "SYSDBA"."LOCK_TEST01"("ID", "NAME")  VALUES(3, '3zzzz');
-- Session B 执行insert  会发生阻塞
insert into "SYSDBA"."LOCK_TEST01"("ID", "NAME")  VALUES(3, '4zzzz');

解决阻塞方法

解决阻塞(一)
/*定位锁等待问题 */
--1. 查看被挂起的事务(TRX_ID)
SELECT VTW.ID AS TRX_ID, VS.SESS_ID,VS.SQL_TEXT,VS.APPNAME,VS.CLNT_IP FROM V$TRXWAIT VTW LEFT JOIN V$TRX VT ON
(VTW.ID=VT.ID) LEFT JOIN V$SESSIONS VS ON (VT.SESS_ID=VS.SESS_ID);
--2. 通过挂起事务ID(TRX_ID)找到它等待的事务(WAIT_FOR_ID)。
SELECT WAIT_FOR_ID,WAIT_TIME FROM V$TRXWAIT WHERE ID=321646; 
--3. 通过等待事务ID(WAIT_FOR_ID)定位到连接以及执行的语句
SELECT VT.ID AS TRX_ID,VS.SESS_ID,VS.SQL_TEXT,VS.APPNAME,VS.CLNT_IP FROM
V$TRX VT LEFT JOIN V$SESSIONS VS ON (VT.SESS_ID=VS.SESS_ID) WHERE
VT.ID = 321643;
--4. 杀掉会话
--SP_CLOSE_SESSION关闭等待事务(SESS_ID)
SP_CLOSE_SESSION(142344256);
解决阻塞(二)
--查询被阻塞的信息和引起阻塞的信息
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,
        '引起阻塞的信息' EM,
        s2.SESS_ID EM_SESS_ID,
        S2.SQL_TEXT EM_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;

--2. 杀掉会话
SP_CLOSE_SESSION(142344256);

阻塞的发生场景

  1. 当多个事务同时试图向有主键或 UNIQUE 约束的表中插入相同的数据时,其中的一个事务将被阻塞,直到另外一个事务提交或回滚。
  2. 当 UPDATE 和 DELETE 修改的记录,已经被另外的事务修改过,将会发生阻塞,直到另一个事务提交或回滚。
  3. 同一个事务中先update语句(发生堵塞),在select查询时也会被堵塞。
  4. 进行update更新时,是范围更新(where id <10),另一个session进行更新时是其中的数据,也会发生阻塞

死锁

模拟死锁场景

-- 按照序号执行
-- 1. session A执行 update id = 1 ,事务不提交
update LOCK_TEST01 set  "NAME" = 'deadlock1' where id =1;
-- 3. session A执行 update id = 2 ,此时会发生阻塞,等待session B 释放
update LOCK_TEST01 set  "NAME" = 'deadlock2' where id =2;

-- 2. session B执行 update id = 2 ,事务不提交
update LOCK_TEST01 set  "NAME" = 'deadlock2' where id =2;
-- 4. session B执行 update id = 1 ,此时发生死锁
update LOCK_TEST01 set  "NAME" = 'deadlock1' where id =1;

java 达梦数据库 表加锁和释放锁SQL_阻塞_02

解决死锁方法

查询死锁历史视图
-- 查询死锁历史记录
select * from V$DEADLOCK_HISTORY;

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

四、相关视图

select * from v$trx ;

-- 查询数据库会话
select * from v$sessions;
-- 查询阻塞锁
select * from v$lock where BLOCKED = 1;
-- 查询锁等待
select * from v$trxwait;
-- 查询死锁历史记录
select * from V$DEADLOCK_HISTORY;

-- 查询等待事件  total_waits 次数
select * from v$system_event