oracle锁问题模拟实验

1.行级死锁。
1.1主键、唯一索引的死锁(会话交叉插入相同的主键值)
a.新建一张表,设置主键(或创建唯一索引)后插入一个值,然后不要COMMIT,另一个会话插入另一个值,也不要COMMIT,然后再把这两个插入的值互相交换一下,在两个会话中分别插入,死锁就会产生。
oracle锁问题模拟实验_第1张图片
因为过程简单,直接上图了,我以scott用户开了会话。1会话建表t_deadlock,插入第一条数据不提交,此时在2会话中插入第二条,不提交。再继续插入1会话中对应值。模拟产生的阻塞。

b.PLSQL下查询:

SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,

       A.BLOCKING_SESSION,

       A.SID,

       A.SERIAL#,

       A.LOGON_TIME,

       A.EVENT

  FROM GV$SESSION A

 WHERE A.SID IN (20,74)

 ORDER BY A.LOGON_TIME;

oracle锁问题模拟实验_第2张图片
可以看到2的会话BLOCKING(阻塞)了sid20的会话。

c.此时在1会话中继续插入2会话第一次插入的值,模拟死锁。
oracle锁问题模拟实验_第3张图片
如图:2会话报ORA-00060 deadlock。

d.继续查阻塞情况。
oracle锁问题模拟实验_第4张图片
e.查alert日志
在这里插入图片描述
f.查trc文件
oracle锁问题模拟实验_第5张图片
Oracle对于死锁有自己的内部处理机制会。当看到trace文件时,需要确认一下产生锁的类型,是两行还是一行,是TX还是TM,如果只有一行那么说明是同一个SESSION,可能是自治事务引起的死锁。

g.继续提交2会话
oracle锁问题模拟实验_第6张图片
会话2执行提交后,会话1就会报错,违反唯一约束。这里体现了ORACLE解除死锁。9I以后Oracle检查到60错误,会自动解锁,并记录trace。
附图
在这里插入图片描述
1.2 外键的死锁(外键未加索引)
外键未加索引很容易导致死锁。在以下两种情况下,Oracle在修改父表后会对子表加一个全表锁:

  1. 如果更新了父表的主键,由于外键上没有索引,所以子表会被锁住。

  2. 如果删除了父表中的一行,由于外键上没有索引,整个子表也会被锁住。
    总之,就是更新或者删除父表的主键,都会导致对其子表加一个全表锁。
    外键的死锁可以这样通俗的理解:有两个表A和B:A是父表,B是子表。如果没有在B表中的外键加上索引,那么A表在更新或者删除主键时,都会在表B上加一个全表锁。这是为什么呢?因为我们没有给外键加索引,在更新或者删除A表主键的时候,需要查看子表B中是否有对应的记录,以判断是否可以更新删除。那如何查找呢?当然只能在子表B中一条一条找了,因为我们没有加索引吗。既然要在子表B中一条一条地找,那就得把整个子表B都锁定了。由此就会导致以上一系列问题。
    模拟:
    建t_deadlock_p 主表
    oracle锁问题模拟实验_第7张图片
    插入对应值
    oracle锁问题模拟实验_第8张图片
    建立子表t_d_f 并插入值。
    oracle锁问题模拟实验_第9张图片
    1会话执行相关删除操作,给子表和父表第一行加行级锁。
    oracle锁问题模拟实验_第10张图片
    查看锁情况,此时没有锁。
    oracle锁问题模拟实验_第11张图片
    会话2执行另一个删除操作,子表、父表加行级锁。
    oracle锁问题模拟实验_第12张图片
    由于第一次删除主表语句时候,触发了对子表的全表扫描,对子表加了表级锁,故第二个主表删除语句出现等待。
    oracle锁问题模拟实验_第13张图片
    BLOCK为1表示阻塞了其它的锁。
    此时在会话1下删除id为2的记录,2会话报死锁了。
    oracle锁问题模拟实验_第14张图片
    查看阻塞情况
    oracle锁问题模拟实验_第15张图片
    trc文件对锁的类型都交代清楚了。
    oracle锁问题模拟实验_第16张图片
    再次模拟,先给外键加索引。
    oracle锁问题模拟实验_第17张图片
    模拟之前的删除操作,顺利完成。
    oracle锁问题模拟实验_第18张图片
    未发现阻塞会话。
    oracle锁问题模拟实验_第19张图片
    1.3位图索引死锁
    表上的位图索引遭到并发更新也很容易产生死锁。在有位图索引存在的表上面,其实很容易就引发阻塞与死锁。这个阻塞不是发生在表上面,而是发生在索引上。因为位图索引锁定的范围远远比普通的b-tree索引锁定的范围大。
    模拟,先建表并插入数据。
    oracle锁问题模拟实验_第20张图片
    在ID列上建bitmap index的话,所有ID=1的会放到一个位图中,所有ID=2的是另外一个位图,而在执行DML操作的时候,锁定的将是整个位图中的所有行,而不仅仅是DML涉及到的行。由于锁定的粒度变粗,bitmap index更容易导致死锁的发生。

    创建位图索引语句。
    在这里插入图片描述
    做更新操作,会话1:所有ID=1的行都被锁定,会话2:所有ID=2的行都被锁定oracle锁问题模拟实验_第21张图片
    会话1:此时会话被阻塞
    oracle锁问题模拟实验_第22张图片
    查看阻塞
    oracle锁问题模拟实验_第23张图片
    会话2:会话被阻塞
    oracle锁问题模拟实验_第24张图片
    此时的会话层只检测到锁等待
    oracle锁问题模拟实验_第25张图片
    但alert 检测到deadlock,这个阻塞不是发生在表上面,而是发生在索引上。
    oracle锁问题模拟实验_第26张图片
    死锁发生的根本原因是对于资源的排他锁定顺序不一致。上面的试验中,session1对于bitmap index中的2个位图是先锁定ID=1的位图,然后请求ID=2的位图,而在此之前ID=2的位图已经被session2锁定。session2则先锁定ID=2的位图,然后请求ID=2的位图,而此前ID=1的位图已经被session1锁定。于是,session1等待session2释放ID=2的位图上的锁,session2等待session1释放ID=1的位图上的锁,死锁就发生了

