数据库事务与事务的隔离级别

栏目: 数据库 · 发布时间: 5年前

内容简介:在数据库的使用中,我们常常把一系列操作的集合看作是一个独立的单元,这种构成单一逻辑工作单元的集合被称为一个数据库系统需要维护事务的以下四种特性,它们被合称为**"ACID"我们用T来指定这个模型,它的SQL大致是以下形式:

在数据库的使用中,我们常常把一系列操作的集合看作是一个独立的单元,这种构成单一逻辑工作单元的集合被称为 事务

事务模型

一个数据库系统需要维护事务的以下四种特性,它们被合称为**"ACID" ,分别对应 原子性**(Atomicity), 一致性 (Consistency), 隔离性 (Isolation), 持久性 (Durability)。

一个简单的事务模型

我们用T来指定这个模型,它的 SQL 大致是以下形式:

-- 操作1:扣除A账户10元
UPDATE account SET amount = amount - 10 WHERE user_name = 'A'

-- 操作2:增加B账户10元
UPDATE account SET amount = amount + 10 WHERE user_name = 'B'
复制代码

T是一个最简单的转账模型,A用户将10元转到了B用户的账本上。

之后我们就用这个事务模型来解释一下事务的四种特性。

一、原子性

原子性表示,一个事务所包含的操作集合是单一的,不可拆分的。而且事务中任何一个操作失败,都要保证数据库回滚到整个事务执行前的状态。

在T事务中,有两个操作,一个是在A账户扣除10元,一个是在B账户增加10元。这个两个操作密不可分。

如果我们将这两个操作看成两个独立的事务,那么假设初始状态:

A账户:100元
B账户:100元
复制代码

我们现在执行了 操作A ,A账户余额变为90元。然后,然后!服务器室由于某些不可控力,发生了爆炸。那么最终结果将会变成:

A账户:90元
B账户:100元
复制代码

A和B就此开始了无止境的撕逼。。

B:你快给我转钱啊!

A:我转了啊!你看我的账户已经扣了10元了!

B:我这里没有收到啊!你自己看,还是100元!

......

二、一致性

一致性原则要求事务的执行不改变数据库的一致。即事务执行前如果数据库一致,事务执行后,这种一致性仍然存在。

以T事务为例,T执行前,A和B的账户余额总和为200元,那么我们要保证在T执行后,A和B的账户余额总合仍然为200元。

三、持久性

持久性的原则要求一旦事务成功完成执行,并且提交到数据库,那么这一次更新将会持久的。也就是说,只要事务成功执行,任何的系统故障都不能撤销这一次事务的提交。

这个概念可能出现一些小漏洞,比如如果事务的结果储存在内存中,那么一旦宕机,所有的数据都会消失,我们需要将数据提交到外部磁盘上,并且做好更新信息的记录,使数据库在宕机后重启凄然能恢复到之前的状态。由于这个概念不属于我们的讨论范围,这里也就不再赘述。

四、隔离性

隔离性确保事务并发执行后的系统状态与这些事务以某种次序串行执行以后的状态是等价的。

如果有多个事务并发执行,即使我们确保事务的原子性和一致性,这些操作在执行时也并不是严格的串行,而是以某种不可见的形式交叉执行,这种不可见行很可能会导致最终状态的不一致。

举个栗子,我们将之前的事务T记作事务T1,并将T1中的操作细分,他们在系统中的实际操作应该大致是这样的:

/**
* read(x):从数据库中将x传送到执行read操作的事务的主存缓冲区中
*
* write(x):从执行write的事务的主存缓冲区中将x取出并写回数据库(其实还有一个commit过程,这里先忽略)
*/

read(A);
A := A-10;
write(A);


read(B);
B := B+10;
write(B);

复制代码

除此以外,我们再定义一个T2,计算A+B的值:

read(A);
read(B);
A := A+B;
复制代码

并行的事务会如何执行呢?如果运气好,它可能会按照T1,T2的顺序完整执行,那么最终我们得到的temp的状态应该是200。

但是如果出现一种情况,当T1中的A扣款成功,并切入数据库,而在执行给B增加余额的操作时,并没有全部完成,而是执行完 B := B+10 以后,开始执行T2,虽然B变量确实发生了改变,但是它还没有被写进数据库中,所以T2中计算出的temp变成了90+100=190。

大致流程会是这个样子:

-- T1
read(A):
A := A-10;
write(A); --这里A在数据库中的值变成了90

read(B);
B := B+10; --这里B确实发生了改变,但是并未提交至数据库

-- T2
read(A); --A = 90
read(B); --B = 100(不是110)
temp := A+B; --得到190 

--T1
write(B) --这里B的修改被提交到数据库

复制代码

为了确保隔离性,数据库系统中存在一种 并发控制系统 ,来完成这一职责。

事务的隔离级别

在介绍事务的隔离级别前,先来介绍一下 脏读幻读不可重复读 的概念。

脏读、幻读、不可重复读

  • 脏读:脏读是指,当一个事务在访问某一数据,并且修改了这一数据,但是在commit之前,另外一个事务也访问了同一数据,然后做了修改。大致模型如下(这里用到了之前忽略的 commit 操作,这个操作是指事务完成后,进入提交状态):
-- T1
read(A);
A := A+1;
write(A);

--T2
read(A);
A := A+2;
write(A);
commit;

-- T1
commit;
复制代码
  • 不可重复读:指在一个事务中,对同一数据进行了两次读取,但是在这个事务还未结束的时候(第一次读取之后,第二次读取之前),另一事务读取了同一数据,并进行了修改,那就导致了两次重复读取的数据不一致,形成了不可重复读状态。
-- 假设初始状态,A=10

-- T1
read(A); -- A = 10;

-- T2
read(A);
A = A+10;
write(A);
commit; -- A = 20;

-- T1
read(A); -- A = 20,与第一次不同
复制代码
  • 幻读:幻读发生在两个事务非独立执行的情况。下面用SQL演示这种情况:
-- T1
UPDATE users SET status = 1;

-- T2
insert users (`status`) values ('0')
复制代码

然后执行T1操作的用户惊奇的发现,明明把所有的user状态都置1了啊,怎么还有一个0 ??????


以上所述就是小编给大家介绍的《数据库事务与事务的隔离级别》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

挑战编程技能

挑战编程技能

Brian P. Hogan / 臧秀涛 / 人民邮电出版社 / 2017-2 / 39.00元

新手程序员在具备了理论基础后,面对实际项目时,往往不知道如何解决问题;有经验的程序员在学习了一门新语言后,也会有很多不知道如何使用的特性。针对程序员的这一普遍困惑,资深软件工程师Brian P. Hogan在这本书中总结了57道练习题,教他们如何锤炼技能。这些练习题的难度会逐渐增加,使得编程训练充满挑战又乐趣多多。一起来看看 《挑战编程技能》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换