Oracle的数据并发与一致性详解(上)

今天想了解下oracle中事务与锁的原理,但百度了半天,发现网上介绍的内容要么太短,要么版本太旧,而且抄袭现象严重,所以干脆查官方帮助文档(oracle 11.2),并将其精华整理成中文,供大家一起学习。

本篇将从数据并发与一致性概念开始,依次介绍事务隔离级别、锁机制、自动锁、手动锁、用户自定义锁的相关内容。

一、Oracle数据并发与一致性概念

在以前单用户的数据库环境中,我们根本就不需要关心数据一致性的问题,因为根本就不会有多个用户在同一时间修改同一数据。但在现在的多用户数据库环境中,必须允许同时发生多个事务,而且这些事务可能会访问同一数据,此外,还要保证这些事务的一致性。因此多用户数据库必须提供以下两个基本功能:

  • 数据并发:即允许多用户同时访问同一数据
  • 数据一致性:即每个用户看到的数据都是一致的

为了描述事务并发运行时的一致性行为,研究人员定义了一种事务隔离模型,称之为serializability(序列化)。这种可串行化事务操作使得它看起来似乎没有其它用户在操作数据。虽然这种序列化机制在一般情况下是可用的,但在并发要求高的场景,它会严重影响系统的吞吐能力。一般情况下,需要在事务隔离级别与性能间作一个取舍。

Oracle通过使用multiversion consistency model(多版本一致性模型)、以及各种锁和事务来维护数据一致性,下面介绍下相关概念。

1.Multiversion Read Consistency(多版本读一致性)

多版本指的是同时存在数据的多个版本,意味着oracle有以下两个特性:

  • read-consistent queries(读一致性查询)

查询返回的数据是已提提交的,在某一时间点是一致的。(注意:oracle中是不会有脏读的,为什么呢?原因是第一条会话插入一条记录不提交,第二个会话再查询时发现这个事务没有commit,从而会找到这个事务的事务槽,事务槽中记录着该行未修改前的值存放在undo的位置,然后把该undo块加载到内存构造出CR块,查询会读取CR块中的值返回给客户。)

  • nonblocking queries(非阻塞查询)

数据的读和写不会相互阻塞。

2.Statement-Level Read Consistency(声明级读一致性)

Oracle总是强制保证声明级的读一致性,确保查询返回的数据在同一时间点是已提交的(原因已在上面提及)。

3.Transaction-Leval Read Consistency(事务级读一致性)

Oracle可以提供事务中所有查询的一致性,即事务中每个声明看到的数据都是某一点的数据,这个点指的是事务开始的点。在序列化事务中的查询只能看到自己本事务发生的修改。事务级读一致性产生了可重复读,且不会产生幻影读。

4.Read Consistency and Transaction Table(读一致性和事务表)

Oracle使用了事务表来确定当数据库开始修改一个块时,是否有未提交的事务,这个事务表也称为interested transaction list(ITL)。事务表中描述了哪个事务有行锁、哪一行包含已提交或未提交的修改。

5.Locking Mechanisms(锁机制)

一般来说,多用户数据库会使用多种数据锁的形式来解决数据的并发与一致性问题,本文后面会有锁的详细介绍。

6.ANSI/ISO Transaction Isotation Levals(ANSI/ISO事务隔离级别)

ANSI和ISO都采纳的SQL标准中,定义了四个级别的事务隔离。这些不同级别对事务吞吐量有不同影响。这些隔离级别定义是为了预防两个并发事务会产生的一些现象,这些现象包括:

  • 脏读:一个事务读取了另一个事务没有提交的数据
  • 不可重复读:一个事务重复读取刚才已读过的数据,结果两次数据不一致,在此期间,其它事务对此数据已修改并提交
  • 幻影读:一个事务重复读取满足查询条件的记录数,结果两次数据不一致,在此期间,其它事务插入了符合此查询条件的数据

SQL标准中根据隔离级别允许发生的现象,定义了四种隔离级别:

Oracle数据库提供了read committed(默认级别)和serializable两种隔离级别,同时还支持只读模式。

二、事务隔离级别

上面已经提到ANSI的四种事务隔离级别,下面来详细介绍oracle数据库提供的三种事务隔离级别:read committed, serializable,read-only。

1.Read Committed事务隔离级别

在此级别中,事务中查询到的数据都是在此查询前已经提交的。这种隔离级别避免了读取脏数据。然而数据库并不阻止其它事务修改一个所读取的数据,其它事务可能会在查询执行期间修改。因此 ,一个事务运行同样的查询两次,可能会遇到不可重复读和幻影读。

  • read committed隔离级别中的读一致性

每个查询都会提供一个一致性的结果集,其中不需要用户做什么(这里的查询也包含像update中where这样的隐式查询)。

  • read committed隔离级别中的写冲突

