数据库事务ACID原则学习分享

本人结合自己对技术的理解,努力以通俗易懂的内容帮助大家了解技术的基本原理,内容仅供参考,不对或不完善的地方请大家指教,感谢关注!

一、事务概念的引入

当今,信息化技术非常发达的时代,我们通过手机上的银行APP软件就可以完成转账业务,比如,张三可以在APP上将100元从自己的账户转给李四的账户,在这个转账过程中银行的信息系统发生了3阶段的工作。
《数据库事务ACID原则学习分享》
如上图所示,张三在银行APP软件填写转账的相关信息并提交后,APP就会通过Internet网络向银行的交易服务系统提交“向李四银行账户转账100元的申请”,当银行交易服务系统接收到这条转账请求信息后,会先对这条申请信息进行解析并生成一套数据库系统能执行的两条指令并打包发给银行内部的数据库系统。

  • 第1条指令: “从张三银行账户减100元”
  • 第2条指令: “给李四银行账户加100元”

银行的数据库系统存储了银行所有账户的储蓄信息,当接到这两条指令后,就会在“账户信息表”中进行账户储蓄信息的变更操作,如下图所示:
《数据库事务ACID原则学习分享》
如果大家已经看明白上面内容,那么接下来就开始抛出本文要讲的“数据库事务”的概念了,通俗讲,数据库事务就是数据库系统为完成某一个业务逻辑工作,进行的一系列的对数据库的操作,上文提到的张三向李四账户转100元就是一个业务逻辑工作,在这个业务逻辑中会对数据库执行两项操作,第1项操作就是在银行数据库中,从张三的账户储蓄扣除100元,第2项就是向李四账户储蓄增加100元。

但是有个可能问题,如果数据库系统在做完对张三的账户扣钱操作之后,数据库系统突然出现故障无法继续工作,那么就会出现“张三账户的钱扣掉,但李四账户没有加钱”的问题,整个银行储蓄账户的数据库就会凭空蒸发100元的信息,如果出现此类问题,那么银行系统就是完全不可信任的,谁也不想平白无故的少钱呀!

为了保障数据库系统不会出现以上的问题,数据库事务必须要从技术上满足“原子性”,顾名思忆,原子就是无可再分的最小单元,该原则就是约束数据库事务在执行的过程中从技术上做到“要么该事务中所有操作都执行完成,要么就全不执行,不能出现执行一部分操作的情况”,当事务满足了该原则后就不会出现上文提到的转账少100元的问题。

以上通过一个简单的示例为大家引入了一个数据库事务概念,并解释了“原子性”事务原则,为了保障数据库事务能够准确、一致的执行,就必须要遵守国际标准的ACID原则,下一章节就会给大家介绍这个原则。

#扩展性了解
《数据库事务ACID原则学习分享》
*Jim Gray 詹姆士·格雷,数据库界三位图灵奖获得者之一,他为数据库最大贡献之一就是就是开创了数据库事务的多数重要理论,并在1983年,由 Andreas Reuter和Theo Härder 创造了数据库事务四大原则ACID来描述数据库事务的这些理论,如果想了解想了解更多的Jim Gray信息请网上搜索或拜读一下Jim Gray的重要著作《事务处理》一书。

数据库三位图灵奖获得者及主要贡献
Charles W. Bachman 查尔斯·巴赫曼 1973年获得图灵奖,数据库技术,1964年创造了最早的网状数据库IDS,数据库标准制定
Edgar Frank Codd 埃德加·弗兰克·科德 1981年获得图灵奖,关系数据库之父
Jim Gray 詹姆斯·尼古拉·格雷 1998年获得图灵奖 数据库事务

二、数据库事务的ACID原则

上一章节提到了,数据库事务要完整、一致的执行对数据库的操作,必须满足事务ACID 原则,即原子性(Atomicity) 、一致性(Consistency)、隔离性(Isolation)、持久化(Durability),这4项原则就是国际上公认的数据库事务标准,支撑事务应用的数据库系统必须要满足ACID原则。

1. 原子性(Atomicity)

在一个数据库事务中所有的操作要么全部都做完,要么全部都不做,不能出现部分完成的情况,上文提到的转账示例就是讲述事务原子性,完成一笔转账事务,数据库系统从张三账户储蓄减少100元,由1000元变成900元,李四的账户增加100元,由600元变成700元。
《数据库事务ACID原则学习分享》
如果在这笔转账事务过程中出现操作失败的情况,导致张三账户的储蓄扣掉了100元,但李四的账户储蓄没有加100元,那么为了满足原子性原则,就必须回滚对张三账户扣钱的操作,即,还原他的账户储蓄到1000元,就当这个事务没有发生过。
《数据库事务ACID原则学习分享》
2. 一致性(Consistency)

