oracle 并发与多版本

Oracle数据库通过高效的锁定机制和多版本体系结构实现并发控制。多版本使得读操作不会被写操作阻塞,提供读一致视图,保证并发访问的一致性。ISO SQL的事务隔离级别如READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE等在Oracle中得以实现,以控制事务间的可见性和冲突。
摘要由CSDN通过智能技术生成

并发控制 concurrency control

数据库提供的函数集合,允许多个人同时访问和修改数据。

锁(lock)是Oracle管理共享数据库资源并发访问并防止并发数据库事务之间“相互干涉”的核心机制之一。

Oracle使用了多种锁,包括:
1. TX锁:修改数据的事务在执行期间会获得这种锁。
2. TM锁和DDL锁:在你修改一个对象的内容(对于TM锁)或对象本身(对应DDL锁)时,这些锁可以确保对象的结构不被修改。
3. 闩(latch):这是Oracle的内部锁,用来协调对其共享数据结构的访问。

不论是哪一种锁,请求锁时都存在相关的最小开销。

TX锁在性能和基数方面可扩缩性极好。

TM锁和DDL锁要尽可能地采用限制最小的模式。

闩和队列锁(enqueue)都是轻量级的,而且都很快。


但是Oracle对并发的支持不只是高效的锁定。它还实现了一种多版本(multi-versioning)体系结构,这种体系结构提供了一种受控但高度并发的数据访问。多版本是指,Oracle能同时物化多个版本的数据,这也是Oracle提供数据读一致视图的机制(读一致视图即read-consistent view,是指相对于某个时间点有一致的结果)。多版本有一个很好的副作用,即数据的读取器(reader)绝对不会被数据的写入器(writer)所阻塞。换句话说,写不会阻塞读。在Oracle中,如果一个查询只是读取信息,那么永远也不会被阻塞。它不会与其他会话发生死锁,而且不可能得到数据库中根本不存在的答案。

默认情况下,Oracle的读一致性多版本模型应用于语句级(statement level),也就是说,应用于每一个查询;另外还可以应用于事务级(transaction level)。这说明,至少提交到数据库的每一条SQL语句都会看到数据库的一个读一致视图,如果你希望数据库的这种读一致视图是事务级的(一组SQL语句),这也是可以的。

数据库中事务的基本作用是将数据库从一种一致状态转变为另一种一种状态。ISO SQL标准指定了多种事务隔离级别(transaction isolation level),这些隔离级别定义了一个事务对其他事务做出的修改有多“敏感”。越是敏感,数据库在应用执行的各个事务之间必须提供的隔离程度就越高。

事务隔离级别

ANSI/ISO SQL标准定义了4种事务隔离级别,对于相同的事务,采用不同的隔离级别分别有不同的结果。即使输入相同,而且采用同样的方式来完成同样的工作,也可能得到完全不同的答案,这取决于事务的隔离级别。这些隔离级别是根据3个“现象”定义的,以下就是给定隔离级别可能允许或不允许的3种现象:
1. 脏读(dirty read): 能读取未提交的数据。只要打开别人正在读写的一个OS文件,就会脏读。 脏读,将影响数据完整性,外键约束会遭到破坏,会忽略惟一性约束
2. 不可重复读(nonrepeatable read):如果你在T1时间读取某一行,在T2时间重新读取这一行时,这一行可能已经有所修改。也许它已经消失,有可能被更新了,等等。
3. 幻像读(phantom read):如果你在T1时间执行一个查询,而在T2时间再执行这个查询,此时可能已经向数据库中增加了另外的行,这会影响你的结果。
与不可重复读的区别在于:在幻像读中,已经读取的数据不会改变,只是与以前相比,会有更多的数据满足你的查询条件。


隔离级别 脏读 不可重复 幻像读
READ UNCOMMITTED 允许 允许 允许 (读未提交)级别用来得到非阻塞读(non-blocking read)
READ COMMITTED 允许 允许 不能提供一致的结果
REPEATABLE READ 允许 可以保证由查询得到读一致的(read-consistent)结果
SERIALIZABLE
Oracle明确地支持READ COMMITTED(读已提交)和SERIALIZABLE(可串行化)隔离级别
不过,在Oracle中,READ COMMITTED则有得到读一致查询所需的所有属性。另外,Oracle还秉承了READ UNCOMMITTED的“精神”。(有些数据库)提供脏读的目的是为了支持非阻塞读,也就是说,查询不会被同一个数据的更新所阻塞,也不会因为查询而阻塞同一数据的更新。不过,Oracle不需要脏读来达到这个目的,而且也不支持脏读。但在其他数据库中必须实现脏读来提供非阻塞读。
除了4个已定义的SQL隔离级别外,Oracle还提供了另外一个级别,称为 READ ONLY(只读)。READ ONLY事务相对于无法在SQL中完成任何修改的REPEATABLE READ或SERIALIZABLE事务。如果事务使用READ ONLY隔离级别,只能看到事务开始那一刻提交的修改,但是插入、更新和删除不允许采用这种模式。如果使用这种模式,可以得到REPEATABLE READ和SERIALIZABLE级别的隔离性。

