暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 19C入门到精通之用锁控制并发存取及死锁

ITPro进化论 2024-01-09
210

数据可使用一种或多种方法来实现使用的并发性。这些方法包括保证由事务独占使用表的锁定机制、允许事务串行化的时间戳方法,以及基于验证的事务调度。锁定方法称为悲观方法,因为它们假定事务将破坏串行调度,除非明确地阻止它们这样做。时间戳和验证方法称为乐观方法,因为它们不假定事务必然会破坏串行调度。

锁定方法导致比乐观方法更长时间的延迟,因为它们要求冲突的事务等待访问锁定的数据库对象。不过,从积极的方面来看,锁定方法不必中止事务,因为它们阻止了可能冲突的事务与其他事务相互影响。如果事务可能破坏串行调度,那么乐观方法通常是要中止它们的。

racle的锁防止试图访问相同资源的事务之间的破坏性交互。资源可以是一个应用表或行,或者可以是内存中的一个共享系统数据结构,还可以是一个数据字典或行。在同意多个用户同时访问数据库从而允许数据并发时,锁保证数据一致性。

Oracle隐式地进行锁定,用户不必担心锁定哪个表以及如何锁定,Oracle会在必要时代表事务自动地进行锁定。默认时,Oracle使用行级锁定,这种锁定的数量限制最小,因此能保证最大的并发处理。Oracle默认在数据块中存储锁定行的信息,而且Oracle从不使用锁升级,即不会将较低的粒度(如行级锁定)升到较高的粒度(如表级锁定)。

1. 并发控制

事务的并发问题主要体现在丢失或覆盖更新、未确认的相关性(脏读)、不一致的分析(不可重复读)和幻象读4个方面,这些是影响事务完整性的主要因素。如果没有锁定且多个用户同时访问一个数据库,则当它们的事务同时使用相同的数据时,可能会发生这4种问题。

1.1. 丢失或覆盖更新

当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其他事务的存在,最后的更新将重写由其他事务所做的更新,这样就会导致数据丢失。例如,最初有一份原始的电子文档,文档人员A和B同时修改此文档,当修改完成之后进行保存时,最后修改完成的文档必将替换第一个修改完成的文档,这就造成了数据丢失更新的后果。如果文档人员A修改并保存之后,文档人员B再进行修改,则可以避免该问题。

1.2. 未确认的相关性(脏读)

如果一个事务读取了另一个事务尚未提交的更新,则称为脏读

例如,文档人员B复制了文档人员A正在修改的文档,并将文档人员A的文档发布。此后,文档人员A认为文档中存在着一些问题需要重新修改,此时文档人员B发布的文档就将与重新修改后的文档内容不一致。如果在文档人员A将文档修改完成并确认无误的情况下,文档人员B再复制则可以避免该问题。

1.3. 不一致的分析(不可重复读)

当事务多次访问同一行数据,并且每次读取的数据不同时,将会发生不一致的分析的问题。不一致的分析与未确认的相关性类似,因为其他事务也正在更改该数据。然而,在不一致的分析中,事务所读取的数据是由进行了更改的事务提交的。而且,不一致的分析涉及多次读取同一行,并且每次信息都由其他事务更改,因而该行发生了不可重复读取的情况。

例如,文档人员B两次读取文档人员A的文档,但在文档人员B读取时,文档人员A又重新修改了该文档中的内容,在文档人员B第二次读取文档人员A的文档时,文档中的内容已经改变,此时则发生了不可重复读的情况。如果文档人员B在文档人员A全部修改后读取文档,则可以避免该问题。

1.4. 幻象读

当一个事务的更新结果影响到另一个事务时,会发生幻象读问题。事务第一次读的行范围显示出其中一行已不存在于第二次读或后续读中,因为该行已被其他事务删除。同样,由于其他事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。

例如,文档人员B更改了文档人员A所提交的文档,但当文档人员B将更改后的文档合并到主副本时,却发现文档人员A已将新数据添加到该文档中。如果文档人员B在修改文档之前,没有任何人将新数据添加到该文档中,则可以避免该问题。

2. 为何加锁

作为共享资源的数据库可以供多个用户同时访问,也就是说,在同一时刻可能会有多个并发执行的事务访问数据库的同一资源。通过下面的方法可以保证这些并发事务的执行不破坏数据的一致性和完整性;

  • 让所有的事务一个一个的串行执行,但这样会大大降低数据库的工作效率。
  • 提供一种对数据库进行并发控制的机制,这种机制就是锁。

在同一时刻,有的事务需要获取数据后进行处理,有的事务仅仅是查询该数据而不进行处理,这样查询和处理操作可以并发执行,互不干扰。同时,修改数据的事务规则,按照一定的算法进行调度,这样就能既不破坏数据的一致性,又能大大提高数据库的执行效率。

2.1. 什么是锁

锁是对数据进行并发控制的机制。Oracle使用锁来保证事务的隔离性,即事务内部的操作和使用的数据对并发执行的其他事务是隔离的、互不干扰的。换言之,锁其实就是事务可以对数据库资源进行操作的权限。

