目录

Oracle数据操作DML中的锁类型

DML锁,也叫数据锁,是为了多用户同时访问数据时保证数据的完整性。例如:DML锁可以防止在网上书城多个用户购买最后一本书都能成功。DML锁防止DML跟DDL操作同时操作相关数据时产生的破坏性冲突。

DML语句自动申请表级锁和行级锁。在下面一节中,圆括号中的首字母缩写是Oracle EM中Locks Monitor使用的缩写。EM对所有的表级锁使用TM表示,而不是使用表级锁的模式(如RS或者SRX)。

行级锁(TX)

行级锁,也叫TX锁,实在表中的某一行加的锁。一个事务在执行下面这些语句时会对要修改的行加行级锁:INSERT,UPDATE,DELETE,MERGE和SELECT…FOR UPDATE。行级锁一直等到事务提交或者回滚后才会释放。

事务在获取行级锁的同时,也会对这行所在的表申请一个表级锁。表级锁防止一些冲突的DDL操作影响当前事务的数据修改。

表级锁(TM)

一个事务在执行下面这些语句时会自动申请一个表级锁(TM锁):INSERT,UPDATE,DELETE,MERGE和SELECT…FOR UPDATE。这些DML操作申请一个表级锁用于表示一个事务正在表上执行DML操作,防止可能跟这个事务冲突的DDL操作。你可以使用LOCK TABLE语句显示的获得一个表级锁。

表级锁可以有下面几种模式:

  • 行级共享锁 row share lock(RS)。也叫subshare table lock (SS),表级子共享锁,表示在这个表上获得了这个锁的事务锁定了某些行,准备对他们做更新操作。一个SS锁时表级锁中最宽松的锁,保证表上最高级别的共享性。
  • 行级排它锁 row exclusive lock(RX)。也叫subexclusive table lock(SX),表级子排它锁,表示获得了这个锁的事务已经完成了表上某些行更新的操作或者正在处理SELECT…FOR UPDATE操作。一个SX锁允许其他事务查询,插入,更新,删除或者获取表上其他某些行的锁。所以SX锁允许多个事务在同时在同一个表上获取SX和SS锁。
  • 共享锁 share table lock(S)。获取该锁的事务允许其他事务查询这张表(但是不能使用SELECT…FOR UPDATE)。但是在只有一个事务获取表级共享锁的时候才允许做更新。多个事务可以在一个表示同时获取共享锁,所以这个锁不能保证这个事务可以修改这张表。
  • 共享行级排它锁 share row exclusive table lock(SRX)。也叫share-subexclusive table lock共享子排它锁(SSX)时一个比共享锁更严格的锁。在一个表上同时只能有一个事务获得SSX锁。获得SSX锁的事务允许其他事务查询表(除了SELECT…FOR UPDATE)但是不能更新这张表。
  • 排它锁(X)是表级锁中最严格的锁,允许获得该锁的事务排它的写操作,在一个表上只有一个事务可以获得X锁。

DML操作中的锁

Oracle数据库在DML操作时会自动的获取表级锁和行级锁。操作的类型绝对需要获取什么锁,下表汇总了这些信息。

注意:下表中DML语句中隐式获取的SX锁在constraints 约束的影响下有时候可能短时间内会变成排它锁(X)

SQL TX TM模式 是否允许其他事务获取锁
RS RX S SRX X
SELECT FROM table - none Y Y Y Y Y
INSERT INTO table Y SX Y Y N N N
UPDATE table Y SX Y1 Y1 N N N
MERGE INTO table - SX Y Y N N N
DELETE FROM table - SX Y1 Y1 N N N
SELECT FROM table FOR UPDATE - SX Y1 Y1 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

Y1表示如果锁定的行跟其他事务已锁定的没有冲突则为Y,否则发生锁等待

当行被查询时的锁

一个查询可能是显示的如SELECT,或者隐式的,如在大多数INSERT,MERGE,UPDATE和DELETE语句条件查询中,在DML语句中唯一不需要查询条件的是携带VALUES值得INSERT语句。因为查询只是读取数据,所以是SQL语句中对其他SQL语句影响最小的。

下面的规则适用于不带for update的查询语句:

  • 查询不需要任何的数据锁。因此,其他的事务可以同时查询和更新表,包括正在查询的行。

  • 查询不需要等待任何数据锁释放。因此,查询在任何时候都可以执行。这条规则有一个例外,查询在一些非常特殊的分布式事务同步的情况可能会发生数据锁等待。

当行被修改时的锁

一些数据库使用一个锁管理器来在内存中维护一个锁的列表。相比之下Oracle数据库把锁信息存储在要锁的行的数据块上。每一个行级锁只影响一行。

Oracle数据库使用队列方式来管理行锁的申请。如果一个事务申请了一个行级锁,如果这行还没有被锁,这个事务会在行所在的数据块上加一个锁。事务本身在数据库header的ITL中有一个记录。每一个被事务修改的行指向在ITL中存储的事务ID,因此,在同一个数据块中被同一个事务修改的100行需要100个行锁,但是所有的100行都指向同一个事务ID。

当一个事务结束后,事务ID仍然存在在数据块header中的ITL里。如果一个新的事务想要修改行,它会使用这个事务ID来判断这个锁是否有效,如果这个锁有效,那么这个事务的session要求在锁被释放时通知它,否则,这个新事务就直接获取锁。

INSERT UPDATE,DELETE和SELECT…FOR UPDATE语句的规则如下:

  • 一个包含DML语句使用这些语句修改行的事务获取一个需要修改的行上的排他的行级锁。因此,其他的事务不能更新和删除被锁的行,直到这个事务提交或者回滚
  • 除了这些行级锁之外,包含修改数据的DML语句的事务同时还至少需要在包含修改数据的表上申请一个SX锁。如果这个事务已经获得了这个表的一个比SX更严格的锁,如S锁,SRX锁或者X锁,那么就没有必要再申请一个SX锁了。如果事务已经获得了一个SS锁,那么Oracle会自动把这个SS锁转换为SX锁。
  • 一个包含DML语句的事务不需要再子查询或者隐式查询的数据上申请行级锁。
  • 同一个事务里查询语句可以获得之前修改的数据,但是不能查询其他事务没提交的数据。