上文提到的张三给李四转账的事务示例,也体现了一致性原则,但侧重点不一样,一致性就是强调数据库经过事务操作后由一个状态转变到另一个状态,数据依然要保持一致,举例解释一下,转账事务执行前张三和李四的账户储蓄加起来是1600元,转账事务完成后张三和李四的账户加起来依然要为1600块,钱只是从一个账户转移到另一个账户,两个账户的钱加在一起的总额并没有因为此次转账发生变化,这就是一致性,为了解释的全面些,咱们再举另一个示例,见下文。

张三到一家电商平台,比如京东,要购买一款最新的小米手机,发现该手机明天有促销,促销时间从当天早上8点开始到晚上10点,促销期间售价由原来的2000元下调到1500元,优惠力度很大,但限定前1000名才可以享受优惠,这里规定的前1000名就是数据库系统的一致性约束。如果张三当天下单晚了,在他支付的那一刹那,正好有另外一个买家占到第1000名,按一致性约束的要求,他就不能再以优惠价格购买该手机,如果还能买就会破坏数据库事务一致性原则,因为在数据库中有一张表记录该促销1000名上限的约束规则,就是促销期间优惠价格销售的手机总数不能超过1000,因此,每次新增一个1500元购买,数据库系统都会统计一下数量并和约束规则核对,如果数量没有超过1000则可以执行1500元价格,超过了就不能执行此价格,如果数据库在技术上未能控制住该约束规则,就发生1500元订单超过1000个,不符合1000名的约束规则,那么,数据库就违背了一致性原则,因此,数据库通过技术手段来解决一致性问题。

3. 隔离性(Isolation)

在实际的业务应用中,通常会有多个事务同时对同一个数据库的同一条或多条数据进行同时操作,那么事务之间难免会互相影响导致出现数据错误,数据库事务隔离性技术正是处理和解决此类问题的一项重要技术,事务的隔离性相对其它事务原则要复杂,它主要包括4个隔离级别,分别是读未提交(Read uncommitted)、读提交(Read committed)、可重复读(Repeated read)以及序列化(Serializable),一般来讲一套数据库只能启动一种隔离级别,需要根据业务需要来进行取舍,接下来我们来逐个分析这4个隔离级别。

1)读未提交(Read uncommitted): 一个事务可以读取另一个未提交事务的中间数据

这里说的“提交”是指事务完成的标志,一个事务成功提交就说明这个事务成功完成,不然事务就会一直处在执行中的状态,涉及修改的数据也不是最终值,如果数据库事务隔离级别设定为读未提交,那么该事务中间状态的数据就可能会被其它事务读到,用到别的事务应用中并导致错误的发生,比如以下示例:

老婆的银行账户里存有300元,打算使用自己的账户在网上购买一套500元的化妆品,但账户的钱不够支付该订单,然后就打电话给老公,让他转200元到她的账户,老公答应马上就转并同时告诉她过一会试试能不能支付,接下来老公通过手机银行APP启动一笔转200元到老婆账户的转账事务,但老婆并不知道钱什么时候到账,干脆就直接在网上启动支付事务试试,正常来说,需要等老公把这笔转账事务完成以后,老婆才能开始支付事务,但如果数据库隔离级别设定为读未提交,那么就会出现问题,见下图:

《数据库事务ACID原则学习分享》

从上图,我门可以清晰的看到,老公发起的转账事务是先启动的,事务中有一项操作是给老婆账户里加200元,因为数据库设定的隔离级别是读未提交,那么老婆发起的支付事务是可以读到老公转账事务过程中的数据,当转账事务把老婆账户里的钱刚增加到500块,恰好接下来被老婆的支付事务读到并完成了500元支付,但之后老公的转账事务因系统问题未能成功提交,那么就按照失败回滚处理,就当没有给老婆转过钱,但结果老婆确能把这笔钱给花了,老公的钱一分钱也没少,亏的最后是银行。

由以上给出的示例,老婆的支付事务读取的那500元钱,其实并不是真实的,因为,老公的转账事务并没有真实的把那200元钱转过来,所以支付事务读出来的数据是不真实的,这在数据库事务概念中有一个专业的术语,叫“脏读”,即一个事务读取了另一个事务的未提交的数据。

如果解决读未提交产生的脏读问题呢,那就需要将数据库事务的隔离级别提高到读提交的级别。

