背景
我们在实际业务场景中,经常会遇到数据频繁修改读取的问题。在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能造成数据不可挽回的错乱,所以我们需要用事务来对数据进行管理。
1. 事务的概念
事务必须服从ACID原则。ACID指的是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。 通俗理解,事务其实就是一系列指令的集合。
原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。 一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。 隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。 持久性:当事务正确完成后,它对于数据的改变是永久性的。
2. 并发事务导致的问题
在许多事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据时,会带来一些的问题。
第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。
小明去银行柜台存钱,他的账户里原来的余额为100元,现在打算存入100元。在他存钱的过程中,银行年费扣了5元,余额只剩95元。突然他又想着这100元要用来请女朋友看电影吃饭,不打算存了。在他撤回存钱操作后,余额依然为他存钱之前的100元。所以那5块钱到底扣了谁的?
脏读:一个事务读取到另一个事务未提交的更新数据。
小明的银行卡余额里有100元。现在他打算用手机点一个外卖饮料,需要付款10元。但是这个时候,他的女朋友看中了一件衣服95元,她正在使用小明的银行卡付款。于是小明在付款的时候,程序后台读取到他的余额只有5块钱了,根本不够10元,所以系统拒绝了他的交易,告诉余额不足。但是小明的女朋友最后因为密码错误,无法进行交易。小明非常郁闷,明明银行卡里还有100元,怎么会余额不足呢?(他女朋友更郁闷。。。)
幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。
公司财务A在进行员工薪资核算业务,需要对小明的工资进行计算并录入系统,必须查询两次明细信息,然后将后一次的明细信息计算总数出来。财务在第一次明细查询时,基本工资2000元,全勤奖1000元,提成2000元,共计5000元。在进行第二次计算时,财务B突然接到通知,需要把下个月的节日福利也在这个月的工资中发放,每人100元。于是财务B在每个人的工资明细中又加了一条节日福利100元。而此时财务A获得第二次查询小明的工资明细后,发现工资明细变成了4条数据,总数是5100元。两次计算结果相差100元,财务A奇怪这多出来的一条明细100元是哪里来的呢?(都怪财务B不告诉A。。。)
不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。
小明在手机上购买起购价为1W元理财产品。系统首先要判断他的余额够不够购买理财产品,如果足够再获取当前的余额,进行申请。系统第一次读取到小明的余额还剩1W元,刚好足够购买产品。但是这个时候刚好他女朋友又看中了一个包包5000元,这次密码终于不会再错误的女朋友毫不犹豫刷了小明的银行卡买下了这个包包。但是这个系统刚好在进行第二次确认,发现小明的余额上只有5000元,根本不够购买。于是系统很生气,拒绝了小明的购买行为,告诉他,你真是个骗子!!!
第二类丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,并提交,第一个事物所做的改变就会丢失。
小明和女朋友一起去逛街。女朋友看中了一支口红,(对,女朋友就是用来表现买买买的)小明大方的掏出了自己的银行卡,告诉女朋友:亲爱的,随便刷,随便买,我坐着等你。然后小明就坐在商城座椅上玩手机,等着女朋友。这个时候,程序员的聊天群里有人推荐了一本书,小明一看,哎呀,真是本好书,还是限量发行呢,我一定更要买到。于是小明赶紧找到购买渠道,进行付款操作。而同时,小明的女朋友也在不亦乐乎的买买买,他们同时进行了一笔交易操作,但是这个时候银行系统出了问题,当他们都付款成功后,却发现,银行只扣了小明的买书钱,却没有扣去女朋友此时交易的钱。哈哈哈,小明真是太开心了!