2、并行插入相同主键记录
我们尝试构建一个同时插入主键相同的记录,看看Oracle是如何处理这样的情况。这两条数据就是相关数据,因为正常条件下,两条数据是无法并存的。
//session1(sid=158)中
SQL> insert into t values (1,'ddd');
已创建1 行。
//session2(sid=141)中
SQL> insert into t values (1,'ddddfsf');
(session2被阻塞)
发生了阻塞!session2因为插入了id=1的数据,但是之前已经存在id=1的记录,即使这条记录还没有被commit。
//查看现象
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME OS_USER_NAME PROCESS LOCKED_MODE
---------- ---------- ---------- ---------- ---------- ------------------------------ ------------------------------ ------------ -----------
8 19 891 53606 141 SCOTT IBM-VS2A1BHCNS0\ibm 772:3932 3
6 17 840 53606 158 SCOTT IBM-VS2A1BHCNS0\ibm 636:1036 3
SQL> select * from v$lock where sid in (141,158);
ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- ---- -------------------
6C8344A8 6C8344BC 141 TX 393233 840 0 4 96 0
6BDC4074 6BDC408C 158 TM 53606 0 3 0 129 0
6BDC4138 6BDC4150 141 TM 53606 0 3 0 96 0
6BE185A8 6BE186C4 158 TX 393233 840 6 0 129 1
6BE29A54 6BE29B70 141 TX 524307 891 6 0 96 0
SQL> select sid, serial#, lockwait, sql_id, blocking_session_status,EVENT#, EVENT
2 from v$session where sid in (141,158);
SID SERIAL# LOCKWAIT SQL_ID BLOCKING_SESSION_STATUS EVENT# EVENT
---------- ---------- -------- ------------- ----------------------- ---------- ----------------------------------------------------------------
158 47 NO HOLDER 256 SQL*Net message from client
141 122 6C8344BC 515n1g3av68fcVALID 183enq: TX - row lock contention
首先,我们分析一下v$locked_object视图。两个会话session分别在undo表空间分配了相应的空间,准备存放undo数据镜像。并且给这两个段加入了共享锁(Lmode=3)。
v$lock视图变得略有复杂。首先,我们观察session1(sid=158)的情况。session1的情况和上面没有多少区别。现在数据表T上加一个共享对象锁(TM锁,Lmode=3)。之后,在Undo区(393233/840)上分配空间,保存数据镜像,并且加入了一个排他锁(TX Lmode=6)。当session1(sid=158)完成之后,session2(sid=141)尝试将相同id的数据插入到数据表中。首先,先给对象T加入一个共享锁对象(TM锁,LMODE=3)。之后,session2尝试给已经为session1排他(lmode=6)的undo空间(393233/840),加一个TX锁,类型为lmode=4(共享锁类型),要求共享undo空间(393233/840)。这样,session1和session2在这个undo空间上存在了blocking,因为lmode4和6之间不能并存。于是,session2被blocking。
此外,session2为了进行insert操作,还分配了一个新的undo空间(53606 0),实行lmode=3的事务锁。
从会话v$session等待情况看,会话2在等待事件“enq: TX - row lock contention”,等待session1释放数据行锁。
结论:当两个会话在同时新增加一个记录的时候,如果输入的主键相同,一个会话会发生blocking。此时,Oracle是难以做决定,前一个会话如果被rollback,第二个事务不存在问题。如果前一个会话提交,第二个事务对应数据行就是非法的数据。在这种情况下,Oracle只能是blocking一个会话了。
3、同时修改相同一条记录
当我们尝试修改一条相同的记录时,会发生什么呢?
//session1(sid=158)
SQL> update t set name='dfs' where id=1;
已更新1 行。
SQL>
//session2(sid=141)
SQL> update t set name='fs' where id=1;
(阻塞)
出现了阻塞blocking。我们观察一下锁的情况。
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID PROCESS LOCKED_MODE
---------- ---------- ---------- ---------- ---------- ------------ -----------
0 0 0 53606 141 772:3932 3
6 16 840 53606 158 636:1036 3
SQL> select * from v$lock where sid in (141,158);
ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- ---- -------------------
6C8344A8 6C8344BC 141 TX 393232 840 0 6 42 0
6BDC4074 6BDC408C 158 TM 53606 0 3 0 87 0
6BDC4138 6BDC4150 141 TM 53606 0 3 0 42 0
6BE185A8 6BE186C4 158 TX 393232 840 6 0 87 1
SQL> select sid, serial#, lockwait, sql_id, blocking_session_status,EVENT#, EVENT
2 from v$session where sid in (141,158);
SID SERIAL# LOCKWAIT SQL_ID BLOCKING_SESSION_STATUS EVENT# EVENT
---------- ------ -------------------------------------
141 47 6C8344BC fgjfccu4as8xv VALID 83 enq: TX - row lock contention
158 122 NO HOLDER 256 SQL*Net message from client
从视图情况下,虽然在修改相同数据行的时候,都是后一个session2被阻塞住。但是,在加锁方式上存在一些差异。这点从v$lock视图上可以分析出。
先看v$locked_object,session2(sid=141)的undo空间锁是没有建立的,前三个数据列为0。
v$lock视图中,session2发起了两个锁,一个是对数据表的共享锁TM。另一个锁是对session1所开启的undo段进行行排他(Lmode=6)要求。因为其上已经存在session1的排他,两者不能并存。于是,session2被阻塞住。
结论:当两个会话在同时修改一个数据时。对共享资源的占用就是第一个会话undo段空间对象。后一个session会被blocking。
4、删除相同的数据
最后我们看看删除数据时候的情况。
//session1(sid=149)
SQL> conn scott/tiger@orcl;
已连接。
SQL> delete t where id=1;
已删除1 行。
SQL> select sid from v$mystat where rownum<2;
SID
----------
149
//session2(Sid=156)
SQL> conn scott/tiger@orcl
已连接。
SQL> select sid from v$mystat where rownum<2;
SID
----------
156
SQL> delete t where id=1;
删除相同的数据时,被blocking。
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME OS_USER_NAME PROCESS LOCKED_MODE
---------- ---------- ---------- ---------- --------- -----------
8 22 896 53606 149 SCOTT IBM-VS2A1BHCNS0\ibm 3752:588 3
0 0 0 53606 156 SCOTT IBM-VS2A1BHCNS0\ibm 1428:3376 3
SQL> select * from v$lock where sid in (149,156);
ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- ---- ----------
6C8344A8 6C8344BC 156 TX 524310 896 0 6 130 0
6BDC4074 6BDC408C 149 TM 53606 0 3 0 309 0
6BDC4138 6BDC4150 156 TM 53606 0 3 0 130 0
6BE23C64 6BE23D80 149 TX 524310 896 6 0 309 1
SQL> select sid, serial#, lockwait, sql_id, blocking_session_status,EVENT#, EVENT
2 from v$session where sid in (149,156);
SID SERIAL# LOCKWAIT SQL_ID BLOCKING_SESSION_STATUS EVENT# EVENT
---------- ---------- -------- ------------- ----------------------- ---------- ----------------------------------------------------------------
149 381 NO HOLDER 256 SQL*Net message from client
156 493 6C8344BC g2pvqwhq0pwjc VALID 183 enq: TX - row lock contention
删除的特征和修改相同,都是两个会话争用这个数据行对应的锁,之后发生锁互斥现象,引发blocking。
结论:在删除两条相同数据行的情况下,是可能发生blocking的。
综合上面的研究,我们可以发现。Oracle采用的多版本一致度和事务行锁机制,最大限度的保证了并行特性发挥。但是,存在并行、存在资源独占使用,就存在锁机制,有锁机制就意味着总会有访问被串行化。但是,不得不承认,Oracle在锁机制上,已经做到了相当小的范围。