Oracle的事务要执行,必须先申请对该资源的锁。按照获得的锁的不同,对该资源进行相应锁赋予的操作。如果没有获得锁,就不能执行对该资源的任何操作。当某种事件出现或该事务完成后,自动解除对该资源的锁。

Oracle中锁的管理和分配是由数据库管理系统自动完成的,不需要用户进行干预。同时,Oracle还提供了手工加锁的命令,供有经验的用户使用。

2.2. Oracle的锁定方式

Oracle使用锁来控制访问两种广泛类型的对象,即用户对象(包括表)和系统对象(可能包括共享内存结构和数据字典对象)。为了避免并发事务之间的冲突,Oracle遵循一种悲观的锁定方式,它可以预期可能的冲突,并且阻止某些事务干扰其他事务。

在锁的环境中,粒度是指被锁机制锁定的数据单元的大小。Oracle使用行级粒度来锁定对象,这是最好的粒度级别(独占表锁定是最粗的粒度级别)。有些数据库(包括Microsoft SQL Server)只提供页面级而不是行级锁定。页面类似于Oracle的数据块,它可以有许多行,因此页面级锁定表示在更新中除了要锁定的行以外,还锁定了其他一些行。如果其他用户需要锁定非更新成分的行时,它们必须等待释放页面上的锁。

锁的粒度越粗,事务的串行性就越强,从而并发异常就越少;但粒度级别越粗,并发级别就越低。Oracle的锁不阻止其他用户读取数据表,并且在默认情况下,查询从不在表上放置锁。

2.3. Oracle的锁类型

按照锁的权限来分,Oracle数据库管理系统提供了两种类型的锁,即排他锁(exclusive lock)和共享锁(share lock)。

按照锁所分配的资源来分,又可以分为数据锁(data lock)、字典锁(dictionary lock)、内部锁分布锁并行缓冲管理锁,其中常见的是数据锁字典锁,其他锁都是由管理系统自动管理的。

  • 排他锁:又称为X锁或写锁。若事务T1对资源R加上X锁,则只允许T1读取和修改R,其他事务可以读取R,但不能修改R,除非T1事务解除了加在R上的X锁。
  • 共享锁:又称为S锁或读锁。若事务T2对资源R加上S锁,允许T2读取R,其他事务也可以读取R。
  • 数据锁:当用户对表格中的数据进行INSERT、UPDATE和DELETE操作时将要用到数据锁。数据锁在表中获得并保护数据。
  • 字典锁:当用户创建、修改和删除数据表时将要用到字典锁。字典锁用来防止两个用户同时修改同一个表的结构。

2.4. 查询锁信息

Oracle在动态状态表vlock表的结构:

describe v$lock;

  • SID:会话标识符。
  • TYPE:获得或等待的锁类型。其中,TX表示事务锁,TM表示表级锁,MR表示介质恢复,ST表示磁盘空间事务。
  • LMODE/REQUEST:锁的模式。其中,0表示无,1表示空,2表示行共享(RS),3表示行排他(RX),4表示共享(S),5表示共享行排他(SRX),6表示排他(X)。若LMODE列含有一个不是0或1的数值,则表明进程已经获得了一个锁;若REQUEST列含有一个不是0或1的数值,则表明进程正在等待一个锁;若LMODE列含有数值0,则表明进程正在等待一个锁。
  • ID1:根据锁的类型的不同,此列中的数值有不同的含义。假如锁的类型是TM,此列中的数值是将要被锁定或等待被锁定的对象的标识;假如锁的类型是TX,此列中的数值是回滚段号码的十进制表示。
  • ID2:根据锁的类型的不同,此列中的数值有不同的含义。假如锁的类型是TM,此列中的数值是0;假如锁的类型是TX,此列中表示交换次数,也就是回滚槽重新使用的次数。

2.5. 监控锁的方法

Oracle使用锁在多个用户同时访问时维护数据的一致性和完整性。但是,当两个或两个以上的用户会话试图竞争同一对象的锁时,锁将成为坏消息。DBA应该监控并管理数据库中的锁的争用。监控锁的方法包括如下3种:

①使用CATBLOCK.SQL和UTLLOCKT.SQL脚本

Oracle提供了两个有用的锁监控脚本,称为CATBLOCK.SQL和UTLLOCKT.SQL。这两个脚本可在“lock这样的数据字典视图中收集的与锁相关的信息视图。脚本UTLLOCKT.SQL查询由CATBLOCK.SQL创建的视图,以报告等待锁的会话及其相应的阻塞会话。CATBLOCK.SQL必须在使用UTLLOCKT.SQL前运行。

②直接查询数据字典视图

以下脚本可用于确定数据库中持有和等待锁的会话。该脚本查询并连接vsession视图。

