6、MySQL 高可用

厨子大约 5 分钟数据库原创面试题MySQL程序厨

6. Mysql 高可用

MVCC机制

6.0 什么是MVCC

事务的几种特性是,原子性,一致性,持久性,隔离性,然后主要的特性就是一致性,其他三种都是为了实现一致性而准备的。

MVCC:多版本并发控制,这样每条记录就能够生成一个版本链,每个事务可以查看不同的版本。

img
img

图片来源:Yes的练级攻略

6.1 什么是当前读和快照读

当前读:当前读就是读和写之前,对其加锁,比如共享锁和排他锁,他们读到的是数据库的最新版本。

快照读:就是在不加锁的select 语句里,但是此时的隔离级别不能是串行化调度,因为串行化调度会退化到当前读,快照读的出现就是为了实现并发控制,其实现就是基于MVCC,快照读读到的不一定是最新状态,有可能是历史状态。

工作原理:事务在启动前会生成一个全库级别的快照,我们可能会想数据库的数据可是非常庞大的,全局数据快照是不是很夸张,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。

6.2 MVCC实现了哪些隔离级别

读提交和可重复读,读未提交的话,则是当前读,每次读到的都是最新的版本,然后串行化调度的话,则是通过加锁的方式进行读写,如果没有MVCC的话,则会在这两个隔离级别下产生冲突,产生脏读等。

如果有了版本控制的话,对于没有提交的事务,我们读的时候则可以读之前的版本,这样就不会发生读写冲突了。

另外并非是,真的存储多个版本,因为数据库的数据量太庞大了,如果每个版本都是一个新的快照,那根本存不下,其实这个是根据undolog来实现的,通过反向操作获取。

这样看起来就是多个版本了。

MVCC是如何实现的?

当某个数据被存入时,比如 id = 1, name = XXX,此时不仅会存入这两个值,还会存入,两个隐藏字段,trx_id

和 roll_pointer

所以,InnoDB 可以通过,回滚日志来复原当前操作的反向操作。

img
img

另外需要注意的是,当某个插入型事务提交之后,对应的undolog则会被回收,因为没有人会再访问之前的数据了

当某个事务执行更新操作时,则会有下面这种情况

img
img

图片来源:Yes的练级攻略

另一个事务也执行更新操作时则会这样存储回滚日志

img
img

图片来源:Yes的练级攻略

没错,修改型事务提交之后,不会被立刻删除,而是会追加,我们则可以根据undolog 访问之前的版本。好啦,版本的事到这我们就理解了,然后我们说一个Readview,这个就是用来判断哪个版本对哪个用户可见,哪个不可见的。

读已提交隔离级别(不可重复读)

这里有四个规则

  • creator_trx_id,当前事务ID。

  • m_ids,生成 readView 时还活跃的事务ID集合,也就是已经启动但是还未提交的事务ID列表。

  • min_trx_id,当前活跃ID之中的最小值。(最早开启的一个id)

  • max_trx_id,生成 readView 时 InnoDB 将分配给下一个事务的 ID 的值(事务 ID 是递增分配的,越后面申请的事务ID越大)

对于可见版本的判断是从最新版本开始沿着版本链逐渐寻找老的版本,如果遇到符合条件的版本就返回。

判断条件如下:

  • 如果当前数据版本的 trx_id ==  creator_trx_id 说明修改这条数据的事务就是当前事务,所以可见。

  • 如果当前数据版本的 trx_id < min_trx_id,说明修改这条数据的事务在当前事务生成 readView 的时候已提交,所以可见。

  • 如果当前数据版本的 trx_id 在 m_ids 中,说明修改这条数据的事务此时还未提交,所以不可见。

  • 如果当前数据版本的 trx_id >= max_trx_id,说明修改这条数据的事务在当前事务生成 readView 的时候还未启动,所以不可见(结合事务ID递增来看)。

可重复读

现在的隔离级别是可重复读。

可重复读和读已提交的 MVCC 判断版本的过程是一模一样的,唯一的差别在生成 readView 上。

上面的读已提交每次查询都会重新生成一个新的 readView ,而可重复读在第一次生成  readView 之后的所有查询都共用同一个 readView 。

也就是说可重复读只会在第一次 select 时候生成一个 readView ,所以一个事务里面不论有几次 select ,其实看到的都是同一个 readView 。

套用上面的情况,差别就在第二次执行select name where id 1,不会生成新的 readView,而是用之前的 readView,所以第二次查询时:

  • m_ids 还是为 [5,6],虽说事务 5 此时已经提交了,但是这个readView是在事务5提交之前生成的,所以当前还是认为这两个事务都未提交,为活跃的。

  • 此时 min_trx_id,为 5。

(对于判断过程有点卡顿的同学可以再拉上去看看,判断版本的过程和读已提交一致)。

所以在可重复级别下,两次查询得到的 name 都为 XX,所以叫可重复读。

推荐阅读:https://zhuanlan.zhihu.com/p/383842414open in new window

这篇文章非常牛批,强烈推荐