前言
如果你在面试被问到mysql事务,
什么是事务
redo和undo又是什么
事务的隔离级别哪几种,存在什么问题
mvcc是什么
mvcc如何解决幻读
可重复读级别为什么还存在幻读
当前读和快照读是什么
什么是事务?
事务的四个属性,原子性a、一致性c、隔离性i、持久性d,通过aid来保证一致性,原子性通俗来讲就是原子性的操作,要么全部成功要么全部失败,隔离性是指并发操作应当与串行操作一致,持久性是指事务一旦提交,必须落入磁盘
如何实现原子性?
带入这种场景思考
如果你在操作A sql,mysql挂了,此时B sql未执行,mysql服务器恢复,A数据
如果你在操作A sql,mysql挂了,此时B sql未执行,mysql服务器恢复,A数据如何回滚。
那么就需要维护sql的执行状态,根据状态来确定是否存入磁盘或是进行回滚
如何回滚?
那么你会想我写A sql时先给他写一个执行sql之前的状态日志,等两个操作完成再提交、如果有问题通过日志进行来撤回,哈哈哈哈,这就是通俗的讲这就是undo log,
但是但是但是但是mysql又优化了落磁盘的一个操作,加了一个东东----redo log,它是什么?
重做日志,对于我们的数据落入磁盘并非容易的事情,每一条都进行一次落磁盘会有大量的io,
这些io是随机io,非常耗时,而我们的redo log达到一定量后合并去写磁盘效率会高一些,对再来查询语句不用慌,它预写日志时会写入一个bufferpool,达到条件后刷入磁盘,在刷入磁盘期间可以命中bufferpool缓存返回。
事务何时提交何时回滚,如何控制?
这里就是依赖这两个日志。提前写入redo log的日志包含了,事务的状态,可以看做一个字段,如果提交,则会有清空undo log的操作,如果回滚,回去找到undo log的日志进行回滚。
事务的隔离级别是哪些?
读未提交有脏读、不可重读、幻读问题
读已提交有不可重复读、幻读问题
可重复读有幻读问题
串行化没有问题
因为读写操作在并发情况下会引起脏读、不可重读、幻读,首先你要代入一种思维那么就是并行进行读写时和串行进行读写时到底有哪些不一样的地方,这些不一样的地方就不该出现,我们要尝试着去让他在并行情况下和串行情况趋于一致,减少锁的使用到达到并行、表述如下
当A事务读到B未提交的事务时的数据则为脏读
当A事务第一次查询到数据,B事务在提交后,A事务查询到数据的列字段变了,这种两次查询的数据不一致则可以理解为不可重复读。
幻读有两种理解:A事务执行查询,在有唯一索引的情况下,明明没有数据,B事务此时插入一条数据,回到A执行插入时报唯一错误这种情况。
A事务执行查询时没有数据,B事务插入一条数据,A执行insert、update、del语句时,A再去查有数据了
mvcc是什么?
mvcc就是为了解决除了写-写并发并发时无须加锁实现语句的一种解决方案
我们的数据每一行中都有三个隐藏字段其中有隐式主键id、最近的一次事务id、和回滚id,
undolog就是回滚数据,它是以链表的形式将每一次对某一行进行的操作串起来,形成了一个历史版本的记录
有了每列隐藏的这些字段可以判断当前读到那个版本,当然这种也称为快照读,但是我们如果有多个事务,ABCD,BCD依次提交后,此时最新的事务id为D,回滚id为C,A此时再去想追溯到以前的A自己的版本该读的是可以的,但是但是,如果ABCD,有的提交有的没提交,在undo log版本链假设是A->B->C->D,B事务再查时是回到A还是A以前,这个A我们不能知道它是否提交,就需要引入一个readview将活跃的事务id维护进去,我们就知道是否提交,根据隔离级别的不同,走不同的逻辑去看到自己应该看到的版本数据。
但是对于幻读,我们是无法通过mvcc解决的,主要是因为你虽然可以只看自己的版本数据,但是幻读是因为插入、更新、删除而导致使用到的当前读、即将别的事务已提交的事务可以查到,为什么这些操作要使用当前读,因为不使用当前读,一个简单的例子A事务修改某条数据,B事务已经将该数据删除提交且是在A事务执行中,A如果修改自己的一个数据再提交,逻辑上是有问题的,因为别的事务已经将该数据删除,所以必须进行当前读,如果当前读触发,必然会查到别的事务的数据。而innodb引擎是如何解决幻读,在A进行查询时能会上一把临间锁next-key lock,其他事务要修改得先获取锁。
当前读和快照读是什么?
当前读读的是最新的一个版本数据
快照读是mvcc根据readview计算得到的版本数据
next-key lock是什么?
next-key lock是行锁加间隙锁的组合
唯一索引情况下成行锁
普通索引情况下,间隙锁加的是列值得一个左开右闭的一个区间,如果是右边扫描的列值与where不相等,会造成一个右开的情况
在select情况下,for update和lock in share model是用户手动进行加锁
两者的区别是for update会锁主键索引,且是读写排他
lock in share model会走一个索引覆盖的锁定,称为共享锁(允许快照读,允许用户再拿for update锁),如果sql进行索引覆盖,则不会锁定
主键索引,使用不当时会有死锁情况,对于同一段索引数据,A加共享锁,B加for update锁,进行增删改阻塞,此时A再增删改也阻塞
仅供参考如有不当,欢迎指正!!!