set echo off
set pagesize 60
Column SID FORMAT 999 heading “SessionID”
Column USERNAME FORMAT A8
Column TERMINAL FORMAT A8 Trunc
select B.SID,C.USERNAME,C.TERMINAL,B.ID2,B.TYPE,B.LMODE,B.REQUEST from DBA_OBJECTS A,V$LOCK B,V$SESSION C 
where A.OBJECT_ID(+) = B.ID1 
AND B.SID = C.SID 
AND C.USERNAME IS NOT NULL 
order by B.SID,B.ID2;

③使用Oracle企业管理器

使用Oracle企业管理器(Oracle Enterprise Manager,OEM)也可以得到会话的锁信息。这是获得锁信息最简单的方法之一,可通过选择Database Control Home Page Performance Additional Monitoring Links Instance Locks选项转到此页。Instance Locks页面显示出所有锁,即阻塞和非阻塞的锁。我们所看到的大多数锁是无害的,它们是Oracle用来维护并发性的例行非阻塞锁。

3. 加锁的方法

3.1. 行共享锁RS(row share)

对数据表定义行共享锁后,如果行共享锁被事务A获得,其他事务可以进行并发查询、插入、删除及加锁,但不能以排他方式存取该数据表。其语法格式如下:

LOCK TABLE xx IN ROW SHARE MODE;

--向表dept_temp中增加行共享锁
lock table dept_temp in row share mode;

3.2. 行排他锁RX(row exclusive)

对数据表定义行排他锁后,如果行排他锁被事务A获得,那么事务A对数据表中的行数据具有排他权限。其他事务可以对同一数据表中的其他数据行进行并发查询、插入、修改、删除及加锁,但不能使用行共享锁、共享行排他锁和行排他锁3种方式加锁。执行下列语句可定义行排他锁:

LOCK TABLE xx IN ROW EXCLUSIVE MODE;

3.3. 共享锁S(share)

对数据表定义共享锁后,如果共享锁被事务A获得,其他事务可以执行并发查询和加共享锁操作,但不能修改表,也不能使用排他锁、共享行排他锁和行排他锁3种方式加锁。执行下列语句定义共享锁:

LOCK TABLE xx IN SHARE MODE;

3.4. 共享行排他锁SRX(share row exclusive)

对数据表定义共享行排他锁后,如果共享行排他锁被事务A获得,其他事务可以执行查询和对其他数据行加锁操作,但不能修改表,也不能使用共享锁、共享行排他锁、行排他锁和排他锁4种方式加锁。执行下列语句定义共享行排他锁:

LOCK TABLE xx IN SHARE ROW EXCLUSIVE MODE;

3.5. 排他锁(exclusive)

排他锁是最严格的锁。如果排他锁被事务A获得,事务A可以执行对数据表的读写操作,其他事务可以执行查询但不能执行插入、修改和删除操作。执行下列语句定义排他锁:

LOCK TABLE xx IN EXCLUSIVE MODE;

--向表dept_temp中增加排他锁
lock table dept_temp in exclusive mode;

4.死锁

在数据库系统中,死锁(deadlocking)是指多个用户(进程)分别锁定了一个资源,又试图请求锁定对方已经锁定的资源,这就产生了一个锁定请求环,导致多个用户(进程)都处于等待对方释放其锁定资源的状态。

4.1 死锁的产生

当两个会话互相等待对方持有的资源而导致相互阻塞时,RDBMS(关系数据库管理系统)中出现死锁。在这种情形下,需要Oracle中止其中一个会话,回滚其事务,Oracle可以快速识别两个会话死锁,中止持有最近应用的锁的会话,这将释放出另一会话等待的对象锁。

如果Oracle在事务中遇到死锁,它在跟踪文件(位于USER_DUMP_DEST初始化参数指定的目录中)中记录所涉及的会话ID、事务发出的SQL语句、死锁中涉及的每个会话在其上持有锁的特定对象名和行等信息。Oracle进一步通知用户,死锁不是Oracle的错误,是由应用设计中的错误所导致,或者是发出特别的SQL引起的。应用设计人员必须在代码中编写异常处理程序以回滚事务并重启它。

用户可以在设计阶段加以注意,保证合适的对象锁定次序等来避免死锁。假如写入程序阻塞了其他写入程序,则Oracle中的死锁是很少出现的。

例如,事务A的线程T1具有Supplier表上的排他锁。事务B的线程T2具有Part表上的排他锁,并且之后需要Supplier表上的锁。事务B无法获得这一锁,因为事务A已拥有它。事务B被阻塞,等待事务A。然后,事务A需要Part表的锁,但又无法获得锁,因为事务B将它锁定了。

对于Part表锁资源,线程T1在线程T2上具有相关性。同样,对于Supplier表锁资源,线程T2在线程T1上具有相关性。因为这些相关性形成了一个循环,所以在线程T1和线程T2之间存在死锁。

4.2 死锁的预防

可以通过如下注意事项来预防死锁:

  • 执行事务时尽可能快速提交,在很大程度上可避免死锁。
  • 进行批量操作时,不同程序员操作表的顺序应该一致。

今天的文章就到这里,如果对你有用,记得点个【】和【在看】,感谢阅读~

文章转载自ITPro进化论,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论