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

Oracle
数据库Oracle性能优化可能出现的问题
oracle认证辅导:重访Oracle密码
Oracle认证:修改用户指定的默认表空间
Oracle认证:Oracle的三种Join方法
Oracle认证辅导:教你数据库查询初始化参数
教你查询Oracle中的表空间
利用变量在Linux中给文件命名
oracle的case函数控制结构DECODE()函数
解决Oracle被锁定有妙招
Oracle数据库编写事务 几个需要遵守指导方针
如何解决Oracle被锁定问题
如何控制Oracle虚拟专用数据
Oracle入门基础之参数文件
如何解决Oracle数据库ORA-00257故障
实例解析:用Oracle创建实例的参数需求
对比Caché和Oracle在数据库的应用
风河应用Oracle产品为企业2.0提供动力
Oracle数据库中Insert、Update、Delete操作速度大提速
Oracle11g再创TPC-C基准测试性价比世界纪录
Oracle用户常用数据字典的查询

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


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 161 ::
收藏到网摘: 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维护行级锁的开销,使其在数据库并发控制方面有着明显的优势。