Data Concurrency andConsistency(数据的并发和并行)
这一章解释了Oracle数据库在多用户环境中,怎样维护数据一致性
这一张包含如下小节:
·Introduction to DataConcurrency and Consistency
·Overview of OracleDatabase Transaction Isolation Levels
·Overview of theOracle Database Locking Mechanism
·Overview ofAutomatic Locks
·Overview of ManualData Locks
·Overview ofUser-Defined Locks
Introduction to DataConcurrency and Consistency(介绍数据并发和数据一致性)
在单用户数据库,一个用户可以数据的时候 可以不用担心是否会有别的用户在同一时间修改同一数据。
而在多用户数据库,多个同时发生的事务中的语句,可以更行同一个数据。事务同事执行时必须要产生有意义的,以及一致的返回结果。因此,一个多用户数据库必须提供以下功能:
·Data concurrency(数据并发),它保证了多用户可以同时访问同一数据。
·Data consistency(数据一致),它保证了每个用户看到的数据都是一致的,包括用户自己事务中显式的修改,以及其他用户所有提交的事务。
为了说明在事务并发运行时的一致性行为,数据库科研人员定义了一个事务隔离型模型,叫做serializability(可串行化)。可串行化事务在一种使其看起来好像没有其他用户正在修改数据库中的数据的环境中运作。
虽然这种程度的事务隔离通常是不错的,但是如果多个应用都运行在可串行化模型下会严重影响应用的吞吐能力。并发完全隔离运行的事务可能意味着在一个事务查询一个表时,另外一个事务无法对这个表进行插入操作。简短截说,真实世界中通常要在完美的事务隔离和性能之间做个折中考虑。
Oracle数据库使用multiversionconsistency model(多版本一致模型)和各种的锁和事务来维护数据一致性。这种情况下,数据库可以提供数据的显示 给多个并发用户。每个显示,在某个时间点都是一致的。因为在同一时间可以存在数据块的好几个版本,所以事务可以读取在查询开始时间点的已经提交数据的版本,然后在这个开始时间点一致的数据。
Multiversion Read Consistency(多版本读一致性)
在Oracle数据库,多版本指同时存在数据的多个版本。Oracle数据库维护多版本读一致性,它意味着数据库查询,需要有下面特性:
·Read-consistent queries (读一致性查询)
查询返回的数据是已经提交的,在某一时间点一致的。
重要提醒:Oracle数据库永远不允许dirty reads(脏读),
脏读是意思是,一个事务查询出了另一个事务还没提交的数据。
解释一下脏读存在的问题,假设一个事务更新了一列的值,还没提交。第二个事务读到了这个更新后的脏值。这时第一个事务回滚了,现在列上的值还是原来的值,但是第二个事务继续使用着刚才更新后的值,这会损坏数据库。脏读危害数据完整性,破坏外键,以及忽略唯一约束。
·Nonblocking queries(查询不阻塞)
数据的读和写之间,不会互相阻塞。
Statement-Level ReadConsistency(语句级读一致性)
Oracle数据库总是强制保证 语句级读一致性,它保证了单个查询返回的数据是在某一个时间点已经提交以及一致的。这个时间点取决于事务隔离级别以及查询的性质:
·在 read committed 隔离级别,这个时间点是语句打开时的点,举个例子,如果一个SELECT语句在SCN1000的时候打开,那么这个语句获取的数据就是在SCN 1000时一致的。
·在serializable或read-only 事务,这个点是 事务开始时的时间。举个例子,如果一个事务在SCN 1000时开始,以及有多个SELECT发生在这个事务,那么每个SELECT返回的数据
都是在SCN 1000时一致。
·在Flashback Query(闪回查询)语句(SELECT .... AS OF),SELECT语句显式指定了这个时间点,举个例子,你可以查询一个表在上一个周四的下午两点时的显示的数据。
Transaction-LevelRead Consistency(事务级读一致性)
Oracle数据库同样可以对一个事务中的所有查询都提供一个一致性,称之为transaction-level read consistency(事务级读一致性),在这种情况下,事务中的每个语句,看到的数据都同一个时间点的一致性数据。这个点是这个事务开启时的点。
serializable事务中的查询只能看到自己本事务发出的修改。举个例子,一个事务更新了employees,然后查询employees,是可以看到修改。事务级读一致性产生了可重复读,且不产生幻影读。
ReadConsistency and Undo Segments(读一致性与回滚段)
为了管理多版本多一致性模型,在一个表同时发生读和更新时,数据库必须创建这个数据的读一致性的集。
Oracle数据库通过UNDO data来完成这个目标。
任何时候用户修改了数据,Oracle数据库会创建undo 条目,它写在undo segments(回滚段)里。undo segments里面包含着未提交的事务,或者刚刚提交事务 修改位置 之前的旧值。因此,同一个数据在不同时间点的不同版本,可以在数据库中同时存在。数据库可以使用数据在不同时间点的快照,来提供读一致性 以及保证 nonblocking queries(查询不阻塞)。
在单节点或ORACLE RAC环境 读一致性都是保证的。Oracle RAC使用 cache-to-cache 块传送 称之为Cache Fusion的机制,来传送数据块的 读一致性版本 从一个instance 到另外一个。
ReadConsistency:Example(读一致性的案例)
下图显示了一个查询使用undo数据提供语句级读一致性(在read commited 隔离级别)
数据库为某个查询检索数据时,数据库保证每个块中的数据反映了该查询开始时的内容。
如果有需要,数据库会回滚块中的改变去重构一个块 它是查询开始时 这个时间点的这个块。
数据库使用一个叫SCN的机制来保证事务的 排序(顺序)。当一个SELECT进入的执行阶段,数据库确定查询开始执行时所记录的SCN。上图,SCN是10023.这个查询只看在SCN 10023时已经提交的数据
上图中,块的SCN号在10023之后的表示已经发生修改的块,像上面显示的,是两个块,SCN是10024. SELECT语句需要一个已经提交块的一致性版本。数据库复制一个当前数据块,变成一个新的buffer(块)然后引用undo数据去重构这个块的之前的版本。这些被重构出来的块叫做consistent read(CR)clones
在上图中,数据库创建了两个CR clones:一个块与SCN 10006时一致 另一个块与SCN 10021时一致。对于这个查询,数据库返回重构后的数据。在这种情况下,Oracle数据库预防了脏读。
ReadConsistency and Transaction Tables(读一致性和事务表)
数据库使用一个 事务表,同样也叫做interestedtransaction list(ITL)。用来确定当一个数据库开始修改一个块时,上面时候有事务未提交。每个段块的块头都有一个事务表(ITL)。
事务表中的条目 描述了哪个事务有行锁,以及块中的哪些行包含提交和未提交的更改。事务表指向的是undo segment,它提供了当数据库做这个修改时的时间信息。
在某种意义上来说,块头包含了一个对块中任意行有过影响的 事务们的历史。CREATE TABLE和ALTER TABLE语句的INITRANS参数控制了 事务历史保存的数量。
Locking Mechanisms(锁机制)
一般来说,多用户数据使用一些形式的数据锁定 来解决数据并发性,一致性,以及完整性的问题,Locks是一种机制 来预防多个事务访问同一资源而导致的破坏性内部交互操作。
ANSI/ISOTransaction Isolation Levels(ANSI/ISO事务隔离级别)
在ANSI和ISO/IEC两个标准都采纳的SQL标准中,定义了 四个级别的transaction isolation(事务隔离)。这些级别 对于事务处理吞吐量有不同程度的影响。
这些隔离级别定义是为了 预防两个并发执行事务肯定会产生的一些现象。这些需要预防的现象有:
·Dirty reads(脏读)
一个事务读到了另外一个事务修改但还没提交的数据
·Nonrepeatable(fuzzy) reads(不能重复,失真 读)
一个事务重复读了刚才已经读取过的数据,然后又发现了别的事务提交了的已经修改的数据。举个例子,一个用户查询了一行,之后又查询了一下相同的行,却发现数据已经修改了
·Phantom reads(幻影读)
一个事务重新执行了一下查询,返回了的行集满足了查询条件,并查到了其他已经提交的事务中插入了一行 符合查询条件的行。
举个例子,一个事务查询了雇员的数量,五分钟以后它执行了相同的查询,但是现在数量增长了1,因为另外一个用户插入了一行。更多的数据符合了之前的查询标准,但是和模糊读不一样,之前的数据没有发生变化。
根据运行在某个隔离级别所允许发生的现象,数据库定义了四个隔离级别
Isolation Level | Dirty Read | Nonrepeatable Read | Phantom Read |
Possible | Possible | Possible | |
Read committed | Not possible | Possible | Possible |
Repeatable read | Not possible | Not possible | Possible |
Serializable | Not possible | Not possible | Not possible |
Oracle数据库提供了 read committed(默认)和serializable 两种隔离级别。同样,数据库还提供了只读模式
Overview of Oracle DatabaseTransaction Isolation Levels(Oracle数据库事务隔离级别介绍)
上面的表总结了ANSI标准 中的事务隔离级别。该标准定义了各个隔离级别所允许或必须防止的现象。Oracle数据库提供下面的事务隔离级别:
·Read Committed Isolation Level(已提交读隔离级别)
·Serializable Isolation Level(串行化隔离级别)
·Read-Only Isolation Level(只读隔离级别)
ReadCommitted Isolation Level(已提交读隔离级别)
在read committed isolation level(默认),每个事务中执行的查询看到的数据都是在查询(注意,这里是查询,不是事务)之前已经提交的。这个隔离级别在一些事务可能会冲突的数据库环境中是适用的。
一个在read committed事务中的查询 避免了读到当查询已经发生后更提交的数据。举个例子,如果一个查询扫描一个百万行的表,扫到了一半,而且如果另一个事务这时修改了第950000行,且提交了,那么之前的查询扫描到第950000行的时候 并不会显示出这个修改。
然后,因为数据库并不阻止其他事务修改一个事务所读取的数据,其他事务可能会在查询执行期间修改。因此,一个事务中运行同样的查询两次,可能会遇到模糊读和幻影读
ReadConsistency in the Read Committed Isolation Level(在已提交读隔离级别中的读一致性)
每个查询都会提供一个一致性的结果集,不需要用户做什么操作,就能保证数据的一致性。
一个隐式查询,比如说在UPDATE语句中WHERE条件隐含的查询,也保证是个一致性的结果集。不管怎样,在DML语句中隐式的查询不会看到它自己做的修改,但是会看到修改之前的数据。
如果一个SELECT 包含一个PL/SQL函数,那么PL/SQL函数之中的sql运行 也会引用语句级的读一致性。而不是外面这个SELECT的读一致性。举个例子,一个函数可以访问一个表中的已经修改的数据和其他用户提交的数据。函数中每个SELECT的执行,都会建立一个新的读一致性快照。
ConflictingWrites in Read Committed Transactions(已提交读中的写冲突)
在一个已提交读事务,当事务要更改一行,而这行已经被另外一个未提交事务所修改(blocking transaction),这时会发生写入冲突(conflicting write)。
这个已提交读事务会等blocking transaction 结束然后释放它的行锁。有以下选项:
·如果blocking transaction回滚,那么waiting transaction会修改之前被locked的行,仿佛其他事务从来没出现过。
·如果blocking transaction提交然后释放锁,那么waiting transaction会继续进行它的打算,更新当前改变后的行。
下表显示了transaction 1(它可以是serializable 或 read committed),与 read committed transaction2的交互。下表显示的是典型的情况,叫做lost update.transaction1做的更新,没有在表中 即使transaction 1提交了它。设计一个策略来解决lost update是一个应用开发的重要部分。
Session 1 | Session 2 | Explanation |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 | session 1查询了Banda,Greene以及Hintz的工资,没有找到名叫Hintz的人。 | |
SQL> UPDATE employees SET salary = 7000 WHERE last_name = 'Banda'; | session 1通过更新Banda的工资来开启了一个事务。事务1的默认隔离级别是READ COMITTED | |
SQL> SET TRANSACTION ISOLATION LEVEL READ COMMITTED; | Session2 开始一个事务2,显式设置隔离级别为READ COMMITTED | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 | 事务2 查询Banda,Greene以及Hintz的工资,Oracle数据库使用读一致性显示了Banda的工资还是为事务1这个未提交事务之前的状态。 | |
SQL> UPDATE employees SET salary = 9900 WHERE last_name = 'Greene'; | 事务2可以成功更新Greene的工资,因为事务1只锁了Banda的一行 | |
SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) VALUES (210, 'Hintz', 'JHINTZ', SYSDATE, 'SH_CLERK'); | 事务1给雇员Hintz插入了一行,但没有提交 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9900 | 事务2查雇员Banda,Greene,Hintz的工资 | |
SQL> UPDATE employees SET salary = 6300 WHERE last_name = 'Banda';
-- prompt does not return | 事务2企图更新Banda的行,而这行当前已经被事务1锁住了,产生了一个写冲突,事务2要等待 直到事务1结束 | |
SQL> COMMIT; | 事务1提交,结束了这个事务。 | |
1 row updated.
SQL> | Banda行的锁被释放掉了,所以事务2继续处理它对Banda的工资的更新 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6300 Greene 9900 Hintz | 事务2查询雇员Banda,Greene,以及Hintz的工资,在已经提交事务1中插入的Hintz,现在事务2也可以看到了,事务2还看到了它对Banda工资的更新。 | |
COMMIT; | 事务2提交,结束了它的事务。 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6300 Greene 9900 Hintz | Session 1查询了Banda,Greene和Hintz的行,Banda的工资是6300,它是由事务2更新的。而由事务1更新的7000 现在"lost(丢)"了 |
Serializable Isolation Level(串行化隔离级别)
在串行化隔离级别中,一个事务可看到的在事务(不是语句)开始时已经提交的,以及事务自己做的修改。一个可串行化事务在一个环境中操作,使环境看起来好像没有其他用户在修改数据。
串行化隔离所适合的环境:
·超大的数据库,很小的事务,每个事务只更新几行。
·在两个并发事务修改相同行的几率相对较低的环境
·有较长的事务,但主要是只读事务的时候
在串行化隔离中,读一致性从通常的语句级拓展成整个事务级。事务中读取的任何行,再次读时都保证是相同的。在事务期间,任何查询的值 都保证只有一份,这时其他事务做的修改,在这个事务是不体现的,和语句运行多长时间没有任何关系。串行化事务不会遇到脏读,模糊读,或幻影读。
Oracle数据库允许串行化事务修改数据,不过如果有其他事务修改,那么这个事务必须在串行化事务开始之前就提交。当一个串行化事务企图修改一个行,而这个被别的事务修改,且在串行化事务开始之后才提交,这时数据会包一个错
ORA-08177: Cannot serialize access for thistransaction
当一个串行化事务报错ORA-08177,一个应用可以做一些动作。包括如下:
·提交事务
·执行其他不同的语句,比如回滚到之前建立的savepoint
·回滚整个事务
下标显示一个串行化事务和其他事务是如何交互的。如果一个串行化事务不去尝试修改其他事务在串行化事务开始后提交的数据,那么serialized access 问题可以避免。
Session 1 | Session 2 | Explanation |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 | 事务1查询Banda,Greene,以及Hintz的工资,没有找到一个叫Hinz | |
SQL> UPDATE employees SET salary = 7000 WHERE last_name = 'Banda'; | session 1 通过更新Banda的工资开启事务,默认是READ COMMITTED | |
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; | Session2开始事务2,设置它为SERIALIZED隔离级别 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 | 事务2查询Banda,Greene,以及Hintz的工资,Oracle数据库使用读一致性显示Banda的工资(事务1还没提交) | |
SQL> UPDATE employees SET salary = 9900 WHERE last_name = 'Greene'; | 事务2更新Greene的工资成功。因为只有Banda的行是被锁的。 | |
SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) VALUES (210, 'Hintz', 'JHINTZ', SYSDATE, 'SH_CLERK'); | 事务1插入一行(Hintz) | |
SQL> COMMIT; | 事务1提交了它的操作,结束了事务。 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9500 Hintz | SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9900 | session 1查询雇员Banda,Greene和Hintz的工资。显示了已经被事务1提交的值,没显示事务2 未提交的更新(Greene)
事务2查询雇员Banda,Greene和Hintz的工资,Oracle数据库读一致性保证了事务1提交的更新和插入,在事务2中不显示。事务2只看到它自己的修改。 |
COMMIT; | 事务2提交 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9900 Hintz | SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9900 Hintz | 两个session都查询Banda,Greene,以及Hintz的工资,每个session都能看到事务1和事务2提交的结果。 |
SQL> UPDATE employees SET salary = 7100 WHERE last_name = 'Hintz'; | session1 通过更新Hintz的工资来开启事务3,默认的隔离级别是READ COMMITED | |
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; | session2开启事务4,设置SERIALIZABLE隔离级别 | |
SQL> UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz';
-- prompt does not return | 事务企图去修改Hintz的更新,但是这行已经被事务3锁了。事务4顺序排在事务3的后面。 | |
SQL> COMMIT; | 事务3提交了更新,结束了事务 | |
UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz' * ERROR at line 1: ORA-08177: can't serialize access for this transaction | 事务3的提交导致事务4的报错(ORA-08177),这个问题出现是因为事务3提交Hintz的修改,在事务4开始以后。 | |
SQL> ROLLBACK; | session2回滚事务4,结束了这个事务。 | |
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; | session 2 开始事务5 设置它为SERIALIZABLE隔离级别 | |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7100 Greene 9500 Hintz 7100 | 事务5查询Banda,Greene和Hintz的工资。Hintz的工资是事务3更新的结果。 | |
SQL> UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz';
1 row updated. | 事务5更新Hintz的工资为其他值,因为之前更新Hintz的事务在事务5开始之前就提交,那么serialized access问题被避免
注意:如果一个不同的事务更新以及提交Hintz行在事务5开始以后,那么serialized access问题依然会出现 | |
SQL> COMMIT; | session2提交更新。结束这个事务 |
Read-Only Isolation Level(只读隔离级别)
只读隔离级别和串行化隔离级别很像,但是只读事务中,不允许有修改操作,除非是SYS用户。因此,只读事务不会有ORA-08177 错误,只读事务在产生一个报告时很有效(内容必须保证是事务开始时一致的)
Oracle数据库完成通过使用UNDO segment 重构数据块来完成读一致性。因为UNDO segment通过一种循环方式使用的,那么数据库可以覆盖undo数据。这时长时间查询的报表会有风险,就是需要完成读一致性的undo数据被其他事务覆盖了,这时会报snapshot too old错误,设置undo retention period(它是旧undo数据被覆盖前 保留的时间),恰当的避免这个错误。
Overview of the OracleDatabase Locking Mechanism(Oracle数据库锁机制概述)
lock是一种机制,用来预防 事务之间访问通共用的数据时 的破坏性交互动作(交互动作会错误的更新数据,或错误的更改底层数据结构)。locks在维护数据库并发性和一致性的过程中 扮演了一个至关重要的角色。
Summaryof Locking Behavior(锁定的行为总结)
数据库维护一些不同类型的锁,取决于获取锁的操作。一般来说,数据库使用两种类型的锁:exclusive locks 和share locks。一个资源上(如一行或一个表)只能同时存在一个exclusive锁,但是一个资源上可以同时存在多个share locks
locks会影响读取者和修改者的交互,读取者是一个对某资源的查询语句,而修改者是对某资源的修改语句。
下面几条规则总结了Oracle数据库中 读取者和写入着的锁定行为:
·只有一个修改者在修改这一行时,这一行才会被锁
当一个语句更新了一行,则事务至获取了对这一行的锁。通过在 行级别 锁定表中数据,数据库实现最小争用。在正常情况下,数据库不会将行锁升级为块锁,甚至表锁。
·某行的修改者会阻塞并发的同一行的其他修改者
如果一个事务在修改某一行中,那么行锁会阻止其他事务在这一时间修改这一行。
·读取者永远不会堵塞修改者
因为某行的读取者不会对这行上锁。一个修改者可以修改这行。有个例外,SELECT ... FOR UPDATE语句,它是一个特殊类型的SELECT语句,会在它读的时候锁定这一行。
·一个修改者永远不会堵塞读取者
当一个修改者正在修改一行时,数据库通过使用undo数据来对读取者提供读一致性。
注意:在一些非常特殊的案例中,读可能会等待同一个块的写
Useof locks(锁的使用)
在一个单用数据库,锁是不需要的。因为之后一个用户在修改信息。然而,当多用户访问和修改数据是,数据库必须提供一种方法来阻止对一个数据的并发修改。锁完成了下列重要数据库需求:
·Consistency 一致性
一个session正在查看或修改的数据不能被其他session修改,直到用户修改完成。
·Integrity完整性
数据和结构必须按照正确的顺序来反应对它们所做的所有修改。
Oracle数据库通过锁机制 提供了事务之间数据的并发性和一致性,以及完整性。
锁是根据需要自动执行的 不需要用户操作。
对某行的并发更新可以解释为什么需要锁。在下面案例中,一个简单的 基于网页的应用将雇员的emaill和电话号码呈现给终端用户。应用使用了如下的UPDATE语句:
UPDATE employees
SET email = ?, phone_number = ?
WHERE employee_id= ?
AND email = ?
AND phone_number = ?
在上面的更新语句中,在WHERE子句中的emaill和phone_number是原始的,未经修改的值。这个更新保证 应用修改的行在应用上次读和写时是没修改过的。这种情况下,引用避免了lost update 数据库问题,既一个用户更新覆盖了另外一个用户的更新
下表显示了当两个session在差不多同一时间更新employees表中相同的行时
Time | Session 1 | Session 2 | Explanation |
t0 | SELECT employee_id, email, phone_number FROM hr.employees WHERE last_name = 'Himuro';
EMPLOYEE_ID EMAIL PHONE_NUMBER ----------- ------- ------------ 118 GHIMURO 515.127.4565 | 在session 1,hr1用户查询hr.employees中Himuro的条目,然后显示了employee_id,email,以及电话号码等属性。 | |
t1 | SELECT employee_id, email, phone_number FROM hr.employees WHERE last_name = 'Himuro';
EMPLOYEE_ID EMAIL PHONE_NUMBER ----------- ------- ------------ 118 GHIMURO 515.127.4565 | 在session2,hr2用户查询了hr.employees的Himuro记录,显示了employee_id,电话号码,email等属性。 | |
t2 | UPDATE hr.employees SET phone_number='515.555.1234' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.127.4565';
1 row updated. | 在session1,hr1用户将这行的电话号码更新为515.555.1234,它获取了这行的锁。 | |
t3 |
| UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.127.4565';
-- SQL*Plus does not show -- a row updated message or -- return the prompt. | 在session2,hr2用户企图更新相同的行,但被阻塞了,因为hr1正在处理这行。 hr2发出的更新基本上和hr1发出的更新是同时的 |
t4 | COMMIT;
Commit complete. | session1 提交 提交会对Himuro行造成永久的更改,然后将对session2的阻塞放开。 | |
t5 | 0 rows updated. | 在session2,hr2用户发现GHIMURO行已经被修改过了,而且和update的where(谓词)不再匹配 因为谓词条件不再匹配,所以session2没有更新任何记录。 | |
t6 | UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.555.1234';
1 row updated. | 在session1,hr1用户发现刚刚对GHIMURO行更新了一个错误的电话号码。用户开始了一个新的事务然后更新电话号码为515.555.1235,这个东西把GHIMURO这行加了锁。 | |
t7 | SELECT employee_id, email, phone_number FROM hr.employees WHERE last_name = 'Himuro';
EMPLOYEE_ID EMAIL PHONE_NUMBER ----------- ------- ------------ 118 GHIMURO 515.555.1234 | 在session2,hr2用户查询hr.employees中的Himuro记录,其中显示的电话号码是之前session1提交的那个修改.Oracle为了保证一致性,并没有显示t6这个未提交事务所做的修改. | |
t8 |
| UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.555.1234';
-- SQL*Plus does not show -- a row updated message or -- return the prompt. | 在session2,hr2用户企图更新相同的行,但是被阻塞,因为hr1当前正在处理这一行. |
t9 | ROLLBACK;
Rollback complete. | 在session1 rollback掉事务. | |
t10 | 1 row updated. | 在session2,更新电话号码的操作成功了,因为session 1 rollback了,那么GHIMURO的行信息依然与update中的谓词相匹配,所以更新成功 | |
t11 | COMMIT;
Commit complete. | session2提交了修改. |
Oracle数据库在执行sql的时候会自动获取所需要的锁.举个例子,在数据库允许session修改数据之前,session必须要先锁住数据。锁给了session独占控制这个数据的权利,这样其他事务就不能修改这条被锁的行,直到锁被释放
因为Oracle数据库的锁定机制和事务控制紧密相连,应用设计的时候只需要定义恰当的事务类型,Oracle数据库会自动管理锁。用户永远不需要显式锁定任何资源,哪怕oracle数据库是提供了手动锁定数据的方法。
下面小节解释了oracle数据库是如何完成数据并发的重要底层原理
LockModes(锁模式)
Oracle数据库自动使用最低限制级别去提供最高程度的数据并发 也同样提供了非常安全的数据完整性。限制级别越低,则会有更多的可用数据提供给其他用户,相反,更高的限制级别,则其他事务获取锁的类型将更多受到限制
Oracle数据库使用两种类型的锁:
·Exclusive lock mode(独占锁模式)
这种类型防止相关的资源成为共享的。一个事务获取了一个独占锁(修改数据),这个事务锁定的期间只有这个事务可以修改这个资源。
·Share lock mode (共享锁模式)
这种模式允许相关资源成为共享的。多用户读数据时可以共享数据,获取共享锁是预防一个写入者要获取独占锁。一些事务可以在同一个资源上都获取都共享锁。
假设一个事务使用SELECT ... FOR UPDATE语句来选择一个表中的一行。这个事务获取了一个独占行锁以及一个共享表锁。行锁允许其他session修改除了这行以外的其他行,而表锁阻其他session修改表结构
LockConversion and Escalation(锁转换和锁升级)
Oracle数据库在需要的时候会执行锁转换。在锁转换中,数据库自动将一个低限制的表锁转换为高限制的表锁。
举个例子,假设一个事务执行SELECT ... FOR UPDATE查出一行,之后对这行进行更新。在这个情况,数据库自动转换行共享表锁到行独占表锁。一个事务中所有的插入,更新或删除行,本事务都会持有这个行锁。因为获取的行锁已经是最高限制级别了,所以是不需要锁转换的,也不执行锁转换。
锁转换和锁升级是不一样的,锁升级发生在,当某个颗粒度级别持有很多锁(比如行),数据库将锁升级到更高的颗粒度级别(比如表)。如果一个用户 锁了一个表中的很多行,那么一些数据库会自动把行锁升级为表锁,这样锁的数量就减少了,但是限制程度增加了。
Oracle数据库永远不会升级锁。锁升级极大的增加了死锁出现的可能性。假设一个系统在事务1执行的中间打算升级锁,但是不能升级 ,因为事务2获取了一些相关的锁。如果事务2这时同样申请相同数据的锁升级 才可以继续往下执行,那么死锁就会出现。
LockDuration(锁持续时间)
当某些事件发生,导致事务不再需要这些资源时,Oracle数据库自动释放锁。绝大部分情况,事务中的语句获取并持有了锁,持续到事务结束。这些锁阻止了破坏性干涉,比如脏读,更新丢失,以及并发事务发出的破坏性DDL语句
注意:因为未索引外键而在子表上发生的表锁,会在语句持续时间持有,而不是事务持续时间。同样,像“Overview of User-Defined Locks”解释的,DBMS_LOCK包使用户自定义的锁 可以随意持有或释放(根据你的意志),持有时间甚至可以超越事务的边界。
事务中语句获取的锁,在事务结束时 Oracle会全部释放(commit和rollback)。当事务中存了一个savepoint,在其后获取了锁,之后又回滚到savepoint,这个锁也会被释放掉。
但是,只有事务没有等待之前被锁资源的,可以获取现在已经可以使用的锁,而之前已经在等待的事务,叫继续等待到原始事务完全提交或回滚。
Locksand Deadlocks(锁和死锁)
两个事务或者多个事务 都在等待被对方所持有的资源,这种情况叫做死锁。死锁会阻止某些事务继续工作。
Oracle数据库会自动检测死锁,并通过回滚其中一个语句来解决死锁。数据库会对遭遇语句级回滚的事务返回一条相应的消息。回滚的语句是属于检测到死锁这个事务的。通常来说,收到信号的事务应该要显示回滚,不过它也可以在等待之后重试被 语句级回滚 的语句。
下表解释了两个事务中的死锁。
Time | Session 1 | Session 2 | Explanation |
t0 | SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 100;
1 row updated. | SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 200;
1 row updated. | session 1 开始 事务1 然后更新employee 100的 工资,session2开始事务2更新employee 200的工资。现在没有任何问题。 |
t1 | SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 200;
-- prompt does not return | SQL> UPDATE employees set salary = salary*1.1 WHERE employee_id = 100;
-- prompt does not return | 事务1现在企图更新employee200的行,这行现在被事务2锁着的。事务2企图更新employee100的行,这行是被事务1锁着的 现在有了一个死锁,因为两个事务都不能获取它需要的资源然后往后执行或者完成。不管每个事务等待多长时间,冲突依然存在。 |
t2 | UPDATE employees * ERROR at line 1: ORA-00060: deadlock detected while waiting for resource
SQL> | 事务1发出死锁信号,并且回滚t1所发的更新语句。但是,t0所发的更新语句并没有回滚。session 1中收到了错误提示。 注意:在死锁中,只有其中的一个session会有死锁错误,但是任何一个事务都可能获得这个错误。 | |
t3 | SQL> COMMIT;
Commit complete. | session 1提交了在t0发出的更新操作,结束了事务 1.在t1发的更新没有成功。 | |
t4 | 1 row updated.
SQL> | 事务 2在t1时提交的更新(之前被事务1阻塞),现在执行成功,返回提示。 | |
t5 | SQL> COMMIT;
Commit complete. | session 2提交了 t0和t1所做的更新。结束了事务2. |
当事务中显示手动加锁,覆盖了Oracle数据库原本的锁时,最经常发生死锁。因为Oracle数据库不会升级锁,不会有读锁,使用的是行锁(而不是页锁),所以很少出现死锁。
Overview of Automatic Locks
Oracle数据库会为事务自动锁定资源,防止其他事务对相同数据执行一些需要独占访问的操作。数据库根据执行的操作和对应的资源的不同限制程度来自动获取不同的锁。
注意:执行简单的读时,数据库永远不会对这些行上锁。
Oracle数据库的锁 分为以下几个类别
Lock | Description |
DML Locks | 保护数据,举个例子,表锁 锁整个表,而行锁 则锁指定的行 |
DDL Locks | 保护schema objects的结构--举个例子,表和视图的定义 |
System Locks | 保护内部的数据库结构,比如数据文件,latchs,mutexes以及内部锁,这些都是完全自动的 |
DMLLocks(DML 锁)
一个DML锁,同样也叫做data lock(数据锁),保证数据被多用户并发访问时的完整性。举个例子,一个DML锁可以防止出现在网上书店中,两个客户同时购买了最后一本书的情况。DML锁防止多个互相冲突DDL,DML操作对数据的破坏性
DML语句自动获取两种类型的锁:
·Row Locks(TX)
·Table Locks(TM)
在下面小节中,每个锁或锁模式后面括号中的缩写,就是在OracleEM的Locks Monitor中使用的缩略语。EM中可能用TM来任何表锁,而不是显示哪种表锁(比如RS 或SRX)。
RowLocks(TX)行锁
行锁,同样也被称为TX锁,是在表中某行上面的锁。一个事务中通过INSERT,UPDATE,DELETE,MERGE或SELECT....FOR UPDATE修改的任何行,上面都会加行锁。行锁会持续到事务结束(提交或回滚)
行锁主要是提供一个排序机制来防止两个事务同时修改一行。数据库对修改过的行 加的都是独占模式的行锁,这样其他事务就没办法在同时对这行修改(直到持有锁的事务提交或回滚)。行锁提供了尽可能细粒度的锁定,这样数据库就能尽可能提供更好的并发性和吞吐。
注意:如果一个事务因为数据库异常关闭,会先进行块级恢复以使行可用,然后再进行整个事务的恢复。
如果一个事务获取了一行的行锁,那么事务同样获取了包含这行的表 的表锁。这表锁将阻止冲突性DDL操作(它或许会覆盖当前事务中的修改)。
下面图解了一个表中一行的更新。Oracle数据库自动防止一个独占锁到被更新的行,以及一个子独占锁到表。
RowLocks and Concurrency(行锁和并发性)
下表解释了Oracle数据库通过行锁实现并发性。三个session同时查询相同的行,session1和2继续更新了两条不同的行(不提交),而session3没有做任何更新。每个session都能看到它自己做的未提交更新,但是不能看到其他session的未提交更新。
Time | Session 1 | Session 2 | Session 3 | Explanation |
t0 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 | 三个不同的session同事查询employee100,101的ID和薪资。每个查询返回的结果都是相同的 |
t1 | UPDATE hr.employees SET salary=salary+100 WHERE employee_id=100; |
|
| session1更新了雇员100的工资,但没有提交。在更新的过程中,写入者只需要获取更行的行级别锁,从而防止其他写入者修改这一行。 |
t2 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 612 101 600 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 | 每个session同事查之前的查询。session1显示的是t1更新的612.而session2和session3的读取者立刻返回了行,没有等到session1结束它的事务。数据库使用多版本读一致性来显示session1 更新之前的工资(512)。 |
t3 |
| UPDATE hr.employees SET salary=salary+100 WHERE employee_id=101; |
| session2更新employee101的工资,但是没有提交。在更新中,写入者只获取了更新行的行级锁,防止其他写入者修改这一行。 |
t4 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 612 101 600 | SELECT employee_id, salaryFROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 700 | SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 | 每个session同事执行之前的查询。session1显示的是612(t1更新),但是没显示session2对employee101做的更新。session2的读取者显示了session2自己做的更新,但是没显示session1做的更新。session3的读取者使用读一致性来显示session1和session2做修改之前的值。 |
Storageof Row Locks(行锁的存储)
有一些数据库使用lock manager(锁管理器)在内存中维护了一个(锁的列表),和它们不同,Oracle数据库将锁信息存储到包含了 被锁行 的 data block(数据块)中
数据库使用排队算法来获取行锁。如果一个事务需要一个行(未被加锁)的锁,那么事务将放置一个锁到数据块中。这个事务修改的每行,都指向一个存储在block header(块头)中的事务id副本。
当一个事务结束,事务id依然在block header。如果一个不同的事务想要修改这行,那么它会使用事务ID去确认锁时候在使用。如果锁是active的,那么session会要求锁释放时得到提醒。否则 ,这个事务获取这个锁。
TableLocks(TM)
表锁,同样也叫TM锁,当事务通过INSERT,UPDATE,DELETE,MERGE,SELECT..FOR UPDATE修改表中数据,或者直接用LOCK TABLE语句,这时会获取TM锁。DML操作获取表锁,是保证DML对这个表的访问,以及防止会发生冲突DDL语句。
表锁可以以下几种模式持有:
·Row Share(RS) (行共享表锁)
这种锁,同样叫做subshare table lock(SS),表明 事务在这个表上锁住了一行,并且打算更新它们。RS锁是表锁中最小约束性的了。提供了表级别的最高并发
·Row Exclusive Table Lock(RX)(行排他 表锁)
这个锁,又叫做 subexclusive table lock(SX),一般表明所在表中有事务更新了行,或者发布了SELECT ... FOR UPDATE。一个SX锁允许其他事务同时查询,插入,更新,删除,或者锁行。因此多个事务可以同时获取在同一个表上获取SS和SX锁。
·Share Table Lock(S)(共享表锁)
一个事务持有了S锁,允许其他事务查询这个表(除了使用SELECT...FOR UPDATE),但更新操作只允许单个事务持有该锁时才允许。因为多个事务可能会并发持有这个锁,那么持有了这个锁并不足以保证这个事务可以修改表
·Share Row Exclusive Table Lock(SRX)(共享 行排他 表锁)
这个锁,同样为叫share-subexclusive table lock(SSX),它比S锁的限制更多。一次只能有一个事务获取SSX锁。一个事务持有SSX锁,允许其他事务查询表(除了for update)不允许更新表。
·Exclusive Table Lock(X)
这个锁有最大的限制,禁止其他事务执行任何DML语句或放置任何类型的锁 到这个表。
Locksand Foreign Keys(锁和外键)
Oracle数据库对外键所引用的父键提供最大的并发控制。锁的行为依赖于子表外键列是否有索引。如果外键没有索引,那么子表会更频繁的被锁,出现死锁,以及减小并发。因为这个原因,外键列最好都建索引,唯一的例外就是引用的父键永远不会更新或删除。
Locksand Unindexed Foreign Keys(锁和未索引外键)
当下面两个条件都符合,那么数据库将会在子表获取全表锁。
·子表的外键列没有存在索引
·一个session修改父表中的 被引用建(父键)。对父表进行插入不会获取子表的全表锁。
假设hr.departments表 是hr.employees表的 父表,而hr.employees表的外键department_id没加索引。
下面的图显示当一个session修改departments表的被引用键列中的值60时会发生什么。
在上面的图,数据库在父表(departments)的被引用列发生修改期间,会持续持有employees表的全表锁,这个锁导致其他事务可以查询,但是不能更新employees表。举个例子,employees表的雇员电话号码在此期间不能更新。当父表(departments)的被引用列修改完毕以后,employees表的全表锁会立即释放(不会一直锁到事务结束)。如果departments表的多行经历父键列被修改,那么employees表的全表锁 会在每行修改时获取,每行修改完释放,一直到多行修改完毕位置(每行获取 释放一次)
注意:在子表的DML不会获取父表的全表锁
Locksand indexed Foreign Keys(锁和加了索引的外键)
当下面两个条件符合,数据库不会获取子表的全表锁:
·子表中的外键列加了索引
·session修改父表中的被引用列
在父表上的锁 预防其他事务获取父表的排外表锁,但是不会阻止在父表上的DML语句,也不会在父表上DML执行期间阻止子表被修改。
最好要加索引。如果父表上的父键(被引用键)发生修改时,而子表也同时在发生更新
下图显示了子表employees以及加了索引的外键列 department_id。一个事务删除了departments表的部门280行。这个删除不会像外键为加索引时 导致数据库获取employees表的全表锁
如果子表指定了ON DELETE CASCADE,那么父表的删除会导致子表的删除。举个例子,删除部门280可以导致employees中的和280部门相关的行被删除。在这种情况,等待以及锁的规则 和你先删除子表中相关行,然后再删除父表中的相关行 是一模一样
DDLLOCKS(DDL锁)
data dictionary lock 数据字典锁 在一个DDL命令操作或者引用到某个对象时,保护它们的schema object 定义。只有被操作或引用的对象上会加DDL锁。数据库永远不会锁定整个data dictonary(数据字典).
当执行DDL事务 时需要DDL锁时 Oracle数据库会自动获取它。用户不可以显式请求DDL锁。举个例子,如果用户正在创建一个存储过程,那么Oracle数据库会自动获取这个存储过程所有引用到的schema objects 上的DDL。这些DDL锁是为了预防 在存储过程还没编译完成时 相关的对象就被删除或者修改结构了。
ExclusiveDDL Locks(独占DDL锁)
独占DDL锁,阻止其他session获取DDL或DML锁。除了在“Share DDL Locks”中说明的,剩余绝大部分DDL锁都是独占锁,防止其他DDL操作可能修改或应用这个资源而导致毁坏性影响。举个例子,DROP TABLE命令是不允许当ALTER TABLE还在执行时 就删除表,反之亦然。
独占DDL锁在DDL语句执行期间和自动提交期间持续存在。在请求独占DDL锁时,如果该对象的DDL锁已经被另外的操作持有,那么请求会等待,等待到旧的DDL锁释放,它才会继续。
ShareDDL Locks(共享DDL锁)
共享DDL做用来阻止与之相冲突的DDL操作,但是允许相似的DDL操作并发执行。
举个例子,当一个CREATE PROCEDURE语句执行,这个语句包含的事务会获取所有引用的表的共享DDL锁。其他事务也可以在相应的表上并发的CREATE PROCEDURE获取共享DDL锁,但是不可以在被引用的任何一个表上获取独占DDL锁
共享DDL锁在DDL语句执行和自动提交期间 持续持有。因此一个事务持有共享DDL锁保证被引用的对象在整个事务执行期间是 恒定的(结构没发生变化)
BreakableParse Locks(可中断解析锁)
解析锁由 SQL语句,或者PL/SQL程序单元 在其引用的相关对象上持有。如果获取了解析锁,那么当引用的对象发生结构改变或者drop时,相关shared sql area 可以被标为无效。
解析锁 被称之为 可中断解析锁 是因为它不会阻挠任何DDL操作,且可以被冲突的DDL操作打断这个锁。
当SQL语句执行到parse phase(解析阶段),在shared pool(共享池)中获取的。只要这个语句的shared sql are仍然停留在 shared pool,这个锁就会一直持有。
SystemLocks(系统锁)
Oracle 数据库使用多种类型的系统锁来保护 数据库和内存的内部结构。由于用户无法控制它们何时出现和持续多久,这些机制对于用户来说几乎是不可访问的。
Latches(不翻了。就叫latch)
latch是简单,低级别的串行化机制,它协调多个用户访问 这些 共享的数据结构、对象、文件。
当多个进程同时读取一个共享内存资源时使用latch保护其不受损坏。
具体来说,latch在以下情况保护数据结构:
·多个session的并发修改
·读取一个正在被别人修改的资源
·当访问的时候,内存被释放(换出)
基本来说,在SGA中,单个latch会保护多个对象。举个例子,后台进程,如DBWn和LGWR从shared pool分配了内存去创建数据结构。为了分配这个内存,这些进程使用shared pool latch 串行访问,防止两个进程同时检查或修改shared pool..内存分配完成以后,其他进程可能需要访问 shared pool areas 比如library cache(解析时需要).这个情况下,进程会只把library cache latch住,而不latch 整个shared pool.
和行锁之类enqueue latches的不同,latch是不允许session排队.当一个latch变为可用时,第一个请求这个latch的将获取,并独占访问它。Latch spinning发生在当一个进程重复循环请求一个latch时,而latch sleeping发生重新latch 请求之前,释放CPU时。
基本来说,一个Oracle进程在操作或查看一个数据结构时 会极端短时间内获取一个latch。举个例子,仅仅为某一名员工处理工资更新,数据库就可能需要获取并释放成千上万个latchs. . latch的实现是依赖操作系统的,特别是关于一个进程是否会等待latch和等待多长时间
latch的时间增长,意味着并发性的下降。举个例子,过多的hard parse(硬解析)操作会对library cache latch 造成冲突。V$LATCH事务包含了每个latch的使用状态的详细信息,包括latch被请求和被等待的次数。
Mutexes(互斥)
mutual exclusionobject互相排斥的对象(mutex)是一个低级别的机制,防止内存中的对象被并行访问时 被换出或被损坏。mutex和latch很相似,但是一个latch基本上都保护一组object。而mutex保护单个object。
mutexes有几个优势:
·mutex可以减少争用的可能性
因为一个latch保护多个objects,当很多进程要并发访问被它保护的不同对象时,它可能会成为瓶颈。通过 串行化访问一个单一的对象 ,而不是一组,mutex增加了可用性
·mutex比latch消耗更少的内存
·当进入共享模式,一个mutex允许被多个session并发引用
InternalLocks(内部锁)
内部锁是高级别的锁,比latch和mutexes更复杂的机制,并用于各种目的。数据库使用以下类型的内部锁:
·Dictionary cache locks (字典cache锁)
当dictionary cache中的条目被修改或使用时会非常短时间的持有这个锁。它们保证语句解析时不会看到不一致的对象定义。Dictionary cache locks可以是共享的,也可以是独占的。共享锁会在解析完成后释放,而独占锁会在DDL语句执行完成后才释放
·File and log managerment locks(文件以及日志 管理锁)
这些鎖保护各种文件。举个例子,一个内部锁保护control file(控制文件)这样一次只能有一个进程修改它。另一个锁协调online redo log文件的使用和归档。data files(数据文件)被锁住 这样可以保证多个instance(实例)通过共享模式来mount一个数据库,或一个instance通过独占模式来mount一个数据库。因为文件和日志锁表示文件的状态,这些锁必要时会持有很长时间。
·Tablespace and undo segment locks(表空间和undo段锁)
这些锁保护表空间和undo段。举个例子,访问一个数据库的所有实例 必须对一个表空间的状态是否处于联机还是离线必须保持一致。undo 段是被锁定的 这样只有一个实例可以写入该段。
Overview of Manual Data Locks(手动数据锁概述)
Oracle数据库通过自动执行锁定来保证数据并发性和数据完整性,以及语句级的读一致性。但是你可以通过手动覆盖Oracle数据库默认的锁机制。在下面情况中,手动覆盖默认的锁是有意义的:
·应用需要事务级读一致性或可重复读。
这个情况,一个事务内的查询必须返回相同的数据,不反应其他事务的修改。完成这个事务级读一致性可以通过使用 显式锁定,只读事务,serializable事务,或覆盖默认锁定。
·应用需要一个应用对一个资源独占访问,这样这个事务就不需要等待其他事务的完成了
你可以在session级和事务级覆盖Oracle数据库的自动锁定。在session级别,session可以通过使用ALTER SESSION语句设置需要的事务隔离级别。在事务级别,事务中包含如下语句时可以覆盖Oracle数据库的默认锁定:
·SET TRANSACTION ISOLATION LEVEL语句
·LOCK TABLE语句(锁定一张表或视图的所有基表)
·SELECT ... FOR UPDATE语句
通过上面语句获取的锁 会在事务结束 或回滚到获取锁之前的savepoint 才会释放。
无论Oracle数据库默认锁定在什么级别被覆盖,DBA和开发都要保证覆盖锁定程序的操作是正确的。锁定程序必须满足下面标准:保证数据完整性,可以接受的数据并发性,死锁不会发生(或偶尔发生 可以被合适的处理)
Overview of User-Defined Locks(用户自定义锁概述)
使用Oracle数据库锁管理服务,你可以为指定的应用定义自己的锁。举个例子,你可以创建一个锁去串行化访问文件系统上的一条消息日志。因为保留的用户锁和oracle数据库的锁一样,它有Oracle数据库中锁所有的功能(包括死锁检测),用户的锁和数据库的锁永远不会冲突,因为用UL前缀标识
Oracle数据库锁管理服务可以通过DBMS_LOCK包调用,你可以在PL/SQL 块中包含如下语句:
·请求指定类型的锁
·给锁一个唯一的名字,用来和实例中的其他程序中的锁相区别
·修改锁的类型
·释放锁