【赛迪网-it技术报道】1.数据库锁的概念
为了确保并发用户在存取同一数据库对象时的正确性(即无丢失修改、可重复读、不读
“脏”数据),数据库中引入了锁机制。基本的锁类型有两种:排它锁(exclusive locks 记
为x 锁)和共享锁(share locks记为 s锁)。
排它锁:若事务t对数据d加x锁,则其它任何事务都不能再对d加任何类型的锁,
直至t 释放d 上的x 锁;一般要求在修改数据前要向该数据加排它锁,所以排它锁又称为
写锁。
共享锁:若事务t对数据d加s 锁,则其它事务只能对d加 s锁,而不能加x 锁,直
至 t 释放 d 上的 s 锁;一般要求在读取数据前要向该数据加共享锁,所以共享锁又称为读
锁。
2.oracle多粒度封锁机制介绍
根据保护对象的不同,oracle数据库锁可以分为以下几大类:
(1) dml lock(data locks,数据锁):用于保护数据的完整性;
(2) ddl lock(dictionary locks,字典锁):用于保护数据库对象的结构(例如表、视图、索
引的结构定义);
(3) internal locks 和 l a t c h es(内部锁与闩):保护内部数据库结构;
(4) distributed locks(分布式锁):用于ops(并行服务器)中;
(5) pcm locks(并行高速缓存管理锁):用于ops(并行服务器)中。
本文主要讨论dml(也可称为data locks,数据锁)锁。从封锁粒度(封锁对象的大小)
的角度看,oracle dml锁共有两个层次,即行级锁和表级锁。
2.1 oracle 的 tx 锁(行级锁、事务锁)
许多对oracle不太了解的技术人员可能会以为每一个 tx锁代表一条被封锁的数据行,
其实不然。 tx的本义是transaction (事务),当一个事务第一次执行数据更改(insert、 update、
delete)或使用select… for update 语句进行查询时,它即获得一个tx(事务)锁,
直至该事务结束(执行commit 或rollback操作)时,该锁才被释放。所以,一个tx
锁,可以对应多个被该事务锁定的数据行。
在 oracle 的每行数据上,都有一个标志位来表示该行数据是否被锁定。oracle 不象其
它一些 dbms(数据库管理系统)那样,建立一个链表来维护每一行被加锁的数据,这样
就大大减小了行级锁的维护开销,也在很大程度上避免了其它数据库系统使用行级封锁时经
常发生的锁数量不够的情况。数据行上的锁标志一旦被置位,就表明该行数据被加 x 锁,
oracle在数据行上没有 s锁。 2.2 tm锁(表级锁)
2.2.1 意向锁的引出
表是由行组成的,当我们向某个表加锁时,一方面需要检查该锁的申请是否与原有的表
级锁相容;另一方面,还要检查该锁是否与表中的每一行上的锁相容。比如一个事务要在一
个表上加 s 锁,如果表中的一行已被另外的事务加了 x 锁,那么该锁的申请也应被阻塞。
如果表中的数据很多,逐行检查锁标志的开销将很大,系统的性能将会受到影响。为了解决
这个问题,可以在表级引入新的锁类型来表示其所属行的加锁情况,这就引出了“意向锁”
的概念。
意向锁的含义是如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁;对任
一结点加锁时,必须先对它的上层结点加意向锁。如:对表中的任一行加锁时,必须先对它
所在的表加意向锁,然后再对该行加锁。这样一来,事务对表加锁时,就不再需要检查表中
每行记录的锁标志位了,系统效率得以大大提高。
2.2.2 意向锁的类型
由两种基本的锁类型(s锁、x 锁),可以自然地派生出两种意向锁:
意向共享锁(intent share lock,简称 is 锁):如果要对一个数据库对象加s锁,首先
要对其上级结点加is 锁,表示它的后裔结点拟(意向)加 s锁;
意向排它锁(intent exclusive lock,简称 ix 锁):如果要对一个数据库对象加x 锁,
首先要对其上级结点加 ix锁,表示它的后裔结点拟(意向)加x 锁。
另外,基本的锁类型(s、x)与意向锁类型(is、ix)之间还可以组合出新的锁类型,
理论上可以组合出4种,即:s+is,s+ix,x+is,x+ix,但稍加分析不难看出,实际上只
有 s+ix 有新的意义,其它三种组合都没有使锁的强度得到提高(即:s+is=s,x+is=x,
x+ix=x,这里的“=”指锁的强度相同)。所谓锁的强度是指对其它锁的排斥程度。
这样我们又可以引入一种新的锁的类型
共享意向排它锁(shared intent exclusive lock,简称 six 锁) :如果对一个数据库对象
加 six 锁,表示对它加 s 锁,再加 ix 锁,即 six=s+ix。例如:事务对某个表加 six 锁,
则表示该事务要读整个表(所以要对该表加s 锁),同时会更新个别行(所以要对该表加 ix
锁)。
这样数据库对象上所加的锁类型就可能有5 种:即s、x、is、ix、six。
具有意向锁的多粒度封锁方法中任意事务 t 要对一个数据库对象加锁,必须先对它的
上层结点加意向锁。申请封锁时应按自上而下的次序进行;释放封锁时则应按自下而上的次
序进行;具有意向锁的多粒度封锁方法提高了系统的并发度,减少了加锁和解锁的开销。
2.2.3 oracle 的 tm 锁(表级锁)
oracle的 dml锁(数据锁)正是采用了上面提到的多粒度封锁方法,其行级锁虽然只
有一种(即x锁),但其 tm锁(表级锁)类型共有5种,分别称为共享锁(s锁)、排它锁
(x 锁)、行级共享锁(rs 锁)、行级排它锁(rx 锁)、共享行级排它锁(srx 锁),与上面提到的s、x、is、ix、six 相对应。需要注意的是,由于oracle在行级只提供x锁,所
以与rs锁(通过select … for update语句获得)对应的行级锁也是x锁(但是该行
数据实际上还没有被修改),这与理论上的is 锁是有区别的。
下表为oracle数据库tm锁的相容矩阵(y=yes,表示相容的请求; n=no,表示不相
容的请求;-表示没有加锁请求):
t2
t1
s x rs rx srx -
s y n y n n y
x n n n n n y
rs y n y y y y
rx n n y y n y
srx n n y n n y
- y y y y y y
表一:oracle 数据库 tm 锁的相容矩阵
一方面,当oracle 执行select…for update、 insert、 update、 delete等 dml
语句时,系统自动在所要操作的表上申请表级rs锁(select…for update)或 rx锁
(insert、update、delete),当表级锁获得后,系统再自动申请 tx 锁,并将实际锁
定的数据行的锁标志位置位(指向该tx锁);另一方面,程序或操作人员也可以通过 lock
table 语句来指定获得某种类型的tm锁。下表总结了 oracle中各 sql语句产生 tm锁的
情况:
sql语句 表锁模式 允许的锁模式
select * from table_name…… 无 rs、rx、s、srx、x
insert into table_name…… rx rs、rx
update table_name…… rx rs、rx
delete from table_name…… rx rs、rx
select * from table_name for update rs rs、rx、s、srx
lock table table_name in row share mode rs rs、rx、s、srx
lock table table_name in row exclusive mode rx rs、rx
lock table table_name in share mode s rs、s
lock table table_name in share row exclusive mode srx rs
lock table table_name in exclusive mode x 无
表二:oracle 数据库 tm 锁小结
我们可以看到,通常的 dml 操作(select…for update、insert、update、
delete),在表级获得的只是意向锁(rs或 rx),其真正的封锁粒度还是在行级;另外,
oracle数据库的一个显著特点是,在缺省情况下,单纯地读数据(select)并不加锁, oracle
通过回滚段(rollback segment)来保证用户不读“脏”数据。这些都极大地提高了系统的
并发程度。
由于意向锁及数据行上锁标志位的引入,极大地减小了 oracle 维护行级锁的开销,这
些技术的应用使oracle 能够高效地处理高度并发的事务请求。 3 oracle 多粒度封锁机制的监控
3.1 系统视图介绍
为了监控oracle系统中锁的状况,我们需要对几个系统视图有所了解:
3.1.1 v$lock视图
v$lock视图列出当前系统持有的或正在申请的所有锁的情况,其主要字段说明如下:
字段名称 类型 说明
sid number 会话(session)标识;
type varchar(2) 区分该锁保护对象的类型;
id1 number 锁标识1;
id2 number 锁标识2;
lmode number 锁模式: 0 (none), 1 (null) ,2 (row share) , 3 (row exclusive) ,4
(share),5(share row exclusive),6(exclusive)
request number 申请的锁模式:具体值同上面的lmode
ctime number 已持有或等待锁的时间;
block number 是否阻塞其它锁申请;
表三:v$lock 视图主要字段说明
其中在type 字段的取值中,本文只关心 tm、tx两种dml锁类型;
关于id1、id2,type取值不同其含义也有所不同:
type id1 id2
tm 被修改表的标识(object_id) 0
tx 以十进制数值表示该事务所占用的回滚段号与该事
务在该回滚段的事务表(transaction table)中所占
用的槽号(slot number,可理解为记录号)。其组成
形式为: 0xrrrrssss ( rrrr = rbs number,
ssss = slot )。
以十进制数值表示环绕
(wrap)次数,即该槽(slot)
被重用的次数;
表四:v$lock 视图中 id1与 id2 字段取值说明
3.1.2 v$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 视图字段说明
3.2 监控脚本
根据上述系统视图,可以编制脚本来监控数据库中锁的状况。
3.2.1 showlock.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;
3.2.2 showalllock.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');
4.oracle多粒度封锁机制示例
以下示例均运行在oracle 8.1.7上,数据库版本不同,其输出结果也可能有所不同。首
先建立3个会话,其中两个(以下用 sess#1、sess#2 表示)以scott用户连入数据库,以操作 oracle 提供的示例表(dept、emp);另一个(以下用 sess#3 表示)以 sys 用户
连入数据库,用于监控;
4.1 操作同一行数据引发的锁阻塞
sess#1:
sql> select * from dept for update;
deptno dname loc
---------- -------------- -------------
10 account 70
20 research 8
30 sales 8
40 operations 8
sess#3:
sql> @showlock
o_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 17 row share dept 8 2 5861
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
17 tx 524290 5861 exclusive 0 761 0
17 tm 32970 0 row share 0 761 0
如第一个脚本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
------ -------
8 2
分解结果与第一个脚本直接查出来的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_name sid lock_type object_name xidusn xidslot xidsqn ---------- ----- --------------- --------------- ------ ------- ------
scott 17 row share dept 8 2 5861
scott 19 row exclusive dept 0 0 0
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
17 tx 524290 5861 exclusive 0 3462 1
17 tm 32970 0 row share 0 3462 0
19 tm 32970 0 row exclusive 0 7 0
19 tx 524290 5861 none 6 7 0
在 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_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 19 row exclusive dept 2 10 5803
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
19 tx 131082 5803 exclusive 0 157 0
19 tm 32970 0 row exclusive 0 333 0
可以看到,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锁已全部被释放。 4.2 实体完整性引发的锁阻塞
dept(部门)表有如下字段 deptno(部门编号),dname(部门名称),loc(部
门位置);其中deptno列为主键。
sess#1
sql> insert into dept(deptno) values(50);
1 row created.
sess#3
sql> @showlock
o_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 7 row exclusive dept 6 88 29
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tx 393304 29 exclusive 0 6 0
7 tm 3574 0 row exclusive 0 6 0
向 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_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 8 row exclusive dept 7 75 30
scott 7 row exclusive dept 6 88 29
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tx 393304 29 exclusive 0 92 1
7 tm 3574 0 row exclusive 0 92 0
8 tx 458827 30 exclusive 0 22 0
8 tm 3574 0 row exclusive 0 22 0
8 tx 393304 29 none 4 22 0
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_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 8 row exclusive dept 7 75 30
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
8 tx 458827 30 exclusive 0 136 0
8 tm 3574 0 row exclusive 0 136 0
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 的所有锁资源均被释放。
4.3 参照完整性引发的锁阻塞
emp(员工)表有如下字段:empno(员工编号),ename(员工姓名),deptno
(员工所在部门编号),其中 deptno 列为外键,其父表为 dept。
sess#1
sql> insert into dept(deptno) values(60);
1 row created.
sess#3
sql> @showlock
o_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 7 row exclusive dept 2 6 33
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tx 131078 33 exclusive 0 148 0
7 tm 3574 0 row exclusive 0 148 0
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_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 7 row exclusive dept 2 6 33
scott 8 row exclusive emp 3 20 31
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tx 131078 33 exclusive 0 228 1
7 tm 3574 0 row exclusive 0 228 0
8 tx 196628 31 exclusive 0 9 0
8 tm 3576 0 row exclusive 0 9 0
8 tx 131078 33 none 4 9 0
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持有的锁也被全部释放。
4.4 外键未加索引引发的锁阻塞
emp 表上的deptno 列为外键,但没有在该列上建索引。
sess#1
sql> delete emp where 0=1;
0 rows deleted.
sess#3:
sql> @showlock
o_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 7 row exclusive emp 0 0 0
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tm 3576 0 row exclusive 0 10 0
首先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_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 8 none emp 0 0 0
scott 7 row exclusive emp 0 0 0
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tm 3576 0 row exclusive 0 31 1
8 tm 3576 0 none 4 12 0
sess#2申请在emp 表上加share 锁(request=4),但该申请被sess#1阻塞,因
为sess#1 已经在emp 表上获得了row exclusive 锁,与share锁不相容。
下面我们对sess#1进行回滚后,再进行监控。
sess#3:
sql> @showlock
o_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 8 share emp 0 0 0
scott 8 row exclusive dept 0 0 0
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
8 tm 3574 0 row exclusive 0 16 0
8 tm 3576 0 share 0 16 0
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_name sid lock_type object_name xidusn xidslot xidsqn
---------- ----- --------------- --------------- ------ ------- ------
scott 7 row exclusive dept 0 0 0
sql> @showalllock
sid ty id1 id2 lock_type request ctime block
----- -- ---------- ---------- --------------- ---------- ---------- ----------
7 tm 3574 0 row exclusive 0 9 0
可以看到,在 emp 表 deptno 列上建立索引后,在 dept 表上执行 delete 操作,
不再要求在emp 表上加share锁,只是在dept表上加 row exclusive锁,封锁的粒度减小,
引起阻塞的可能性也减小。
5.总结
oracle数据库通过具有意向锁的多粒度封锁机制进行并发控制,保证数据的一致性。其 dml
锁(数据锁)分为两个层次(粒度):即表级和行级。通常的 dml 操作在表级获得的只是
意向锁(rs或rx),其真正的封锁粒度还是在行级;另外,在oracle数据库中,单纯地读
数据(select)并不加锁,这些都极大地提高了系统的并发程度。
在支持高并发度的同时,oracle数据库利用意向锁及数据行上加锁标志位等设计技巧,减小了
oracle维护行级锁的开销,使其在数据库并发控制方面的优势愈加明显。
闽公网安备 35060202000074号