当前位置: 首页 > 图文教程 > 数据库 > Oracle > Oracle多粒度封锁机制研究(二)

Oracle
Oracle数据库技术(32)
Oracle数据库技术(33)
Oracle数据库技术(34)
Oracle数据库技术(35)
Oracle数据库技术(36)
Oracle数据安全面面观
Oracle数据操作和控制语言详解
Oracle数据库数据对象分析
解析Oracle 8i/9i的计划稳定性
使用Oracle实现实时通信
Oracle数据库中索引的维护
Oracle数据库游标使用大全
Oracle9i中监视索引的使用
在Oracle9i中使用多种Block Size
监控Oracle数据库的常用shell脚本
Performance Improvement Tips for Oracle on UNIX
Raw Partitions and Windows NT
How to use OS commands to diagnose Database Performance issues?
Raw Devices and Oracle - 20 Common Questions and Answers
Monitor Oracle Resource Consumption in UNIX

Oracle多粒度封锁机制研究(二)


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 163 ::
收藏到网摘: n/a

1.1.1v$locked_object视图

    v$locked_object视图列出当前系统中哪些对象正被锁定,其主要字段说明如下:

字段名称
 类型
 说明
 
XIDUSN
 NUMBER
 回滚段号;
 
XIDSLOT
 NUMBER
 槽号;
 
XIDSQN
 NUMBER
 序列号;
 
OBJECT_ID
 NUMBER
 被锁对象标识;
 
SESSION_ID
 NUMBER
 持有锁的会话(SESSION)标识;
 
ORACLE_USERNAME
 VARCHAR2(30)
 持有该锁的用户的Oracle用户名;
 
OS_USER_NAME
 VARCHAR2(15)
 持有该锁的用户的操作系统用户名;
 
PROCESS
 VARCHAR2(9)
 操作系统的进程号;
 
LOCKED_MODE
 NUMBER
 锁模式,取值同表三中的LMODE;
 


表五:v$locked_object视图字段说明

    1.2监控脚本

    根据上述系统视图,可以编制脚本来监控数据库中锁的状况。

    1.2.1showlock.sql

    第一个脚本showlock.sql,该脚本通过连接v$locked_object与all_objects两视图,显示哪些对象被哪些会话锁住:

/* showlock.sql */

column o_name format a10

column lock_type format a20

column object_name format a15

select rpad(oracle_username,10) o_name,session_id sid,

decode(locked_mode,0,'None',1,'Null',2,'Row share',

3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,'Exclusive') lock_type,

object_name ,xidusn,xidslot,xidsqn

from v$locked_object,all_objects

where v$locked_object.object_id=all_objects.object_id;
 


    1.2.2showalllock.sql

    第二个脚本showalllock.sql,该脚本主要显示当前所有TM、TX锁的信息;

/* showalllock.sql */

select sid,type,id1,id2,

decode(lmode,0,'None',1,'Null',2,'Row share',

3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,'Exclusive')

lock_type,request,ctime,block

from v$lock

