“Oracle两个同时进行的insert不会被阻塞…..”
Oracle是目前支持并行操作处理最好的数据库系统。在开始提供商业化数据库产品的阶段,事务行锁和多版本一致读的特性就奠定了Oracle商业数据库领军的地位。这两个特性的最终目的就是提高数据库系统的并发操作能力。
并发操作可以最大限度的提升系统的整体性能,但是也必然引起资源的公用。无论在数据库系统,还是在操作系统等其他系统中,资源的共享与互斥必然回引入锁技术。过小的范围锁不能满足资源共享保护需求,过大的范围锁又会限制并行特性的发挥。
多版本一致读
在Oracle中,一般性select读操作是不会阻塞其他dml操作的。同时,在进行dml操作的时候,也不会影响到select操作获取数据。举例来说:当一个会话session1进行数据表数据dml操作的时候,没有进行commit/rollback操作。另一个会话session2是可以进行select操作,获取的数据是session1进行修改之前的数据。
当session1提交了事务之后,所有会话select的数据就是提交之后的数据集合。整个过程中,所有其他会话发出的select操作是不会因为session1的事务而被阻塞。
事务行级锁
事务行级锁是Oracle的另一个出色特性。当进行事务的时候,为了防止其他会话对数据进行修改,不同的数据库系统是采用不同的控制方法的。通常的做法有数据表级锁、页级锁等。但是这些类型锁都不免将锁定资源的范围扩大化。Oracle提供了事务行级锁,每个事务只会锁定该事务修改的数据行,不会影响到其他与事务无关的数据。一些资料中,将Oracle这个特性也叫行级锁,很多人理解为Oracle在每个数据行进行加锁,这实际上是不准确的。因为即使我们修改了多行,从Oracle的角度看,也只是加了一个锁,锁住了多行数据集而已。
那么,回到开篇的“Oracle两个同时进行的insert不会被阻塞…..”,这种说法对吗?下面我们分别进行一系列的实验,来证明这种说法。
下面是环境准备。
SQL> conn scott/tiger@orcl;
已连接。
SQL> create table t (id number(10), name varchar2(20));
表已创建。
SQL> alter table t add constraint pk_t_id primary key (id);//确立主键
表已更改。
1、无关数据行插入
首先我们同时使用两个会话,进行无关数据行的插入。
//session1
SQL> conn scott/tiger@orcl;
已连接。
SQL> select sid from v$mystat where rownum<2;
SID
----------
158
SQL> insert into t values (1,'ddd');
已创建1 行。
首先,启动了一个会话(sid=158),下面开启另一个会话。
//session2
SQL*Plus: Release 10.2.0.1.0 - Production on 星期六 2月 19 20:15:16 2011
Copyright (c) 1982, 2005, Oracle. All rights reserved.
SQL> conn scott/tiger@orcl;
已连接。
SQL> select sid from v$mystat where rownum<2;
SID
----------
141
SQL> insert into t values(2,'dkl');
已创建1 行。
我们发现,启动两个会话,同时开启数据库事务,插入两条无关数据,不会发生阻塞。注意:我们强调的是无关数据,就是两条数据可以在数据表中提交并存。
此时,我们观察一下系统的锁视图情况。
//锁状态
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME OS_USER_NAME PROCESS LOCKED_MODE
---------- ---------- ---------- ---------- ---------- ------------------------------ ------------------------------ ------------ -----------
4 45 870 53606 141 SCOTT IBM-VS2A1BHCNS0\ibm 772:3932 3
3 19 842 53606 158 SCOTT IBM-VS2A1BHCNS0\ibm 636:1036 3
//object_id=53606对应数据表T
SQL> select * from dba_objects where object_id=53606;
OWNER OBJECT_NAME
------------------------------ ---------------------
SCOTT T
//锁状态检查
SQL> select * from v$lock where sid in (141,158);
ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- ---- ---------------
6BDC4074 6BDC408C 158 TM 53606 0 3 0 226 0
6BDC4138 6BDC4150 141 TM 53606 0 3 0 214 0
6BE185A8 6BE186C4 158 TX 196627 842 6 0 226 0
6BE29A54 6BE29B70 141 TX 262189 870 6 0 214 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
---------- ---------- -------- ------------- ----------------------- ---------- ----------------------------------------------------------------
141 47 NO HOLDER 256 SQL*Net message from client
158 122 NO HOLDER 256 SQL*Net message from client
额外说明:视图v$locked_object前三个数据列,xidusn为进行dml操作时候,使用undo段的编号,xidslot为段对象的slot编号,xidsqn为段对象的系列号。三个字段表示了对象在undo表空间上的对应位置。对应的xidsqn对应v$lock锁的id2属性。
从v$lock视图的情况下,我们不难发现并行插入的过程。首先,两个会话在数据表T上加一个表级别(TM)的共享锁(LMODE=3)。LMODE=3是可以共享的,在数据表上加共享锁的目的就是防止在事务进行中,尝试对数据表结构进行一些修改。同时,在不同的数据行,发起事务锁(TX),模式为独占模式。分别排他锁住两个数据行。这两个会话事务结构之间不会发生阻塞情况。
那么,在这样的方式下,是不是不会发生blocking类型的阻塞?下面我们进行一些其他实验。