事务的ACID特性:
-
原子性(Atomicity)
:当前事务的操作要么同时成功,要么同时失败。原子性由undo log日志
来保证 -
一致性(Consistency)
:使用事务的最终目的,由业务代码正确逻辑保证 -
隔离性(lsolation)
:在事务并发执行时,他们内部的操作不能互相干扰 -
持久性(Durability)
:一旦提交了事务,它对数据库的改变就应该是永久性的。持久性由redo log日志来保证
在 mysql 的 InnerDB 中, 定义了四中隔离级别, 级别越高事务隔离性越好, 性能越低, 而隔离性是由Mysql的各种锁一级MVCC实现的;
read uncoummit
(读未提交): 脏读
B事务读到A事务还未提交或回滚的数据*read commit
(读已提交): 不可重复读
只能读到已提交的事务, A未提交前=500, B读到500, A修改为800, B设置+300也等于800*repeatable read
(可重复读): 幻读默认事务
第一次执行sql时, 进行快照保存, 读快照中的内容serializable
(串行) 没问题(性能底下)
A执行事务时, B也执行事务, A未提交前,B的sql进行阻塞, A提交或回滚后B进行执行
undo 回滚日志
CopyOnWrite机制, 复制快照修改, 然后快照替换
每个更新或者插入操作都会记录日志, 并且每个都有一个记录id, 整个事务会记录一条日志链.
事务的可见性
在可重复赌隔离级别, 当事务开启, 执行任何查询sql时会生成当前事务的一致性视图Read-view, 该视图在事务结束之前都不会变化,(如果是读已提交隔离级别在每次执行查询sql时都会重新生成), 这个视图是由执行查询时所有未提交事务id数组(数组中最小id为min_id)和已创建最大事务id(Max_id)组成, 事务里的任何sql查询结果需要从对应的版本链中最新数据开始逐条跟read-view进行对比得到最终的快照结果.
版本链对比规则:
- 如果row的trx_id落在绿色部分(trx_id<min_id), 表示这个版本是已提交的事务生成的, 是可见的
- 如果row的trx_id落在红色部分(trx_id>max_id), 表示这个版本是未来启动的事务生成的, 是不可见的
- 如果row的trx_id落在黄色部分(min_id<=trx_id<=max_id),包括两种情况
a: 若row的trx_id在视图数组中, 表示这个版本是由未提交的事务生成, 不可见, 当前自己的事务时可见的,
b: 若row的trx_id不在视图数组中, 表示这个版本是已提交的事务生成的, 可见
已提交事务 ==> min_id = 200已提交事务max_id=300 ==> 未开始事务
对于删除情况可以认为是update的情况, 会将版本链上最新的数据复制一份, 然后将trx_id修改成删除操作的trx_id, 同时在该条记录的头信息(record header)里的(delete_flag)标记位写上true, 来表示当前记录已经被删除, 在查询时按照上面的规则查到对应的记录如果delete_fla g标记为true, 意味着记录已经被删除, 则不返回结果
事务执行流程
- update
- 从磁盘DB文件中读取放到 BufferPool 缓存池中进行修改
- 写入undo日志以便回滚
- 写入redo Log Buffer缓冲区, 写入redo磁盘文件
- 写入binlog日志, 提交
- 写入commit标记到redo日志文件中,
- 开启异步缓存池的数据进行IO线程操作随机时间片写入db文件
当IO线程还没成功, mysql宕机了, 重启时会从redo中查询出来缓存到缓存池buffer Pool
为啥使用redoLog日志?
redo日志为顺序写, 写入磁盘的末尾追加
数据库表每个表一个db文件, 并且经常有删除,需要写到删除的位置. 所以无法追加, 只能随机写