Oracle为DML操作自动获得行锁和表锁,操作的类型决定了锁的行动,下面对DML操作锁的情况作了1个汇总:
SQL Statement |
Row Locks |
Table Lock Mode |
RS |
RX |
S |
SRX |
X |
SELECT ... FROM table ... |
―― |
none |
Y |
Y |
Y |
Y |
Y |
INSERT INTO table ... |
Yes |
SX |
Y |
Y |
N |
N |
N |
UPDATE table ... |
Yes |
SX |
Y(注) |
Y(注) |
N |
N |
N |
MERGE INTO table ... |
Yes |
SX |
Y |
Y |
N |
N |
N |
DELETE FROM table ... |
Yes |
SX |
Y(注) |
Y(注) |
N |
N |
N |
SELECT ... FROM table FOR UPDATE OF ... |
Yes |
SX |
Y(注) |
Y(注) |
N |
N |
N |
LOCK TABLE table IN ... |
―― |
|
|
|
|
|
|
ROW SHARE MODE |
|
SS |
Y |
Y |
Y |
Y |
N |
ROW EXCLUSIVE MODE |
|
SX |
Y |
Y |
N |
N |
N |
SHARE MODE |
|
S |
Y |
N |
Y |
N |
N |
SHARE ROW EXCLUSIVE MODE |
|
SSX |
Y |
N |
N |
N |
N |
EXCLUSIVE MODE |
|
X |
N |
N |
N |
N |
N |
注:如果另外一个事务和当前事务出现行冲突,则需要等待
下面论述当行被查询和修改时会触及到的锁。
当行被查询时的锁
1个查询可以直接通过SELECT查询数据,或其它语句间接的查询数据,如:INSERT、MERGE、UPDATE和DELETE,其中只有INSERT操作不是一定会触及到查询的。由于查询仅读数据,因此他们被其它DML语句干涉的可能性是最小的。
如果查询没有FOR UPDATE子句,则查询时:
1)查询不要求数据锁,因此,其它事务能查询和更新正在被查询的数据;
2)查询没必要等待任何数据锁被释放,因此,查询总是能履行。1个例外是查询必须等待散布式事务的1些特定的数据锁。
当行被修改时的锁
1些
数据库使用1个内存中的列表来保护锁,但
Oracle数据库存储锁信息在数据块中,信息包括了被锁的行,每一个行锁仅影响1行数据。
Oracle数据库为行锁的获得使用了1个队列机制,如果1个事务要求1个行锁,并且行未被锁,那末事务获得行的数据块的1个锁,事务本身会在数据块头的interested transaction list(ITL)区域放1个条目,被事务修改的每行都指向ITL中存储的事务ID的1个拷贝,因此,被单个事务修改的在同1块中的100行数据会要求100个行锁,但是所有100行都援用同1个事务ID。
当事务结束时,事务ID保存在数据块头的ITL区域中。如果1个新的事物想修改1行,那末它使用事务ID判断该锁是不是是激活的,如果锁是激活的,那末新事务的session会要求在锁被释放时被通知,否则,新事务获得锁。
INSERT、UPDATE、DELETE和SELECT ... FOR UPDATE将满足:
1)使用这些DML操作的事务将在修改的行上要求排它行锁,因此,其它事务不能更新或删除锁定的行,直到事务commit或roll back;
2)除行锁,使用这些DML操作的事务最少需要要求1个子排它表锁(subexclusive table lock,SX)。如果事务已具有了1个S、SRX或X表锁(比SX锁有更强的限制),那末SX锁不被需要;如果事务已具有了1个SS锁,那末
Oracle数据库自动转换SS锁到SX锁;
3)除非触及的行被修改,事务不会对任何子查询或隐含的子查询触及的行加行锁;例以下面的update操作,使用那个了1个子查询(括号中的部份)和隐含子查询(WHERE a > 5):
UPDATE t SET x = ( SELECT y FROM t2 WHERE t2.z = t.z ) WHERE a > 5;
事务将不会对子查询(SELECT y FROM t2 WHERE t2.z = t.z)触及的行加锁。
4)在同1个事务中,1个查询能看到先前的DML语句修改的行,但不能看到其它事务未提交的改变。