READ UNCOMMITTED

READ UNCOMMITTED隔离级别允许脏读。 Oracle没有利用脏读,甚至不允许脏读。READ UNCOMMITTED隔离级别的根本目标是 提供一个基于标准的定义以支持非阻塞读。Oracle会默认地提供非阻塞读,在数据库中很难阻塞一个SELECT查询。每个查询都以一种读一致的方式执行,而不论是SELECT、INSERT、UPDATE、MERGE,还是DELETE。这里把UPDATE语句称为查询,UPDATE语句有两个部分:一个是WHERE子句定义的读部分,另一个是SET子句定义的写部分。UPDATE语句会对数据库进行读写,就像所有DML语句一样。对此只有一个例外:使用VALUES子句的单行INSET是一个特例,因为这种语句没有读部分,而只有写部分。

先创建表:
scott@ORCL>create table accounts
  2  ( account_number number primary key,
  3  account_balance number not null
  4  );

插入数据:
scott@ORCL>insert into accounts values('123',500);
已创建 1 行。
scott@ORCL>insert into accounts values('456',240.25);
已创建 1 行。
scott@ORCL>insert into accounts values('789',100);
已创建 1 行。
scott@ORCL>commit;
提交完成。
数据如下:
scott@ORCL>select * from accounts;

ACCOUNT_NUMBER ACCOUNT_BALANCE
-------------- ---------------
           123             500
           456          240.25
           789             100

scott@ORCL>select sum(account_balance) from accounts;

SUM(ACCOUNT_BALANCE)
--------------------
              840.25
下面,select语句开始执行,读取第1行、第2行等。在查询中的某一点上,一个事务将$400.00从账户123转到账户789。这个事务完成了两个更新,但是并没有提交。现在数据如下:
帐号 账户金额 是否?
1 123 ($500.00) changed to $100.00 X
2 456 $240.25
3 789 ($100.00) changed to $500.00 X
如果我们执行的查询要访问某一块,其中包含表“最后”已锁定的行(第3行),则会注意到,这一行中的数据在开始执行之后有所改变。
为了提供一个一致(正确)的答案,Oracle在这个时刻 会创建该块的一个副本,其中包含查询开始时行的“本来面目”。
它会读取值$100.00,这就是查询开始时该行的值。
Oracle就有效地绕过了已修改的数据,它没有读修改后的值,而是从undo段(回滚段)重新建立原数据
因此可以返回一致而且正确的答案,而无需等待事务提交。

再来看允许脏读的数据库,这些数据库只会返回读取那一刻在账户789中看到的值,在这里就是$500.00。这个查询会把转账的$400重复统计两次。因此,它不仅会返回错误的答案,而且会返回表中根本不存在的一个总计(任何时间点都没有这样一个总计)。

这里的关键是,脏读不是一个特性:而是一个缺点。Oracle中根本不需要脏读。Oracle完全可以得到脏读的所有好处(即无阻塞),而不会带来任何不正确的结果。

READ COMMITTED

事务只能读取数据库中已经提交的数据。这里没有脏读,可能有不可重复读(也就是说,在同一个事务中重复读取同一行可能返回不同的答案)和幻像读(与事务早期相比,查询不光能看到已经提交的行,还可以看到新插入的行)。在数据库应用中, READ COMMITTED可能是最常用的隔离级别了,这也是Oracle数据库的默认模式
根据先前的规则,在使用READ COMMITTED隔离级别的数据库中执行的查询肯定就会有相同的表现,真的是这样吗?这是不对的。
在Oracle中,由于使用多版本和读一致查询,无论是使用READ COMMITTED还是使用READ UNCOMMITTED,从ACCOUNTS查询得到的答案总是一样的。 Oracle会按查询开始时数据的样子对已修改的数据进行重建,恢复其“本来面目”,因此会返回数据库在查询开始时的答案。
下面来看看其他数据库,如果采用READ COMMITTED模式。我们从表所述的那个时间点开始:
1. 现在正处在表的中间。已经读取并合计了前N行。
2. 另一个事务将$400.00从账户123转到账户789。
3. 事务还没有提交,所以包含账户123和789信息的行被锁定。
我们知道Oracle中到达账户789那一行时会发生什么,它会绕过已修改的数据,发现它原本是$100.00,然后完成工作。
如下显示了其他数据库(非Oracle)采用默认的READ COMMITTED模式运行时可能得到的答案:
非Oracle数据库使用READ COMMITTED隔离级别时的时间表
时间 查询 转账事务
T1 读取第1行。到目前为止Sum=$500.00
T2 读取第2行。到目前为止Sum=$740.25
T3 更新第1行,并对第1行加一个排他锁,防止出现其他更新和读取。
第1行现在的值为$100.00

T4 读取第N行。Sum=…
T5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值