深入理解数据库锁

本文以oracle数据库学习数据库锁

锁的分类

oracle中分为两种模式的锁,一种是排他锁(X锁),另一种是共享所(S锁).

  • 排他锁,也可以叫写锁
  • 共享所,也可以叫读锁

锁是实现并发的主要手段,在数据库中应用频繁,但很多都由数据库自动管理,当事务提交后会自动释放锁.

锁的类型

Oracle为了使数据库实现高度并发访问,它使用了不同类型的锁来管理并发会话对数据对象的操作.Oracle的锁按作用对象不同分为如下几种类型.
  • DML锁: 该类型的锁被称为数据锁,用于保护数据
  • DDL锁: 可以保护模式中对象的结构
  • 内部闩锁: 保护数据库的内部结构,完全自动调用

这里主要介绍下常用的DML锁,它主要保证了并发访问时数据的完整性.它又可以分为以下两种类型的锁:
1) 行级锁(TX),也可以称为事务锁.当修改表中某行记录时,需要对将要修改的记录加行级锁,防止两个事务同时修改相同记录,事务结束,该锁也会释放,是粒度最细的锁.该锁只能属于排他锁(X锁).

2) 表级锁(TM),主要作用书防止在修改表的数据时,表的结构发生变化.例如,会话S在修改表A的数据时,它会得到表A的TM锁,而此时将不允许其他会话对该表进行变更或删除操作. 该情况的验证过程如下:

UPDATE TABLE_NAME SET COLUMN= 'test' WHERE ID = 'id';

此时已经锁定该表,表级锁将不允许在事务结束前其他会话对表TABLE_NAME进行DDL操作.
其次,在执行DROP TABLE TABLE_NAME操作,执行后会提示ORA-00054错误.
原因是:
在执行DML操作时,数据库会先申请数据对象上的共享锁,防止其他会话对该对象执行DDL操作。一旦申请成功,则会对将要修改的记录申请排他锁,如果此时其他会话正在修改该记录,那么等待其事务结束后再为修改的记录加上排他锁。

表级锁包含如下集中模式:

  • ROW SHARE:行级共享锁(RS)。该模式下不允许其他的并行会话对同一张表使用排他锁,但允许其利用DML语句或lock命令锁定同一张表中的其他记录。SELECT...FROM FOR UPDATE 语句就是给记录加上了RS锁.
  • ROW EXCLUSEIVE, 行级排他锁(RX).该模式下允许并行会话对同一张表的其他数据进行修改,但不允许并行会话对同一张表使用排他锁.
  • SHARE,共享锁(S).该模式下,不允许会话更新表,但允许会话对表添加RS锁.
  • SHARE ROW EXCLUSIVE,共享行级排他锁(SRX).该模式下,不能对同一张表进行DML操作,也不能添加S锁.
  • EXCLUSIVE,排他锁(X).该模式下,其他的并行会话不能对表进行DML和DDL操作,该表只能读.

下表列出了以上5中模式相互之间的兼容关系.其中,✔表示相互兼容,×表示相互不兼容

\RSSRXSRXX
RS×
S×××
RX××
SRX××××
X×××××

下面所示是Oracle中的各种SQL语句所产生的表级锁模式以及允许的锁定模式情况的汇总.

SQL语句表锁模式RSSRXSRXX
SELECT ...FROM tableNONEYYYYY
INSERT INTO ...RXYNYNN
UPDATE table ...RXYNYNN
DELETE FROM table ...RXYNYNN
SELECT * FROM table FOR UPDATERXYNYNN
LOCK TABLE table IN ROW SHARE MODERSYYYYN
LOCK TABLE table IN ROW EXCLUSIVE MODERXYNYNN
LOCK TABLE table IN SHARE MODESYNNNN
LOCK TABLE table IN SHARE ROW EXCLUSIVE MODESRXYNNNN
LOCK TABLE table IN EXCLUSIVE MODEXNNNNN

