-
-
- MySQL事务的特性
-
- [问题1 : 一致性是什么?在哪里有体现?](#问题1 : 一致性是什么?在哪里有体现?)
- [数据的隐藏列与事务的Read View](#数据的隐藏列与事务的Read View)
-
- 隐藏列
- [事务的Read View (mvcc)](#事务的Read View (mvcc))
- 什么时候数据是可见的?
- [MySQL 事务的隔离形式与读取数据不一致的问题](#MySQL 事务的隔离形式与读取数据不一致的问题)
-
MySQL事务的特性
- 原子性: 要么全部成功,要么全部失败(undo log)
- 一致性: 事务会按照管理员制定的规则运行,保持数据的一致
- 隔离性: 多事务并行的情况下,事务之间是不会相互影响的(mvcc)
- 持久性: 事务一旦提交,便会持久化到磁盘,不会丢失数据.(binlog)
问题1 : 一致性是什么?在哪里有体现?
网路上的解释:
A有2000元,啊B有500元,两个人总计2500元。然后A给B转账1000元,A的余额减1000元,B的余额加1000元,这时 A的余额1000元,B的余额1500元,总计还是2500元,事物前后的数据是一致的。不会出现 A 的钱扣了,B 的钱没有增加 的情况.
这种看起来很模糊,感觉和原子性类似,要么全部成功要么全部失败,一致性就像是强行塞进去的一种性质.
而且这样描述 MySQL 事务的一致性是可以被人为打破的
因为 MySQL 的语句是程序员定义的,他想怎么写就怎么写,如果程序员可以写一个有问题的程序,MySQL 事务的一致性不就被打破了吗?
比如:程序员可以写给 A 扣 1000 元,只给 B 加五百元.(MySQL 的语句是可以自定义的),那么不就有 500 元凭空消失了吗,MySQL 开始前总金额是 2500,事务执行后就只剩 2000 元了.是不是意味着MySQL 的一致性被打破了.
但事实上 MySQL 的一致性与其他几种性质一样,无论 MySQL 的语句如何变化,都能保持他的四种特性
所以一致性到底是什么?
- 事务会按照管理员制定的规则运行,保持数据的一致
比如: 制定的规则是 扣除金额=增加金额,那么事务结束总金额一定是相同的,数据是一致的
如果制定的规则是 扣除金额是增加金额的两倍,那么总金额一定会丢失扣除金额的一半,因为MySQL 是按照管理员制定的规则进行运行保持数据的一致.而不是根据他的值,最后值的变化一定是满足管理员的规则的.这便是数据的一致性
感觉没什么用,因为所有的程序都是根据程序员制定的规则进行运行的.这本来就是一个常态.
MySQL 的事务是如何工作的?
数据的隐藏列与事务的Read View
隐藏列
- 数据除了我们定义的一些列之外还有一些隐藏的列

trx_id 是事务版本的id, undo log指向之前数据的状态
事务的Read View (mvcc)
每一个事务在创建的时候都会有一个Read View
包含:
creator_trx_id: 创建的事务 id
m_ids.:目前活跃的事务 id
min_trx_id: 最小的活跃事务 id
max_trx_id:下一个创建的事务的 id(最大活跃的加 1)
事务每次更改数据都会记录更改的事务与更改之前的状态(undo log)
什么时候数据是可见的?
- 小于最小事务 id,说明事务开始前就已经提交了,所以是可见的
- 小于最大事务 id+1 并且不在活跃事务 id列表中的,说明已经提交了
MySQL 事务的隔离形式与读取数据不一致的问题
- MySQL的事务是并发(也可以串行)运行,并且是相互隔离的,那就会有一些数据读取不一致的问题
读未提交
读取最新版本的数据
脏读
- 事务 b 修改一条数据(未提交),事务 a 读取这条数据,这时 b 事务回滚,事务 a 再一次读取这个数据
- 这时事务 a 中两次读取的数据的结果是不一样的,这就是脏读
读提交
每次都会刷新事务的 Read View ,确保可见所有的已提交记录,获取已提交的最新的数据
不可重复读
- 事务 a 读取一条数据,之后事务 b 修改这条数据(提交),事务 a 再一次读取数据,
- 这时事务 a 中两次读取的数据的结果是不一样的,这就是不可重复读
可重复读
事务的Read View 不会改变,可见的数据不会改变,每次可以获取到的数据都是事务开始之前的状态
幻读
- 事务 a 读取m 到 n 区间的数据, 之后事务 b 添加一条 m 到 n 区间的数据 ,事务 a 再一次读取m 到 n 区间的数据数据,
- 这时事务 a 中两次读取的数据的结果(条数)是不一样的,这就是幻读.
串行化
单线程依次的执行,一般很少使用,性能太低
MySQL 默认使用的是可重读
可重复读可以完全避免幻读的问题吗?
不可以,有两种情况可重复读还是会出现幻读
- 事务 A(读区间,更改区间,再读区间);事务 B(在事务 A期间,更改前插入或者删除了某条数据),会出现幻读的情况
- 事务A先用读快照(普通 select), 事务 B 插入一条数据,再使用读当前(select...for update)就会出现幻读
可以理解update 是要使用最新的数据进行 update,就会去发现最新的数据,可见范围就变了,就出现了幻读.
参考
https://blog.csdn.net/yzf279533105/article/details/129370396
https://www.xiaolincoding.com/mysql/transaction/mvcc.html#事务有哪些特性