Oracle中的锁
Posted 丹心明月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle中的锁相关的知识,希望对你有一定的参考价值。
1.锁的基本概念
锁的定义:锁(lock)机制用于管理对共享资源的并发访问;以保证多用户环境下数据库的完整性和一致性。锁的对象既可以是用户对象(如表或表行),也可以是系统对象(如共享数据结构以及数据字典行)。
事务并发访问等绝大多数情况下的锁,是数据库自动添加、释放的;Oracle也支持手动加锁的操作。
在事务执行期间,Oracle能够根据加锁的资源及需要执行的操作自动的决定锁的类型及对资源的限制级别;加锁的时候的原则是最低限制级别:即能在行上加锁就不会在表上加,能共享就不排他。
对于锁类型的解释,可参考视图V$LOCK_TYPE。
2.锁模式(lock modes):共享和排他
一般情况下,数据库使用两种类型的锁:排他锁和共享锁。一个资源上只可加一个排他锁,但可以加多个共享锁。
Oracle数据库自动使用最低适用的限制级别,以支撑最高程度的数据并发。
在多用户的数据库系统中,Oracle使用两种模式的锁:
模式名称 | 简介 | 何时使用 |
共享锁(shared lock, S锁,读锁) | S锁是指可以查看数据但无法修改和删除数据的一种数据锁,若事务1对数据对象A加上S锁,则事务2只能读A;其他事务只能再对A加S锁,而不能加排他X锁,直到事务1释放A上的S锁。这就保证了其他事务可以读A,但在事务1释放A上的S锁之前不能对A做任何修改。 | 当执行SELECT时,数据库会自动使用S锁 |
排他锁(exclusive lock, X锁,独占锁,写锁,互斥锁) | 如果事务1对数据或数据对象A加上X锁后,则其他事务不能再对A加任何类型的锁。获得X锁的事务既能读数据,又能修改数据。 | 执行INSERT、UPDATE、DELETE时,数据库会自动使用X锁 |
锁会影响读与写的交互:读即对资源的查询;写即对资源的更改。Oracle数据库中的锁对读和写行为的概括:
-
仅当写时才锁定行:当语句更新某行时,事务仅会锁定该行,以降低对相同数据的争用。一般情况下,数据库不会将行锁上升到块或表级别;
-
对一行的写会阻塞对相同行的并发写:当事务更新一行的时候,会防止其他事务并发更新相同的行;
-
读永不阻塞写:因为读不会锁定行,故而写可以更新该行。唯一的例外是SELECT...FOR UPDATE语句,该语句会锁定所读的行,故而不允许其他事务对所读的行进行更新;
-
PS:select...for update语句会对选定的行加排他行锁+表级共享锁:排他行锁不允许其他会话更改锁定行,表级共享锁不允许其他会话变更表结构。
-
-
写永不阻塞读:当写一行的时候,数据库使用undo数据保证行的一致性读;
3.锁转换(lock conversion)与锁提升(lock escalation)
在必要情形下,Oracle会进行锁转换。锁转换中,数据库自动将低限制级别的表锁上升到高限制级别。
例如,假设一个事务执行了SELECT...FOR UPDATE并对所选行进行了更新:此场景下,数据库会自动将行共享表锁转换为行排他表锁。
锁转换与锁提升不同。在一个粒度下的锁数量过多的时候,锁提升会自动将锁定的粒度提升,例如从行级锁提升到表级锁。锁的数量降低了,但锁的限制级别提升了。Oracle数据库不会进行锁提升操作。因为锁提升会提高死锁的可能性。
4.锁的持续时间
事务内各语句获得的锁在事务执行期内有效,以防止事务间破坏性的相互干扰,例如:脏读(dirty read),丢失更新(lost update),以及其他并发事务找那个的DDL操作。如果某个事务中的SQL语句对数据进行了修改,只有在此事务提交后开始的事务才能看到前者修改的结果。
当用户提交(commit)或回滚(rollback)一个事务,Oracle将自动释放该事务内各个SQL语句获得的锁;若是在事务内回滚到某个保存点(savepoint),则Oracle也会自动释放此保存点后获得的锁。等待事务(即其他事务持有的锁与等待事务所需加的锁冲突时)只有在持有资源的事务提交或回滚后才会获得所需资源并对其加锁。
5.锁与死锁
死锁是指,两个或多个用户互相等待对方锁定的资源。
Oracle会自动检测死锁,并回滚引起死锁的其中一个语句,而语句所属的事务,会收到相应的提示:ORA-00060:deadlock detected while waiting for resource,而该事务需要显式的回滚或提交。
6.锁的类型
Oracle的锁分为以下几类:
锁 | 描述 |
DML锁 | 保护数据。例如,表锁锁定整张表;行锁锁定选定的表 |
DDL锁 | 保护模式对象的结构。例如,表以及视图的定义等 |
系统锁(system locks) | 保护内部数据库结构,例如表文件等。latch、mutex以及内部锁完全是自动的。 |
1)DML锁
DML锁,又称数据锁。
DML语句自动获取以下类型的锁:
-
row locks(TX)
-
table locks(TM)
1.1)row locks(TX)
是在表单行上的锁。事务会在INSERT/UPDATE/DELETE/MERGE或SELECT...FOR UPDATE语句更新的每一行上获取一个行级锁。行级锁在事务提交或回滚之前都一直存在。
如果事务获取了行级锁,那么该事务同时也会获取包含该行的表上的表级锁。该表级锁会阻塞对该表的DDL操作。
行锁的存储:与那些在内存中维护一个锁列表的数据库不同,Oracle数据库在包含被锁定行的数据块中存储锁信息。
数据库采用一种排队的机制来获取行锁。若事务需要在未有锁的行上加锁,那么事务会直接在数据块上置锁;在事务结束后,事务ID会依旧保存在块头中。若其他事务想要更新此行,则使用块头上的事务ID来判断该锁是否为活跃锁:若锁活跃(active),则会话等待;若不活跃,则获取锁。
1.2)table locks(TM)
表锁,当通过INSERT/UPDATE/DELETE/MERGE/SELECT...FOR UPDATE语句或LOCK TABLE语句更新表的时候,获取表锁。DML操作需要表锁来代表事务保留对表的DML访问,并防止与事务冲突的DDL操作。
表锁模式有:
-
Row Share(RS):也称子共享表锁(subshare table lock,SS),表明持有该锁的事务在该表中有锁定的行并将会更新这些行。行共享锁是表锁的最小限制模式,提供了最大程度的并行度。
-
Row Exclusive Table Lock(RX):也称为子排他表锁(SX),表明持有该锁的事务已更新表行或执行了SELECT...FOR UPDATE命令。SX锁允许其他事务在相同表中并行查询、插入、更新、删除或锁定行。因此,SX锁允许多事务对同一个表持有SX和SS锁;
-
Share Table Lock(S):已有共享表锁的表上允许其他事务执行查询(SELECT...FOR UPDATE除外),但仅当只有一个事务持有共享表锁的时候才可以执行更新操作。因为多个事务可能同时持有共享表锁,故而持有该锁并不能完全保证事务可以更新该表;
-
Share Row Exclusive Table Lock(SRX):也称共享子排他表锁(SSX),比共享表锁限制级别要高。同一表上同时仅可有一个事务持有SSX锁。SSX锁允许其他事务查询表(SELECT...FOR UPDATE除外),但不可更新表;
-
Exclusive Table Lock(X):最强限制,不允许其他事务进行任何类型的DML操作或获得任何类型的锁。
1.3)锁与外键
若外键没有加索引,那么子表可能经常会被锁,会经常发生死锁,并发度也会降低。故而,强烈建议为外键加索引。
1.3.1)锁与未加索引的外键
在以下情形,数据库会在子表上加全表锁:(在外键列上加上索引就好了)
-
子表的外键列上无索引;
-
会话更新父表中的主键(例如删除或更新主键)或聚合行(merge rows)到主表。仅仅insert数据到父表并不会要求对子表的全表锁;
2)DDL锁
数据字典锁(data dictionary lock)在对模式对象进行DDL操作或DDL操作引用该模式对象时,用以保护模式对象的定义。仅进行DDL操作或DDL操作引用的模式对象才会加锁。数据库不会锁定整个数据字典。
Oracle数据库为DDL操作自动加DDL锁。用户不可以显式加DDL锁。例如,若用户创建了一个存储过程,那么数据库会自动在存储过程所引用的模式对象上加锁。DDL锁可保证在存储过程编译通过之前,其所引用的模式对象不会被更改或删除。
2.1)排他DDL锁
排他DDL锁不允许其他会话获取DDL或DML锁。除了以下提及的共享DDL锁,其他DDL操作均会给资源加排他DDL锁。例如,DROP TABLE时不可以对相同表ALTER TABLE,反之亦然。
2.2)共享DDL锁
共享DDL锁禁止冲突的DDL操作,但可以允许类似的DDL操作。例如,在运行CREATE PROCEDURE语句时,事务会在所有引用的表上添加共享DDL锁。其他事务可并行的创建引用相同表的存储过程(获得共享DDL锁),但不可在引用表上加排他DDL锁。
2.3)易破坏的解析锁(breakable parse locks)
解析锁(parse lock)由SQL语句或PL/SQL程序单元所在所引用的模式对象上持有。解析锁用以在引用的对象被更改或删除后,相关的共享SQL区域可变为不可用。之所以解析锁被称为breakable,是因为它并不会阻止DDL操作,且冲突的DDL操作会打破该锁。
在SQL语句执行的解析阶段,解析锁在共享池(shared pool)中获得。只要语句相关的共享SQL区域留存在共享池,该锁就一直存在。
3)系统锁(system locks)
Oracle用多种系统锁以保护内部数据库及内存结构。用户不可用。
3.1)Latches
Latches是简单、低级别的串行机制,用以协调多用户对共享数据结构、对象和文件的访问。Latches在多进程访问共享内存资源时,保护其不被损坏。特别地,latches在以下场景下保护数据结构:
-
多会话并发更新;
-
读写会话同时进行;
-
在被访问时释放旧内存;
通常,在SGA,一个latch会保护多个对象。例如,后台进程例如DBWn和LGWR从共享池分配内存以创建数据结构。为分配这些内存,这些进程使用共享池latch用以序列化访问,以防止两个进程同时检索或更新共享池。在内存分配后,其他进程可能需要访问共享池区域,例如library cache。此场景下,进程仅latch库缓存,而非整个共享池。
与行锁等不同,latch并不能保证会话排队。在latch可用时,第一个请求latch的进程获取对它的排他访问。若进程在循环中重复请求latch,则会产生latch spining,而当进程在更新latch请求前释放CPU资源则会产生latch sleeping。
通常,Oracle进程会在操作或检索数据结构时简短的获得latch。例如,在对某个雇员的工资进行更新的时候,数据库可能需要获取和释放近千个latch。latch的实现与操作系统相关,特别是进程是否等待和等待多久latch方面。
latch数量的提升意味着并行度的降低。例如,过量的硬解析操作会创建library cache latch。V$LATCH视图包含每个latch具体的使用状态信息,包括每个latch等请求及等待次数等。
3.2)Mutexes
mutual exclusive object(mutex)是保护内存中的对象在并发进程访问时不被破坏或老化的一种低级机制。mutex类似于latch,不过latch一般保护一组对象,而mutex仅保护单个对象。
mutex的几点优点:
-
mutex可降低争用的可能性:因为latch保护多个对象,所以在多进程尝试并发访问这些对象时,则可能会变为瓶颈。通过对单个对象而非一组对象的序列化访问,mutex提高了可用性。
-
mutex比latch消耗更少的内存;
-
共享模式下,mutex允许多会话并发引用。
3.3)internal locks
内部锁是比latch和mutex更高级别、更复杂的机制,为多种目的服务。数据库使用以下几种类型的内部锁:
-
字典缓存锁(dictionary cache locks):这种锁周期很短,仅当字典缓存中的行被更新或使用时加在行上。他们保证解析的语句可以读到一致的对象定义。字典缓存锁可以是共享的或排他的。共享锁在解析结束时释放;排他锁在DDL操作结束时释放;
-
文件和日志管理锁(file and log management locks):该锁保护多种文件。例如,内部锁保护控制文件以确保在某个时间点,仅一个进程可更新它;还有的锁用以试用和归档在线重做日志文件;数据文件的锁可确保在多实例场景下以共享模式加载数据库,或在单实例时以排他模式加载;因为文件和日志锁表示文件的状态,故而持锁时间会相当久;
-
表空间和undo段锁(tablespaces and undo segment locks):该锁保护表空间和undo段。例如,访问实例的所有数据库必须对表空间是在线还是离线的状态判断一致;undo段的锁用以确保仅一个数据库实例可以写入段;
7.锁方式:隐式锁定和显式锁定
1)隐式锁定:Oracle自动为对象加锁;
2)显式锁定:使用命令明确为某一对象加锁(很少使用)。
LOCK TABLE没有触发行锁,只有TM表锁:
LOCK TABLE TABLE_NAME IN ROW SHARE MODE NOWAIT; --2:RS
LOCK TABLE TABLE_NAME IN SHARE UPDATE MODE; --2:RS
LOCK TABLE TABLE_NAME IN ROW EXCLUSIVE MODE NOWAIT; --3:RX
LOCK TABLE TABLE_NAME IN SHARE MODE; --4:S
LOCK TABLE TABLE_NAME IN SHARE ROW EXCLUSIVE MODE; --5:SRX
LOCK TABLE TABLE_NAME IN EXCLUSIVE MODE NOWAIT; --6:X
Oracle数据库通过锁自动确保数据的并发、数据完整性和语句级别的读一致性。不过,也可以手动覆写数据库的默认锁机制。一般在以下场景下适用覆写默认的锁机制:
-
应用程序需要事务级别的读一致性或可重复读:即事务期间必须保证数据的一致,不要读到其他事务对数据的变更。可通过使用显式锁定、只读事务、串行化事务或覆写默认锁机制等方式实现事务级别的读一致性;
-
应用程序需要事务具有最高的资源访问权限,而无需等待其他事务;
可在会话或事务级别覆写Oracle数据库的自动锁机制。会话级别,可通过ALTER SESSION语句设置所需的事务隔离级别;事务级别,包含以下语句的事务会覆写Oracle数据库的默认锁机制:
-
SET TRANSACTION ISOLATION LEVEL
-
LOCK TABLE
-
SELECT...FOR UPDATE
在事务结束或回滚时释放所持有的锁。
更改默认锁机制的时候,需要考虑:保证数据的完整性;数据的并发访问可接受;死锁问题;
8.用户定义锁
使用Oracle数据库锁管理服务,可自定义锁。例如,可定义一个对文件系统上信息日志串行访问的锁。用户定义锁不会与Oracle数据库所冲突,因为他们是以UL开头定义的。
使用DBMS_LOCK包的存储过程自定义锁。可在PL/SQL块中包含以下功能的语句:
-
请求特定类型的锁;
-
为锁分配唯一名称;
-
改变锁类型;
-
释放锁;
以上是关于Oracle中的锁的主要内容,如果未能解决你的问题,请参考以下文章