2)读提交(Read committed): 一个事务要等另一个事务提交后才能读取数据

一个事务正在修改一条或多条数据,那么其它事务是不可以读这些数据,必须要等第一个事务完成后才能读,这样就不会像读未提交一样,其它事务读取了脏数据。

但读提交也有它的局限性,因为读提交只解决了不让其它事务读本事务正修改的数据问题,但控制不了其它事务修改其要读的数据,在有些场景依然会出现问题,如下面这个例子:

老公打算通过其银行账户在一个网上购物商城买一部2000元的小米手机,他知道自己账户里有2100元,然后点击支付按钮开始走支付事务,这个网商平台是实时结算,商城的账户在另一家银行,那么商城的第三方支付系统首先会先查老公的账户里钱够不够,然后联系另一家银行的系统准备划账,但就在这个时候,她老婆用他的卡在另一家商城购物,大概花费500元,也发起了一个支付事务,但这个事务完成的速度很快,把钱直接扣掉500元,剩下1600元,但接下来老公的这笔支付事务准备开始扣除2000元钱的时候,再次查询发现钱不够了,然后就通知老公剩下的钱不足,不能完成支付,老公在支付页面上看到的情况就是,您账户储蓄满足支付,开始跨行支付中->对不起,您账户的钱不足无法完成支付,并且是等了很久才给出的账户钱不足提示。这个问题就是 不可重复读 问题,一个事务中前后两次读同一条数据,结果值是不一样的,要解决不可重复读问题,就要将隔离级别提升到可重复读级别。
《数据库事务ACID原则学习分享》
3)可重复读(Repeatable read): 在一个事务开启时,其它事务不能修改其要读取的数据

当一个事务开始后,该事务所有涉及读的数据都不能被其它的事务修改,这样就能保证其事务执行过程中多次读取同样的数据不会发生变化,这种隔离级别在数据库事务中已经算比较高的级别,基本可以满足大多数事务应用的场景,但是可重复读虽然能够防止其它事务修改它的数据,但不能防止其它事务在它读取数据范围插入数据,举例:

老公通过使用银行的APP软件做一个统计,主要包括两项,第一项是找出化妆品总共消费额,另一项是化妆品单笔最高消费金额,老公启动了这次的统计事务,统计这两项都支出多少钱,事务开启以后,数据库系统先按化妆品的类别码进行扫描,将所有化妆品的订单记录都扫描出来,然后计算个汇总值,然后数据库系统进行第2次扫描,本次扫描是通过排序找出消费金额最高的单笔消费,但最后APP返回出来的结果就是,化妆品总共消费1000元,化妆品最高单笔消费1500元,这样老公很是纳闷,为什么总共消费1000元,其中的最高单笔消费确是1500元,不合理呀,其实问题就出在数据库系统统计事务第1项刚做完,正准备开始下一项统计的时候,老婆发起了另一笔支付事务完成了一笔1500元的化妆品消费,结果就在老公的统计事务的两项统计操作之间,出现了一笔新的订单。
《数据库事务ACID原则学习分享》
由上边的示例可以了解到,可重复读不能解决数据库事务的幻读问题,幻读是什么呢?从字面上就能理解,就是在一个事务中重复读两次一个范围的数据,但发现第2次多读了数据出来,感觉就像是发生了幻觉,幻读同样也是数据库事务在多用户同时并发使用中出现的问题,但可重复读的事务隔离级别已经是很高的了,使用的也非常广泛,如果真的想杜绝所有的事务隔离问题,那只有最后一个隔离级别–序列化。

4)Serializable序列化:Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,一个事务完成以后开始下一个事务,不会出现前三个隔离级别出现的脏读、不可重复读以及幻读的问题,但这种隔离级别效率最为低下,一般数据库事务不会使用这种隔离级别。

4. 持久化(Durability):一个数据库事务一旦成功提交完成后,所有这个事务对数据库所有更改的内容全部被保留下来,在数据库系统正常运行的过程中,除非新的事务要去更改,否则内容不会改变,以及一些系统异常的情况下除非是存储磁盘损坏,不然数据也不会改变。

三、总结

数据库事务ACID原则是数据库系统执行事务保障数据库正确一致的基本原则,市面上所有事务型数据库技术产品都必须要满足该原则,本文的初衷就是让大家能够快速理解事务基本原则的概念,感谢大家阅读本文,希望能提出宝贵意见!

    原文作者:技术通俗讲
    原文地址: https://blog.csdn.net/gavinpro001/article/details/89935848
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