所有文章
https://www.cnblogs.com/lay2017/p/12078232.html
正文
一、什么是事务?
概念性的东西通常都显得抽象、晦涩,包罗万象但似乎很难一下子抓到要点。为此,我们先来看一个比较典型的例子:银行转账
市民王先生到银行转账1000元给老李,王先生的账户里现有10000元,老李的账户恰好也有10000元。银行将从王先生账户扣除1000元:10000-1000=9000,然后给老李的账户加上1000元:10000+1000=11000。
王先生的账户剩余:9000元
老李的账户剩余:11000元
我们来看看,这里有两个步骤1)从王先生账户扣钱;2)给老李账户加钱;我们做一个问题假设,银行柜员从王先生的账户扣完钱以后,忘记给老李的账户加钱了。那么会出现什么结果?
王先生的账户剩余:10000-1000=9000
老李的账户剩余:10000+0=10000
很明显,老李没有收到王先生的钱。而王先生也很委屈,已经扣完钱了啊。这反应了一个比较严重的问题,就是当你要处理的事情被分为了很多个步骤,这些步骤可能处理成功,可能失败,甚至可能完全忘了处理。如果都处理成功也没什么问题。如果都处理失败,我们也可以当作什么都没有发生过。唯独有的步骤成功,有的步骤失败的时候比较烦人了。就会出现上面的例子中的问题。
这时候我们会想,既然都成功或者都失败都行,我们能不能让多个步骤的处理像处理一个步骤一样简单点要么全部成功,要么全部失败呢?如上例子中,要么王先生扣完1000以后也给老李加上10000,要么王先生没扣钱,老李也没加钱,最多老李发现钱没到账会再要求王先生转一次。
我们可以给这两个步骤取个名字:转账,转账包含了从一个账户扣钱,给另一个账户加钱两个步骤,两个步骤同时成功或者同时失败。
抛开上面的例子,我们所要处理的其它事情会不会也存在转账这样的问题呢?当然!那么,我们把这些要做的事情像转账一样取个共同的名字,就叫做"事务"。
到这里,我们给事务下一个定义吧。
事务就是你要做的事情,通常这件事会包含多个步骤。事务中的多个步骤处理起来就像处理一个步骤一样,要么全部成功,要么全部失败。事务解决了多个步骤部分成功,部分失败所带来的不一致问题。
所以,事务的诞生是其实是为了解决问题,这种技术手段解决了"一致性"问题。
二、事务的ACID四个特征
任何存在的真实的亦或者是虚拟的人事物都有其特征,我们通过其特征来识别它与其它人事物之间的区别。
那么,事务这种技术手段存在什么特征呢?
事务的特征,我们简称为ACID。它们分别是什么呢?
A:atomicity 原子性
C:consistency 一致性
I:isolation 隔离性
D:durability 持久性
一致性
我们总说事务具备一致性,但是个人认为它并非事务本身的特性。而是事务这种技术手段,维护了所要处理的对象的一致性。比如说mysql中的数据的一致性,mysql的事务技术维护了mysql内部存储的数据具备一致性。
再来,理解一致性,我们先提一下我们以前热力学学过的"能量守恒定律"。
能量守恒定律是指一个封闭或者孤立的系统中总的能量是保持不变的。不会凭空产生,也不会凭空消失,只能从一个物体传递给另一个物体,且能量的形式可以相互转换。
和我们赖以生存的自然界一样,我们程序员所构建的系统也是一个"能量守恒"的世界。我们的代码,我们的数据都是这个世界的一尘一土。
而事务技术,就是为了让这个程序世界能够像真实世界一样做到"能量守恒"。我们简称其为"事务的一致性"。
似乎从这里感受到了早年看书经常忽略的话"编程就是对现实世界进行建模",我们可以像上帝一样在这里创造万物,而万物生生不息。
原子性
原子是什么?原子是指在化学的定义上一个不可再分割的基本微粒,它是构成一般物质的最小单元,我们也成为元素。
以上定义指的是一个客观存在的事物由原子构成。那,如果是一个行为,它具备原子特性是什么概念呢?
首先,原子不可分割,是最小的单元。所以,一个行为具备原子特性就不能被再拆分成多个步骤,我们把这个步骤看成一个最小单元。行为全部完成,或全部没开始。还是不好理解对吧?我们举个例子
你一步一步地走路回家,一路上你不停地抬脚、落脚。这时候你突然发现在你正要落脚地位置有一坨不可言状地东西。你感到恶心,所以脚落到一半就收住了。
很明显,抬脚和落脚这两个行为并没有形成"走一步"地原子性。想象一下,如果抬脚和落脚这两个行为成为了一个行为一样,抬了就必定会落,那么即使你的大脑发现有一坨东西,为了"走一步"你也要踩下去,因为抬脚和落脚是原子行为,不可分割。
当然,我们可以抬脚以后收回,这就是事务"回滚",让一切像没发生过一样重新开始。
这里的原子性只是表达了事务的一种特性,它旨在忽略你要做的事情的具体步骤。保证事情全部完成,或者全部像没开始一样。
持久性
持久性在定义中是指:事务一旦被提交,它产生的改变就是永久性的。即使发生故障也不应该影响改变的成功与否。
这里注意"提交"这两个事情,我们通俗地说就是一件事完成以后,它产生的影响就是既存事实。不能因为其它因素导致这个事实不存在了。好像还是不好理解,举例说明
银行查账的时候发现,A转账给B,B转账给C。这里有两个转账事务产生了。那么如果银行因为停电,A转账给B这个事务不再是既存事实,银行查账只发现B转账给C这一笔,就会奇怪为什么B的账户了多了一笔本该是A转账过来的钱呢?
持久性,代表了一种不可改变的既存事实,也就是我们常说的历史无法改变,只能看向未来。这样看来,事务的持久性是不是也意味着在维护着上面的"一致性"呢?即,如果丢了秦始皇统一的历史,整个后面的朝代还怎么存在呢
隔离性
我们先来回顾一下前面的原子性,一件事要么处于所有步骤完成的状态,要么处于所有步骤未开始的状态。这很好的保证了在一件事务发生的时候,世界的"能量守恒"的基础。
那,如果多个事务同时发生的时候呢?是不是依旧会出现各种错误?隔离级别,我们以数据库的角度或许更好理解一点。
比如,数据库记录着字段:a=0,b=0
事务A将a=1,事务B检查a=1,则将b=1
1)read uncommitted级别
A将a=1,未提交事务。B检查a=1,将b=1,提交事务。A回滚a=0。
这时候b产生了错误结果。这就是脏读,读取了未提交的数据。
所以,如果A修改a的,B修改b不依赖于a的结果,那么可以采用read uncommitted级别。简单来说,A和B本身就不需要考虑并发。
2)readcommitted级别
A将a=1,未提交事务。B检查a=0,不处理b,提交事务。A回滚a=0。
这时候b=0,a=0,数据并没有异常。
再来,A将a=1,未提交事务。B检查a=0,不处理b。A提交事务,B再次查询a并打印日志a=1。
这时候,a=1,b=0,日志a=1。在B事务中a的值前后不一致。这就是不可重复读问题。
readcommitted其实就是在数据库进行update操作的时候,read需要等待update的事务提交以后才能读取。
3)repeatable read级别
A将a=1,未提交事务。B检查a=1,不处理b。A提交事务,B再次查询a并打印日志a=0。
这时候,a=1,b=0,日志a=0。在B事务中a的值前后一致了。
repeatable read其实就是在事务开启的时候,不允许其它事务进行update。但是如果是insert操作呢?所以,如果出现insert操作,则又会有幻读问题。
4)serializable级别
A将a=1,提交事务。B检查a=1,处理b=1,提交事务。
这时候a=1,b=1。A和B事务串行化处理了,简单来说就是单线程的节奏。所以,可以想象serializable这个级别的效率有多低下。
不过,如果不考虑效率问题,serializable会是解决脏读、不可重复读,幻读的最简单的方案了。
总结
本文简单叙述了事务的基本概念,并讨论了ACID的特性。比较麻烦的其实是事务的隔离级别,如果我们需要理解或者编写一套事务框架隔离级别将需要足够地了解。
原文地址:https://www.cnblogs.com/lay2017/p/12081604.html