而如果我们创建的是普通的B*Tree index,重复上面的试验则不会出现任何的阻塞和死锁,这是因为锁定的只是DML操作涉及到的行,而不是所有ID相同的行。

1.4常见事务引发的死锁

如果你有两个会话,每个会话都持有另一个会话想要的资源,此时就会出现死锁(deadlock)。例如,如果我的数据库中有两个表A和B,每个表中都只有一行,就可以很容易地展示什么是死锁。我要做的只是打开两个会话(例如,两个SQL*Plus会话)。在会话A中更新表A,并在会话B中更新表B。现在,如果我想在会话B中更新表A,就会阻塞。会话A已经锁定了这一行。这不是死锁;只是阻塞而已。因为会话A还有机会提交或回滚,这样会话B就能继续。如果我再回到会话A,试图更新表B,这就会导致一个死锁。要在这两个会话中选择一个作为“牺牲品”,让它的语句回滚。

想要更新表B的会话A还阻塞着,Oracle不会回滚整个事务。只会回滚与死锁有关的某条语句。会话B仍然锁定着表B中的行,而会话A还在耐心地等待这一行可用。收到死锁消息后,会话B必须决定将表B上未执行的工作提交还是回滚,或者继续走另一条路,以后再提交。一旦这个会话执行提交或回滚,另一个阻塞的会话就会继续,好像什么也没有发生过一样。

模拟一.两个表之间不同顺序相互更新操作引起的死锁

模拟二:同一张表删除和更新之间引起的死锁

1.5自治事务引发的死锁
一般来说构成死锁至少需要两个会话,而自治事务是一个会话可能引发死锁。
自治事务死锁情景:存储过程INSERT表A,然后INSERT表B;其中INSERT表A触发TRIGGER T,T也INSERT表B,T是自治事务(AT),AT试图获取对B的锁,结果B已经被主事务所HOLD,这里会报出来ORA-00060 – 等待资源时检查到死锁.

解决方法:去掉了T中的PRAGMA AUTONOMOUS_TRANSACTION声明,保持和存储过程事务一致.

你可能感兴趣的:(oracle)