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

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

  请尊重作者劳动成果,转载请标明原文链接:

  https://www.cnblogs.com/jpcflyer/p/9164100.html

一、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官网在线帮助文档

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

  1. Oracle的数据并发与一致性详解(下)

    上篇介绍了数据并发与一致性的相关概念.以及oracle的事务隔离级别等内容,本篇继续介绍锁机制.自动锁.手动锁.用户自定义锁的相关内容. 请尊重作者劳动成果,转载请标明原文链接: https://ww ...

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

    Oracle中用exp/imp命令参数详解 [用 exp 数 据 导 出]:1  将数据库TEST完全导出,用户名system 密码manager 导出到D:\daochu.dmp中   exp sy ...

  3. Java并发关键字Volatile 详解

    Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议 ...

  4. Oracle GoldenGate中HANDLECOLLISIONS参数使用详解

    Oracle GoldenGate中HANDLECOLLISIONS参数使用详解   HANDLECOLLISIONS 是一个 replicat 进程参数,主要在 initial load 中使用.在 ...

  5. Oracle Database 12c Release 2安装详解

    第1章 Oracle Database 12c Release 2安装详解 1.1 下载方法 oracle官网https://www.oracle.com 1)打开官方网站,找到下载连接 2)选择更多 ...

  6. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  7. oracle入坑日记<三>用户详解(角色理解)

    1   用户是什么 1.1.权限管理是Oracle的精华,不同用户登录到同一数据库中,可能看到不同数量的表,拥有不同的权限.Oracle 的权限分为系统权限和数据对象权限,共一百多种.如果把Oracl ...

  8. oracle数据库定时任务dbms_job的用法详解

    本文来源:Ruthless <oracle数据库定时任务dbms_job的用法详解> 一.dbms_job涉及到的知识点   1.创建job: variable jobno number; ...

  9. Oracle10g数据泵impdp参数详解--摘自网络

    Oracle10g数据泵impdp参数详解 2011-6-30 12:29:05 导入命令Impdp •      ATTACH 连接到现有作业, 例如 ATTACH [=作业名]. •      C ...

随机推荐

  1. [leetcode]39. Combination Sum组合之和

    Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), fin ...

  2. Python项目--Scrapy框架(二)

    本文主要是利用scrapy框架爬取果壳问答中热门问答, 精彩问答的相关信息 环境 win8, python3.7, pycharm 正文 1. 创建scrapy项目文件 在cmd命令行中任意目录下执行 ...

  3. 机器学习(三)--------多变量线性回归(Linear Regression with Multiple Variables)

    机器学习(三)--------多变量线性回归(Linear Regression with Multiple Variables) 同样是预测房价问题  如果有多个特征值 那么这种情况下  假设h表示 ...

  4. node.js中实现http服务器与浏览器之间的内容缓存

    一.缓存的作用 1.减少了数据传输,节约流量. 2.减少服务器压力,提高服务器性能. 3.加快客户端加载页面的速度. 二.缓存的分类 1.强制缓存,如果缓存有效,则不需要与服务器发生交互,直接使用缓存 ...

  5. arguments.callee的作用及替换方案

    arguments.callee的作用 arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,返回正被执行的 Function 对象,也就是所指定的 Funct ...

  6. day 5,格式化输出,for,while, break,continue,列表

    本节内容: 1,格式化输出 2,数据类型 3,for 循环 4,while 循环 5,列表 pycharm的简单使用,设置pycharm自动生成日期和计算机用户名 ctrl+d复制一行 1,格式化输出 ...

  7. 转载-js如何设置网页横屏和竖屏切换

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  8. boost--时间处理

    date_time库的时间功能位于名字空间boost::posix_time,它提供了微妙级别(最高可达纳秒)的时间系统,使用需要包含头文件"boost\date_time\posix_ti ...

  9. 配置微信jssdk自定义分享

    前段时间做这个功能的时候遇到这个问题,之前的话是微信自动抓取界面第一张图,现在微信更新api,必须自行配置,接入jssdk,才能实现该功能. 详细可以查看微信的jssdk文档 微信官方开发者文档    ...

  10. winSCP无法连接虚拟机Linux解决

    刚在虚拟机上装上Linux(Centos7)后使用winSCP建立文件共享发现连接超时,经过几个小时的查找发现Linux中没有eth0文件,这说明其网卡名不是eth0,在网上查过一些解决办法有的通过修 ...