在Oracle中除了执行DML时自动为表添加TM锁外,也可以主动地为表添加TM锁,语法如下:

LOCK TABLE [schema.] table IN 
[EXCLUSIVE] 
[SHARE]
[ROW EXCLUSIVE]
[SHARE ROW EXCLUSIVE]
[ROW SHARE* | SHARE UPDATE*]
MODE [NOWAIT]

DDL锁也可以称为数据字典锁,主要作用是保护模式中对象的结构.当执行DDL操作时首先Oracle会自动地隐式提交一次事务,然后自动地给处理对象加上锁;当DDL结束时,Oracle会隐式地提交事务并释放DDL锁.与DML不同的是,用户不能显式的要求使用DDL锁.

DDL锁分为如下3类:

  • Exclusive DDL Lock,排他DDL锁定.如果对象加上了该类型的锁,那么对象不能被其他会话修改,而且该对象也不能再增加其他类型的DDL锁.如果是表,此时可以读取数据.
  • Shared DDL Lock,共享DDL锁定.保护对象的结构,其他会话不能修改该对象的结构,但是允许修改数据.
  • Breakable Parsed Lock,能打破的解析锁定.该类型的锁可以被打断,不能禁止DDL操作.

锁等待与死锁

锁等待

在某些情况下由于占用的资源不能及时释放,而造成锁等待,也可以叫锁冲突.锁等待会严重地影响数据库性能和日常工作.

例如当一个会话修改表A的记录时,它会对该记录加锁,而此时如果另一个会话也来修改此记录,那么第二个会话将因得不到排他锁而一直等待,此时会出现执行SQL时数据库长时间没有响应的现象.直到第一个会话把事务提交,释放锁,第二个会话才能对数据进行操作.

下面为大家示例锁等待现象:

  1. 打开SLQ*PLUS窗口,修改PRODUCTINFO 表中PRODUCTID 字段为1的记录,脚本如下:

UPDATE PRODUCTINFO SET ORIGIN = '修改1' WHERE PRODUCTID = 1;

此时虽然提示已更新,但事务并没有提交.接下来进行第二步操作.

  1. 打开另一个SQL*PLUS 窗口,同样修改PRODUCTINFO 表中PRODUCTID字段为1的记录,脚本如下:

UPDATE PRODUCTINFO SET ORIGIN = '修改2' WHERE PRODUCTID = 1;

此时执行效果不会提示已更新,而是一直等待.

此时的情况是因为第一个会话封锁了该记录,但事务没有结束,锁不会释放,而这时第二个会话也要修改同一条记录,但它缺没有办法获得排他锁,所以只能等待.如果第一个会话修改数据的事务结束,那么第二个会话会结束等待.及时地结束事务是解决锁等待情况发生的有效方法.

死锁

死锁的发生和锁等待不同,它是锁等待的一个特例,通常发生在两个或者多个会话之间.假设一个会话想要修改两个资源对象,可以是表也可以是字段,修改这两个资源的操作在一个事务当中,当它修改第一个对象时需要对其锁定,然后等待第二个对象,这时如果另外一个会话也需要修改这两个资源对象,并且已经获得对第二个对象的锁定,那么就会出现死锁,因为当前会话锁定了第一个对象等待第二个对象,而另一个会话锁定了第二个对象等待第一个对象.这样,两个会话都不能得到想要得到的对象,于是出现死锁.

这里例子就不在展示,大家可以自行试一试.实际开发中出现死锁情况大致有以下几种原因:

  1. 用户没有良好的编程习惯,偶尔会忘记提交事务,导致长时间占用资源.
  2. 操作的记录过多,而且操作过程中没有良好地对其分组.对于数据两很大的操作,可以将其分成几组提交事务,这样可以避免长时间的占用资源.
  3. 逻辑错误,两个会话都想得到已占有的资源.

相关推荐