mysql 事务,ACID原则,脏读,不可重复读,幻读
什么是事务
底层思想:要么都成功,要么都失败
- sql执行 A给B转账,A(1000)--200-> B(200)
- sql执行 B收到A的钱 A(800)------> B(400)
上面有两个sql共同执行,处理同一个事件
假设最开始A有1000元,B有200元,然后A给B转账200元。
如果在其中任意一个sql执行失败后,这被转账的200元不应该从A上面直接扣除,也不应该给B账户上面直接增加。这时候应该回滚到最开始的状态。
而转账成功的条件是,上面两个sql执行均成功的前提下。
事务原则
事务原则:ACID原则(原子性,一致性,隔离性,持久性),也正是应该这些原则,可能会产生脏读,幻读等情况
- 原子性
上面的案例中,转账扣费和到账这两个步骤需要一起成功或者一起失败,不能只发生其中一个动作
关键就是一个事务中的sql要么一起成功,要么一起失败,不存在只有一部分成功一部分失败的情况 - 一致性
针对一个事务操作前与操作后的状态一致
在上面案例中,转账成功或者失败的最终结果,两者之间的金额总数肯定是不变的,转帐前A+B的总金额是1200,转账后依然是1200,不会发生任何改变。可以理解为能量守恒定律或者物质守恒定律。这个也被称为最终一致性 - 持久性
事务结束后数据不随着外界原因导致数据丢失
上面案例中
操作前:A(1000),B(200)
操作后:A(800),B(400)
如果在操作前(事务还没提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为
A(1000),B(200)
事务没有提交(包含执行失败回滚),恢复到原状
如果在操作后(事务已经提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为
A(800),B(400)
事务已经提交(全部成功的情况下),持久化到数据库
事务一旦提交且执行成功,结果就不可逆了
4. 隔离性
针对多个用户同时操作,主要是排除其他事务对本次事务的影响
比如A给B转账的同时,C也在给B转账,这里被看成两个事务,互不影响
因为事务原则产生的问题
- 脏读:由于隔离性级别不够而产生的
别人博客说明:
脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
例如:事务T1修改了某个表中的一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因回滚(Rollback)了,那么事务T2读取的数据就是脏的(无效的)。
解决办法:把数据库的事务隔离级别调整到READ_COMMITTED(读提交/不可重复读)
我的理解:
比如B原本有200元,然后A和C均给B转账,A转账200给B,C转账100给B,因为同时产生,而B又被两个事务同时读取的初始值都是200元,结果分别得到400和300这两个结果,造成数据错误的情况。这个情况就如同在多线程或者多进程的情况下对同一个文件进行处理,但是这个过程又没有加锁,造成最终文件处理达不到预期值。
在mysql中,最高级别的隔离就是加一个锁,每次只处理一个事务,在安全性达到最高的情况下,效率也是最低的
- 不可重复读:在一个事务内读取的某一行数据,多次读取的结果不同(这个不一定是错误,可能是某些场合不对)
别人的说明:
不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。
例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,T1此时再次读取该数据,两次读取便得到了不同的结果。
解决办法:把数据库的事务隔离级别调整到REPEATABLE_READ(可重复读)
我的理解:
这个问题,其实就是第一次读取和第二次读取的数据不一致,可能在第一次读取完后,有其他事务处理更改了数据,造成第二次读取的数据与第一次读取的数据对应不上,这里可以加行锁解决。
- 幻读(虚读):在一个事务内读取到别的事务插入的数据,导致前后读取不一致
别人的说明:
幻读也是指当事务不独立执行时,插入或者删除另一个事务当前影响的数据而发生的一种类似幻觉的现象。
例如:系统事务A将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
我的理解:
这玩意儿和不可重复读不是一个东西吗?
- 不可重复读和幻读的区别:
不可重复读出现多是因为修改;幻读重点是新增、删除。mysql中的REPEATABLE_READ模式引入了间隙锁(GAP),解决了幻读的问题。不论是什么方式解决幻读,都会付出一定代价的性能让步。所以说在业务需求和技术方案之间权衡也是技术人员最需要掌握得技能之一。
解决办法:把数据库的事务隔离级别调整到SERIALIZABLE_READ(序列化执行),或者数据库使用者自己进行加锁来保证。
参考连接:
https://www.cnblogs.com/vinter/p/12581238.html
https://www.cnblogs.com/yubaolee/p/10398633.html
其实仔细想想,原子性是其他三个的根源,可以这样说明:原子性是事务的基础,持久性和隔离性是手段,一致性是目的
测试事务,实现转账
-- mysql是默认开启事务自动提交的
-- 设置事务自动提交开启(默认开启)
set autocommit = 1
-- 设置事务自动提交关闭
-- 关闭自动提交
set autocommit = 0
-- 手动处理事务
set autocommit = 0
-- 事务开启
-- 标记一个事务的开始,从这个之后的sql都在同一个事务内
start transaction
-- 设置一个事务的保存点,保存点可以存在多个
savepoint <命名>
-- 提交:持久化(成功的情况下)
commit
-- 回滚:回到原来的状态(失败的情况下)
rollback
-- 回滚到保存点
rollback to savepoint <保存点名字>
-- 删除保存点
release savepoint <保存点名字>
-- 事务结束
-- 开启自动提交
set autocommit = 1

浙公网安备 33010602011771号