- 什么是事务
- 原子性:
- 一致性
- 隔离性
- 问题1: 为什么MySQL要使用mvcc实现隔离性而不使用 锁 解决并发问题?
- 持久性
- 问题2: MySQL 不是磁盘数据库吗,持久化为什么是 redo log 保证的?
- 问题 3: redo log 储存了什么东西,持久化(崩溃恢复是怎么做的?)
- 问题 4 : MySQL 的 bing log (二进制日志)已经有全量的数据,为什么还要使用 redo 做崩溃恢复,为什么不直接使用 bin log做崩溃恢复.
- 问题 5 :redo log与 bin log 如何保证数据的一致性?
- MySQL 的 MVCC 多版本并发控制
- 事务的read view(事务能感应到的数据有哪些?)
- 事务的隔离级别
- 读未提交
- 脏读问题
- 读提交
- 不可重复读问题
- 幻读问题
- 可重复读(默认级别)
- 串行化
- 问题 6 : 可重复读可以完全避免幻读的问题吗?
- 问题 7 : 你们公司(之前的业务)使用的什么隔离级别?
MySQL 数据的版本链(undo log)
MySQL 的数据有两个隐藏列,
- 第一是当前数据的版本(修改数据的事务 id)
- 第二的指向数据之前状态的指针(指向的是一行 undo log 日志)
这样我们的数据就像链条一样可以获取到之前数据的版本(做回滚与 mvcc 都非常有用)
什么是事务
ACID
原子性:
任务要么全部成功要么全部失败,不存在只执行一部分的情况
- 原子性是通过undo log(回滚日志),当事务失败就会将数据回滚到之前的状态
一致性
- 事务前后始终保持一致性约束: 比如之前有唯一键约束,事务后也保持唯一键约束.(程序按照程序员的规则运行)
隔离性
- 多事务并行的时候是相互不影响的(通过 mvcc 实现 : 多版本并发控制)
问题1: 为什么MySQL要使用mvcc实现隔离性而不使用 锁 解决并发问题?
- 锁会阻塞其他的事务,而多版本并发控制,通过数据版本链事务能感应到的数据,可以避免阻塞.–提高性能
- 锁并不满足事务(多个任务)的原子性,如果事务失败需要通过版本链将全部的任务修改的数据都回到事务开启前的状态
- 所以 MySQL 是锁与 mvcc 共用一起保证事务的隔离与原子性(比如修改单行数据:事务先上行排他锁,执行任务;事务提交后再释放锁),这样可以避免数据更新丢失的问题并且可以在失败的时候将数据回滚到之前的状态.
持久性
- 事务提交后数据将持久化,不会因为故障丢失数据(redo log(重做日志) 实现)
问题2: MySQL 不是磁盘数据库吗,持久化为什么是 redo log 保证的?
- 数据持久化磁盘的消耗比较大(慢),MySQL 储存数据的流程是
- 先存缓冲区(内存),再找合适的时机落盘,这样性能会高非常多(内存的 io 有百万,千万级 qps)
- 但是如果数据没来得及落盘,服务就崩溃了(断电),那么数据就有丢失的风险
redo log(重做日志): 解决崩溃恢复的数据丢失问题
- redo log会记录写(命令)数据的偏移量(数据做了什么修改)
- redo log会通过追加的形式将日志记录到磁盘
- redo log 的落盘机制(可以设置的)
- 事务提交立即落盘
- 事务提交落用户缓冲区(每隔 1s,自动落盘/占用缓冲区一半自动落盘)-只要 MySQL 服务宕机数据就会丢
- 事务提交落内核缓存(由内核缓存决定落盘时机)-只要操作系统正常数据就不会丢
- 记录 redo log 比数据落盘更快,因为日志是顺序写(追加的形式),而数据直接落盘需要找到数据对应的位置
问题 3: redo log 储存了什么东西,持久化(崩溃恢复是怎么做的?)
- redo log会记录写(命令)数据的偏移量(数据做了什么修改)
- redo log会通过追加的形式将日志记录到磁盘
- redo log 的落盘机制(可以设置的)
- 事务提交立即落盘
- 事务提交落用户缓冲区(每隔 1s,自动落盘/占用缓冲区一半自动落盘)-只要 MySQL 服务宕机数据就会丢
- 事务提交落内核缓存(由内核缓存决定落盘时机)-只要操作系统正常数据就不会丢
- 记录 redo log 比数据落盘更快,因为日志是顺序写(追加的形式),而数据直接落盘需要找到数据对应的位置
问题 4 : MySQL 的 bing log (二进制日志)已经有全量的数据,为什么还要使用 redo 做崩溃恢复,为什么不直接使用 bin log做崩溃恢复.
-
bin log 是一个比较重的服务,因为是储存全量的数据,文件比较大,一个文件写满了会创建新的文件继续写(无限空间),并且 bin log是不会默认开启的,而是手动的开启(需要数据备份,或者配置主从才会使用)
-
redo log 是一个比较轻的服务,专门做数据的崩溃恢复的,他只记录近期的数据(没有持久化到磁盘的数据),采用的循环写的策略,如果 redo log 写满了,会循环覆盖之前的内容,所以消耗的空间是有限的
-
bin log当然可以做数据恢复,因为也是追加的数据修改的日志(可以做全量数据恢复的东西自然可以做数据崩溃的恢复)
-
没有必要只为了数据恢复而强制开启一个比较重的服务(bin log),有点拿大炮打苍蝇的感觉
问题 5 :redo log与 bin log 如何保证数据的一致性?
为什么要一致性?
- bin log是配置主从的,从数据库复制的 bin log
- 如果数据不一致,那么数据主从数据库的数据就会不一致
redo log 是事务开始的时候就会记录(用于事务途中的崩溃回滚)
bin log 是事务提交时记录的,事务提交数据更新生效,同步从节点数据
redo log 双写:
-
写的命令执行前:记录 redo log ,登记为待提交状态
- 这时候崩溃,就会回滚,不生效
-
事务提交:记录 bin log,再将 redo log 登记为提交状态
- binlog 记录成功后将 redo log 登记为生效状态,避免 redo log 与 bin log 数据不一致导致的主从数据不一致问题
(问: 什么先记录 redo log,崩溃恢复 redo log 再刷的时候 也可以重新记录 没记录的 bin log? 搞不懂)
MySQL 的 MVCC 多版本并发控制
事务的read view(事务能感应到的数据有哪些?)
read view
- 事务号(id)
- 当前活跃事务列表(ids) : 表示已经开始但是没有提交的事务
- 事务开始时列表中最小的事务号min: 事务开始时最小的活跃事务 id
- 事务开始时列表中最大的事务号+1(max): 事务开始时最大的活跃事务 id+1
哪些数据版本是当前事务可见的?
提交可见原则:
- 小于(min)的数据版本是可见的:小于min 说明事务开启前,该版本的数据已经提交生效了
- 小于 max 但是不再 ids(活跃列表中的)是可见的:
- 小于 max 表示事务开启前,改版本的事务已经开启(这是前提,因为>= max表示事务开启时,该数据的事务并没有开启,肯定是不可见的),
- 不在 ids 中的事务:不在 ids 中表示事务已经提交,证明数据可见
MySQL 事务就根据数据的版本链找到自己可以感应到的数据的版本进行读取,实现多版本并发控制
事务的隔离级别
读未提交
读取版本链中最新的版本的数据(不判断是否可见)
脏读问题
- 事务 b 修改一条数据(未提交),事务 a 读取这条数据,这时 b 事务回滚,事务 a 再一次读取这个数据
- 这时事务 a 中两次读取的数据的结果是不一样的,这就是脏读
读提交
通过 read view 读取自己能感应到的最新版本的数据
- 可以避免脏读的问题
不可重复读问题
- 事务 a 读取一条数据,之后事务 b 修改这条数据(提交),事务 a 再一次读取数据,
- 这时事务 a 中两次读取的数据的结果是不一样的,这就是不可重复读
(思考: 事务在查询的时候已经持有该条数据的资源(共享锁),其他事务就无法操作该数据(加排他锁),还会有不可重复读的问题吗?
幻读问题
- 事务 a 读取m 到 n 区间的数据, 之后事务 b 添加一条 m 到 n 区间的数据 ,事务 a 再一次读取m 到 n 区间的数据数据,
- 这时事务 a 中两次读取的数据的结果(条数)是不一样的,这就是幻读.
可重复读(默认级别)
-
也是通过 read view 读取自己可以感应到的数据的版本,
-
但是不同的是,可重复读的活跃事务列表(ids)是一个拷贝,不会变化,所以只能读取到事务开始时数据的样子,中间就算有提交的更改也是感应不到的
-
可以避免不可重复读与幻读(一定程度上)问题
串行化
- 前一个事务执行完成,下一个事务才能执行
- 就类似单线程依次的执行,一般很少使用,性能太低
问题 6 : 可重复读可以完全避免幻读的问题吗?
不可以,有两种情况可重复读还是会出现幻读
- 事务 A(读区间,更改区间,再读区间);事务 B(在事务 A期间,更改前插入或者删除了某条数据),会出现幻读的情况
- 事务A先用读快照(普通 select), 事务 B 插入一条数据,再使用读当前(select…for update)就会出现幻读
可以理解update 是要使用最新的数据进行 update,就会去发现最新的数据,可见范围就变了,就出现了幻读.
问题 7 : 你们公司(之前的业务)使用的什么隔离级别?
- 很多时候不会在意,重复读,幻读的问题(很少有人会再同一个事务中做两次相同的查询/范围)
- 所以一般情况下:读提交的隔离级别就已经够用了
- 所以很多团队会选择读提交的隔离级别,而不是默认的可重复读
- 为什么?
- 隔离级别越高性能越低,读提交的性能要稍微好一点.
参考
https://blog.csdn.net/dengjiayue/article/details/144537660?fromshare=blogdetail&sharetype=blogdetail&sharerId=144537660&sharerefer=PC&sharesource=dengjiayue&sharefrom=from_link