where TYPE IN('TX','TM');
 


    2Oracle多粒度封锁机制示例

    以下示例均运行在Oracle 8.1.7上,数据库版本不同,其输出结果也可能有所不同。首先建立3个会话,其中两个(以下用SESS#1、SESS#2表示)以SCOTT用户连入数据库,以操作Oracle提供的示例表(DEPT、EMP);另一个(以下用SESS#3表示)以SYS用户连入数据库,用于监控;

    2.1操作同一行数据引发的锁阻塞

SESS#1:

SQL> select * from dept for update;

DEPTNO DNAMELOC

---------- -------------- -------------

10 account70

20 research8

30 sales8

40 operations8

SESS#3:

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT17 Row shareDEPT 825861

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

17 TX5242905861 Exclusive07610

17 TM329700 Row share07610
 


    如第一个脚本showlock所示,执行完SELECT…FOR UPDATE语句后,SESS#1(SID为17)在DEPT表上获得Row share锁;如第二个脚本showalllock所示,SESS#1获得的TX锁为Exclusive,这些都验证了上面的理论分析。另外,我们可以将 TX锁的ID1按如下方法进行分解:

SQL> select trunc(524290/65536) xidusn,mod(524290,65536) xidslot from dual;

XIDUSN XIDSLOT

------ -------

82
 


    分解结果与第一个脚本直接查出来的XIDUSN与XIDSLOT相同,而TX锁的ID2(5861)与XIDSQN相同,可见当LOCK TYPE为TX时,ID1实际上是该事务所占用的回滚段段号与事务表中的槽(SLOT)号的组合,ID2即为该槽被重用的次数,而这三个值实际上可以唯一地标识一个事务,即TRANSACTION ID,这三个值从系统表v$transaction中也可查到。

    另外,DEPT表中有4条记录被锁定,但TX锁只有1个,这也与上面的理论分析一致。继续进行操作:

SESS#2:

SQL> update dept set loc=loc where deptno=20;
 


    该更新语句被阻塞,此时再查看系统的锁情况:

SESS#3:

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT17 Row shareDEPT 825861

SCOTT19 Row ExclusiveDEPT 000

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

17 TX5242905861 Exclusive034621

17 TM329700 Row share034620

19 TM329700 Row Exclusive070

19 TX5242905861 None670
 


    在DEPT表上除了SESS#1(SID为17)持有Row share锁外,又增加了SESS#2(SID为19)持有的Row Exclusive锁,但还没有为SESS#2分配回滚段(XIDUSN、XIDSLOT、XIDSQN的值均为0);而从第二个脚本看到,SESS#2 的TX锁的LOCK_TYPE为None,其申请的锁类型(REQUEST)为6(即Exclusive),而其ID1、ID2的值与SESS#1所持有的TX锁的ID1、ID2相同,SESS#1的TX锁的阻塞域(BLOCK)为1,这就说明了由于SESS#1持有的TX锁,阻塞了SESS#2的更新操作(SESS#2所更新的行与SESS#1所锁定的行相冲突)。还可以看出,SESS#2先申请表级的TM锁,后申请行(事务)级的TX锁,这也与前面的理论分析一致。
下面,将SESS#1的事务进行回滚,解除对SESS#2的阻塞,再对系统进行监控。

SESS#3:

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT19 Row ExclusiveDEPT 2105803

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

19 TX1310825803 Exclusive01570

19 TM329700 Row Exclusive03330
 


    可以看到,SESS#1的事务所持有的锁已经释放,系统为SESS#2的事务分配了回滚段,而其TX锁也已经获得,并且ID1、ID2是其真正的Transaction ID.再将会话2的事务进行回滚。

SESS#2:

SQL> rollback;

Rollback complete.
 


    检查系统锁的情况:

SESS#3:

SQL> @showlock

no rows selected

SQL> @showalllock

no rows selected
 


    可以看到,TM与TX锁已全部被释放。

    2.2实体完整性引发的锁阻塞

    DEPT(部门)表有如下字段DEPTNO(部门编号),DNAME(部门名称),LOC(部门位置);其中DEPTNO列为主键。

SESS#1

SQL> INSERT INTO DEPT(DEPTNO) VALUES(50);

1 row created.

SESS#3

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT7 Row ExclusiveDEPT 68829

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TX39330429 Exclusive060

7 TM35740 Row Exclusive060
 


    向DEPT表中插入一条DEPTNO为50的记录后,SESS#1(SID为7)在DEPT表上获得Row Exclusive锁,并且由于进行了数据插入,该事务被分配了回滚段,获得TX锁。

SESS#2

INSERT INTO DEPT(DEPTNO) VALUES(50);
 


    这时,SESS#2(SID为8)也向DEPT表中插入一条DEPTNO为50的记录,该语句被阻塞,检查锁情况:

SESS#3

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT8 Row ExclusiveDEPT 77530

SCOTT7 Row ExclusiveDEPT 68829

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TX39330429 Exclusive0921

7 TM35740 Row Exclusive0920

8 TX45882730 Exclusive0220

8 TM35740 Row Exclusive0220

8 TX39330429 None4220
 


    SESS#2在DEPT表上也获得了Row Exclusive锁,同样也获得了回滚段的分配,得到TX锁,但是由于其插入的记录与SESS#1插入的记录的DEPTNO均为50,该语句成功与否取决于SESS#1的事务是提交还是回滚,所以SESS#2被阻塞,表现为SESS#2以Share方式(REQUEST=4)等待SESS#1所持有的 TX锁的释放。

    这时,如果SESS#1进行回滚:

SESS#1

SQL> ROLLBACK;

Rollback complete.

SESS#2

1 row created.

SESS#3

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT8 Row ExclusiveDEPT 77530

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

8 TX45882730 Exclusive01360

8 TM35740 Row Exclusive01360
 


    SESS#2的阻塞将被解除,SESS#2只持有原先已有的TM与TX锁,其等待的TX锁(由SESS#1持有)也消失了。

    如果SESS#1提交而不是回滚,在SESS#2上将会出现如下提示:

    ERROR at line 1:

    ORA-00001: unique constraint (SCOTT.PK_DEPT) violated错误。

    即发生主键冲突,SESS#1与SESS#2的所有锁资源均被释放。
 2.3参照完整性引发的锁阻塞

    EMP(员工)表有如下字段:EMPNO(员工编号),ENAME(员工姓名),DEPTNO(员工所在部门编号),其中DEPTNO列为外键,其父表为DEPT.

SESS#1

SQL> insert into dept(deptno) values(60);

1 row created.

SESS#3

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT7 Row ExclusiveDEPT 2633

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TX13107833 Exclusive01480

7 TM35740 Row Exclusive01480
 


    SESS#1(SID为7)在DEPT表中先插入一条DEPTNO为60的记录,SESS#1获得了DEPT表上的Row Exclusive锁,及一个TX锁。

SESS#2

insert into emp(empno,deptno) values(2000,60);

被阻塞

SESS#3

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT7 Row ExclusiveDEPT 2633

SCOTT8 Row ExclusiveEMP32031

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TX13107833 Exclusive02281

7 TM35740 Row Exclusive02280

8 TX19662831 Exclusive090

8 TM35760 Row Exclusive090

8 TX13107833 None490
 


    SESS#2(SID为8)向EMP表中出入一条新记录,该记录DEPT值为60(即SESS#1刚插入,但还未提交的记录的DEPTNO 值),SESS#2获得了EMP表上的Row Exclusive锁,另外由于插入记录,还分配了回滚段及一个TX锁,但由于SESS#2的插入语句是否成功取决于SESS#1的事务是否进行提交,所以它被阻塞,表现为SESS#2以Share(REQUEST=4)方式等待SESS#1释放其持有的TX锁。这时SESS#1如果提交,SESS#2的插入也将执行成功,而如果SESS#1回滚,由于不符合参照完整性,SESS#2将报错:

SESS#2

insert into emp(empno,deptno) values(2000,60)

*

ERROR at line 1:

ORA-02291: integrity constraint (SCOTT.FK_DEPTNO) violated - parent key not

Found
 


    SESS#2持有的锁也被全部释放。
 2.4外键未加索引引发的锁阻塞

    EMP表上的DEPTNO列为外键,但没有在该列上建索引。

SESS#1

SQL> delete emp where 0=1;

0 rows deleted.

SESS#3:

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT7 Row ExclusiveEMP000

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TM35760 Row Exclusive0100
 


    首先SESS#1(SID为7)做了一个删除操作,但由于条件(0=1)为永假,所以实际上并没有一行被删除,从监控脚本可以看出SESS#1在EMP表上获得Row Exclusive锁,但由于没有实际的行被删除,所以并没有TX锁,也没有为SESS#1分配回滚段。

    SESS#2:

    SQL> delete dept where 0=1;
 


    该语句虽然也不会删除实际数据,但却被阻塞,查看系统的锁情况:

SESS#3:

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT8 NoneEMP000

SCOTT7 Row ExclusiveEMP000

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TM35760 Row Exclusive0311

8 TM35760 None4120
 


    SESS#2申请在EMP表上加SHARE锁(REQUEST=4),但该申请被SESS#1阻塞,因为SESS#1已经在EMP表上获得了Row Exclusive锁,与SHARE锁不相容。

    下面我们对SESS#1进行回滚后,再进行监控。

SESS#3:

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT8 ShareEMP000

SCOTT8 Row ExclusiveDEPT 000

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

8 TM35740 Row Exclusive0160

8 TM35760 Share0160
 


    SESS#2在EMP表上获得Share锁后,又在DEPT表上获得Row Exclusive锁,由于没有实际的行被修改,SESS#2并没有获得TX锁。

    在Oracle8中,如果子表的外键上没有加索引,当在父表上删除记录时,会先在子表上申请获得Share锁,之后再在父表上申请Row Exclusive锁。由于表级Share锁的封锁粒度较大,所以容易引起阻塞,从而造成性能问题。

    当在外键上建立索引后,在父表上删除数据将不再对子表上加Share锁,如下所示:

SESS#1:

SQL> create index i_emp_deptno on emp(deptno);

Index created.

SQL> delete dept where 0=1;

0 rows deleted.

SQL>

SQL> @showlock

O_NAMESID LOCK_TYPEOBJECT_NAMEXIDUSN XIDSLOT XIDSQN

---------- ----- --------------- --------------- ------ ------- ------

SCOTT7 Row ExclusiveDEPT 000

SQL> @showalllock

SID TYID1ID2 LOCK_TYPEREQUESTCTIMEBLOCK

----- -- ---------- ---------- --------------- ---------- ---------- ----------

7 TM35740 Row Exclusive090
 


    可以看到,在EMP表DEPTNO列上建立索引后,在DEPT表上执行DELETE操作,不再要求在EMP表上加Share锁,只是在DEPT表上加Row Exclusive锁,封锁的粒度减小,引起阻塞的可能性也减小。

    3Oracle多粒度封锁机制总结

    Oracle通过具有意向锁的多粒度封锁机制进行并发控制,保证数据的一致性。其DML锁(数据锁)分为两个层次(粒度):即表级和行级。通常的DML操作在表级获得的只是意向锁(RS或RX),其真正的封锁粒度还是在行级;另外,在Oracle数据库中,单纯地读数据(SELECT)并不加锁,这些都极大地提高了系统的并发程度。

    在支持高并发度的同时,Oracle利用意向锁及数据行上加锁标志位等设计技巧,减小了Oracle维护行级锁的开销,使其在数据库并发控制方面有着明显的优势。