小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。 面试的逻辑思路:
事务是我们经常使用的,事务的由来就是遵循四大基本特性.但是面对高并发的时候的并发事务往往无法满足ACID,所以导致了一系列并发问题,为了解决这些并发问题,所以mysql提出了四种隔离级别供使用者进行自己的取舍.但是往往有人既想要熊掌又想要鱼,所以提出了MVCC的概念
线路:
事务四大特性
-
原子性:事务是最小的单位,不允许进行分割,即事务所执行的操作要么都完成要么都失败
-
一致性:这个一致性其实很多概念解释的都很模糊,比如
事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
其实这些说法也对,但是没有说到点子上去
就拿上面转账来说,事务的一致性其实就是多个事务看这个总钱数,无论是这个事务之前前还是执行后,都是一样的.也就是别的事务不会看到这个事务执行期间的中间状态(中间状态从A扣钱和给B加钱肯定是有个顺序的,这个时候钱的总数肯定的少了或者多了的)
那么就有的人会问了,那么这个概念不是和原子性一致了嘛
原子性关注于状态,要么都成功要么都失败
而一致性关注于数据,保证事务执行期间的这个数据不会超脱于业务之上
-
隔离性:多个事务执行期间,对同一条数据不会产生影响
-
持久性:事务执行完成之后,对数据的修改会保存在数据库中,不会再有回滚的操作出现
对应四大特性的实现方式
原子性:使用undo log实现,原理就是
1. 在操作任何数据之前,都先将数据备份到另一个地方(这个地方称之为undo log)
2. 然后就行数据修改
3. 如果出现异常或者执行了rollback操作,系统就使用undo log中的数据将数据恢复到事物执行之前的状态
持久性:使用的redo log实现的,原理就是
1. 操作任何数据的时候,都将操作之后的数据进行备份到一个地方(这个地方就叫做redo log)
2. 事务提交之前将redo log持久化
3. 如果 系统出现崩溃则可以直接使用redo log恢复数据
隔离性:通过加锁和MVCC去实现的
一致性一般由代码层面来保证
事务并发的问题
- 脏读(Dirty read): (读取了后来回滚的操作)事务A读取了事务B未提交的数据修改,事务B又回滚了(读了不该读的数据)
- 幻读(Phantom read): (我以为我读取了全部)事务A读取了全部学生进行操作,结果事务B又加了学生(幻想自己读了所有)
- 不可重复读(Unrepeatableread):(我读取的数据被修改了)事务A读取的数据被事务B修改并提交了(同一个事务读的两次结果不一样了)与幻读的区别在与幻读针对新增和删除.不可重复度在于修改
理解起来很好理解,就是容易记混,哈哈哈
隔离级别
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生。所有的事务都是一条一条执行的.MVCC的概念与此刚好对立
- Repeatable read (可重复读):可避免脏读、不可重复读的发生。在读已提交的基础上.在事务执行内部多次的获取数据都是一致的.
- Read committed (读已提交):可避免脏读的发生。事务提交之后才能被别的事务看到
- Read uncommitted (读未提交):最低级别,任何情况都无法保证。事务还没提交就被别的事务看到
在启动参数中设置transaction_isolation即可配置事务隔离级别,查看当前隔离级别 show variables like 'transaction_isolation'默认隔离级别是“可重复读”
MVCC
自己关于MVCC的理解不深,后期会重新补充的
> 概念
首先需要理解几个概念
mvcc:多版本并发控制
当前读:会对读取的信息进行加锁的操作
+ 加锁的select操作,比如后面加上关键字`for update`
+ update、insert、delete操作等
快照读:不加锁的一种非阻塞读操作,
+ `不加锁`的select操作
事务的四种隔离级别中
+ 读已提交
+ 可重复读
都是基于MVCC实现的
串行化,因为是基于一步一步操作执行的,这个时候就必须是用到了锁来限制.所以这个串行化是使用当前读实现的
> MVCC的结构
+ 版本链
+ undo log
+ readview:是去判断版本链中select有效的哪个版本
在每执行一步快照读操作的时候,执行的字段中包含上面描述的`事务ID`,和`版本回滚指针`
readView的工作模式
readview中有四个字段描述
+ m_ids:生成该条readview时当前系统中活跃的读写事务的`事务id`列表.活跃的事务id指的是未commit的事务id
+ min_trx_id:readview中最小的`事务id`
+ max_trx_id:readview时系统分配给下一个事务的id值
+ creator_trx_id:表示生成该条readview的事务id
> 为什么需要使用到MVCC
一般需要并发操作有三个场景
+ 读-读:不涉及数据变更,是不会有并发冲突的
+ 读-写:有可能会出现`脏读、重复读、幻读`
+ 写-写:有可能出现更新失败的情况
mvcc就是为了解决第二种`读-写`操作会需要频繁使用锁机制来保证并发冲突情况的另一种解决办法
以此防止使用锁机制造成效率的下降
> 怎样使用MVCC
1. 借助undo log实现.每条undo log中多两个字段,表示出来`事务id`和`版本回滚指针`
2. 使用readview定位到select操作应该执行的版本信息
# 相关问题
1. 当前读和快照读在RR下的区别
当前读并不需要进行一个串性的操作,而是各个语句的执行相互嵌套
快照读需要串性的锁来管控
2. #### RC,RR级别下的InnoDB快照读有什么不同?
正是Read View生成时机的不同,从而造成RC,RR级别下快照读的结果的不同
在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;
而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。