在一个read committed事务中,当事务要更改一行,而这行已经被另外一个未提交事务修改了(有时称之为blocking transaction),这里会发生写冲突。此时这个事务会等待blocking transaction结束,并有以下两个选项:

  • 如果blocking transaction回滚,那么waiting transaction会修改之前被locked的行
  • 如果blocking transaction提交然后释放锁,那么waiting transaction会在改变后的数据基础上,进行更新

下表显示了事务1(可以是read committed或serializable)与事务2(read committed)的典型交互,称之为lost update(丢失更新)。

事务1 事务2 说明
SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN (‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500
 
SQL> UPDATE employees SET salary
= 7000 WHERE last_name = ‘Banda‘;
 
事务1用的是默认隔离级别READ COMMITTED

 
SQL> SET TRANSACTION ISOLATION
LEVEL READ COMMITTED;
 
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN
(‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500

事务2通过使用oracle的读一致性得到了事务1更新前的数据

 
SQL> UPDATE employees SET salary =
9900 WHERE last_name = ‘Greene‘;
SQL> INSERT INTO employees
(employee_id, last_name, email,
hire_date, job_id) VALUES (210,
‘Hintz‘, ‘JHINTZ‘, SYSDATE,
‘SH_CLERK‘);
 
事务1插入了employee Hintz,但并没有提交

 
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN
(‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9900

事务2看不到事务1未提交的Hintz信息

 
SQL> UPDATE employees SET salary =
6300 WHERE last_name = ‘Banda‘;

-- prompt does not return

事务2尝试去更新被事务锁住的Banda信息,产生了写冲突,此时事务2要等到事务1结束后再执行

SQL> COMMIT;
 
 
1 row updated.

SQL>

事务1提交,结束了事务,事务2继续处理

 
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN
(‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6300
Greene              9900
Hintz
 
COMMIT;
SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN (‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6300
Greene              9900
Hintz

关于丢失更新的问题,后面有时间再讨论。

2.Serializable事务隔离级别

在序列化隔离级别中,事务可以看到的是事务开始时已经提交的或事务自己做的修改。此隔离级别适合下面的场景:

  • 超大数据库并且事务很小,每个事务只更新几行
  • 在两个并发事务修改相同行的几率相对比较低的场景
  • 有较长的事务,但主要是只读事务的时候

在序列化隔离级别中,读一致性从通常的语句级扩展成整个事务级。事务中读取的任何行,再次读时保证是相同的。序列化事务不会遇到脏读、不可重复读、幻影读的问题。

Oracle允许序列化事务修改数据,不过如果有其它事务修改,那么这个事物必须在序列化事务开始之前就提交。当一个序列化事务企业修改一行,而该行被别的事务修改,且在序列化事务开始之后才提交,这时候会报ORA-08177:Cannot serialize access for this transaction。此时,应用可以采取以下动作:

  • 提交事务
  • 执行其它不同的语句,也许会回滚到之前的savepoint
  • 回滚整个事务

下面显示一个序列化事务是如何与其它事务交互的。如果一个序列化任务不去尝试修改其它事务在序列化事务开始后提交的数据,那么serialized access问题可以避免。

事务1 事务2 事务3
SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN (‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500
 
SQL> UPDATE employees SET salary
= 7000 WHERE last_name = ‘Banda‘;
 
事务1是默认的 READ COMMITTED

 
SQL> SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE;
 
SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN (‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500
 
SQL> UPDATE employees SET salary =
9900 WHERE last_name = ‘Greene‘;
SQL> INSERT INTO employees
(employee_id, last_name, email,
hire_date, job_id) VALUES (210,
‘Hintz‘, ‘JHINTZ‘, SYSDATE,
‘SH_CLERK‘);
 
SQL> COMMIT;
 
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

注意:oracle的读一致性使得事务2的前后读取是一致的,即事务1的插入和更新操作对事务2来说是不可见的

 
COMMIT;
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
SQL> UPDATE employees SET salary
= 7100 WHERE last_name = ‘Hintz‘;
 
 
SQL> SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE;
 
SQL> UPDATE employees SET salary =
7200 WHERE last_name = ‘Hintz‘;

-- prompt does not return
SQL> COMMIT;
 
 
UPDATE employees SET salary = 7200
WHERE last_name = ‘Hintz‘
*
ERROR at line 1:
ORA-08177: can‘t serialize access
for this transaction

报错原因在于事务3的提交是在事务4开始之后,没有满足序列化

 
SQL> ROLLBACK;

事务2回滚以结束事务

 
SQL> SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE;
 
SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN (‘Banda‘,‘Greene‘,‘Hintz‘);

LAST_NAME         SALARY
------------- ----------
Banda               7100
Greene              9500
Hintz               7100
 
SQL> UPDATE employees SET salary =
7200 WHERE last_name = ‘Hintz‘;

1 row updated.
 
SQL> COMMIT;

3.Read-Only事务隔离级别

只读隔离级别和序列化隔离级别很像,只是在只读事务中,不允许有修改操作,除非是用sys用户。因此只读事务不会有ORA-08177错误,只读事务在产生一个报告时很有效。

——限于精力,今天先写到这里,明天继续更新下篇——锁机制、手动锁、用户自定义锁。

参考资料:《Oracle官网在线帮助文档

原文地址:https://www.cnblogs.com/jpcflyer/p/9164100.html

时间: 2024-10-09 10:46:47

Oracle的数据并发与一致性详解(上)的相关文章

ORACLE EXPDP IMPDP数据导入导出命令详解及同EXP IMP命令详细对比

ORACLE EXPDP IMPDP数据导入导出命令详解及同EXP IMP 命令详细对比 一.EXPDP IMPDP EXP IMP 可以实现 1.可以实现逻辑备份和逻辑恢复 2.可以在数据库用户之间移动对象 3.可以在数据库之间移动对象 4.可以实现表空间转移 二.EXPDP的命令详解 C:\Users\Administrator>20:42:32.90>expdp help=y Export: Release 11.2.0.1.0 - Production on 星期六 10月 10 09

Oracle中用exp/imp命令参数详解【转】

Oracle中用exp/imp命令参数详解 [用 exp 数 据 导 出]:1  将数据库TEST完全导出,用户名system 密码manager 导出到D:\daochu.dmp中   exp system/[email protected]   rows=y  indexes=y compress=n buffer=65536   feedback=100000full=y  file=d:\daochu.dmp  log=d:\daochulog.txt   owner=(ECC_BIZ,

Java并发关键字Volatile 详解

Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议:MESI ? 由于计算机储存设备(硬盘等)的读写速度和CPU的计算速度有着几个数量级别的差距,为了不让CPU停下来等待读写,在CPU和存储设备之间加了高速缓存,每个CPU都有自己的高速缓存,而且他们共享同一个主内存区域,当他们都要同步到主内存时,如果每个CPU缓存里的数据都不一样,这时应该以哪个数

Oracle 建表常用数据类型的详解

创建表时,必须为表的各个列指定数据类型.如果实际的数据与该列的数据类型不相匹配,则数据库会拒绝保存.如为学生指定出生日期为"1980-13-31". 在Oracle中,常见的数据类型有: 字符串:字符串分为定长类型char和变长类型varchar2. 数字:整数 number(整数位),小数 number(总长度,小数位),只写number,表示无限制. 日期:date类型,可以保存年月日时分秒. 问题:Oracle中为什么字符串类型为varchar2,它与varchar有什么关系?

Oracle中常用的to_Char用法详解

Oracle中常用的to_Char用法详解(有FMT的详细列表) The following are number examples for the to_char function. to_char(1210.73, '9999.9') would return '1210.7' to_char(1210.73, '9,999.99') would return '1,210.73' to_char(1210.73, '$9,999.00') would return '$1,210.73'

Oracle Database 12c Release 2安装详解

第1章 Oracle Database 12c Release 2安装详解 1.1 下载方法 oracle官网https://www.oracle.com 1)打开官方网站,找到下载连接 2)选择更多下载. 3)选择数据库版本,这里选择的是目前的最新版本 4)接收许可协议,选在linux版本进行下载 5) 接收许可协议,点击linuxx64_12201_database.zip 6)登陆oracle账户,没有的可以自己创建一个 7)然后就能够进行下载 https://docs.oracle.co

(转)MVC 3 数据验证 Model Validation 详解

继续我们前面所说的知识点进行下一个知识点的分析,这一次我们来说明一下数据验证.其实这是个很容易理解并掌握的地方,但是这会浪费大家狠多的时间,所以我来总结整理一下,节约一下大家宝贵的时间. 在MVC 3中 数据验证,已经应用的非常普遍,我们在web form时代需要在View端通过js来验证每个需要验证的控件值,并且这种验证的可用性很低.但是来到了MVC 新时代,我们可以通过MVC提供的数据验证Attribute来进行我们的数据验证.并且MVC 提供了客户端和服务器端 双层的验证,只有我们禁用了客

8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to Entities 使用LINQ to Entities操作实体 LINQ操作 懒加载和预加载 插入数据 更新数据 删除数据 本章小结 本人的实验环境是VS 2013 Update 5,windows 10,MSSQL Server 2008. 上一篇<Code First开发系列之领域建模和管理实体关系>,我们主要介绍了EF中“约定大于配置”的概念,如何创建数据

Oracle之外键(Foreign Key)用法详解(一)

Oracle外键(Foreign Key)用法详解(一) 1.目标 演示如何在Oracle数据库中使用外键 2.什么是外键? 1)在Oracle数据库中,外键是用来实现参照完整性的方法之一.打个形象的比喻,外键是指定义外键的表的列的值必须在另一个表中出现. 2)被参照的表称之为父表(parent table),创建外键的表称之为子表(child table).子表中的外键关联了父表中的主键. 3)外键可以在创建表时定义或者通过ALTER TABLE语句创建. 3.创建表时定义外键 语法: CRE