事务的4个特性 AICD
- 原子性(A): 一个事务中的操作要么全部成功,要么全部失败
- 隔离性(I): 两个事务之间互不影响
- 一致性(C): 通过数据库字段约束以及业务程序实现符合现实世界的约束
- 持久性(D): 一旦事务执行成功,那么结果就应该永久保存
redo日志
前面的内容我们知道了Mysql修改数据的时候都是现在内存中修改,然后在刷新到磁盘上面,在刷新之前系统可能出现了故障导致内存中的数据丢失了,这该怎么办呢? 简单粗暴的方法就是每次事务提交的时候就刷新数据到磁盘,这样也可以解决,但是存在两个问题:
- MySql与磁盘交互的最小单位是一个页,至少要刷新一个页面,太耗性能
- 随机IO刷新会很慢
MySQL的做法是在事务提交的时候记录 哪个表空间中的 哪个页面及页面偏移量的数据需要修改成什么,这就是redo日志。如果系统出现了崩溃,在恢复后需要从redo日志中 重新执行更新逻辑
- redo日志占用的空间较小,只记录表空间ID,页号,偏移量以及需要修改的数据
- redo日志是顺序写入到磁盘
事务的隔离级别
事务在并发执行的时候会遇到一些问题
- 脏写: 一个事务修改了另一个未提交事务的数据
- 脏读: 一个事务读取了另一个未提交事务的修改过的数据
- 不可重复读: 一个事务修改了另一个未提交事务读取的数据,导致未提交的事务两次读取的结果不一致
- 幻读: 一个事务根据某些条件查询出来了一些记录,另一个事务删除或插入了一条符合查询条件的数据。
为了解决这些问题,SQL标准中定义了4中隔离级别
- read uncommitted: 解决脏写,可能出现脏读、不可重复读、幻读
- read committed: 解决脏写、脏读,可能出现不可重复读、幻读
- repeatable read: 解决脏写、脏读、不可重复读,可能出现幻读
- serializable: 解决所有问题
MySQL的默认隔离级别是 repeatable read.
设置事务的隔离级别命令:
set [global|session] transaction level [read uncommitted|read committed|repeatable read|serializable]MVCC原理
innodb引擎的表,它的聚簇索引记录中都包含了下面两个必要的隐藏列:
- trx_id: 一个事务每次对某条记录进行修改的时候,都会把该事务的事务id设置给trx_id
- roll_pointer: 每次对聚簇索引的记录进行修改的时候,都需要把旧的版本写入到undo日志中,这个roll_pointer就是指向这条旧的数据。
每次对记录进行一次改动,都会记录一条undo日志,每条undo日志都会有trx_id、roll_pointer两个字段,roll_pointer指向更早的记录,这种机制就是多版本并发控制(MVCC)

MySQL如何实现4种隔离级别
read uncommitted: 由于允许读取到未提及事务的数据,所以事务直接读取MVCC的最新数据即可。
serializable: 这个级别就是要求所有事务串行执行,所以MySQL采用的是加锁的方式实现
read committed / repeatable read: 这两个隔离级别都不允许读取到未提交事务的数据,核心问题就是需要判断MVCC版本链中哪些数据是对当前事务可见,所以MySQL又引入了ReadView(一致性视图)
ReadView 主要4个字段:
- m_ids: 生成ReadView的时候,当前系统活跃的事务id列表
- min_trx_id: 生成ReadView的时候,当前系统活跃最小事务id
- max_trx_id: 生成ReadView的时候,系统应该分配给下一个事务的id
- creator_trx_id: 生成ReadView的事务的事务id
在访问某条记录的时候需要按照下面的步骤来判断:
- 如果被访问记录的trx_id 和 ReadView中的creator_trx_id 值相同,那么表示当事务在访问它自己修改过的数据,允许访问
- 如果被访问记录的trx_id 小于 ReadView中的min_trx_id,说明生成该版本的事务在生成ReadView的时候已经提交,允许访问
- 如果被访问记录的trx_id 大于等于 ReadView中的max_trx_id,说明生成该版本的事务在生成ReadView的时候还未提交,不允许访问
- 如果被访问记录的trx_id在ReadView中的min_trx_id和max_trx_id之间,就需要判断记录的trx_id是否在m_ids的列表中,如果存在说明该版本的时候在生成ReadView的时候还未提交,不允许访问,如果不在则可以访问。
当出现了不允许访问的时候就沿着版本链找上一条记录,重复以上的判断,直到找到可以访问的数据。
read committed 与 repeatable read最大的区别就是生成 ReadView 的时机不同,read committed是在每次查询的时候都会生成一个ReadView, 而repeatable read只在第一次读取数据的时候生成ReadView。
原文链接: http://herman7z.site