原文:zhuanlan.zhihu.com/p/55872397
做者:柳树html
上一讲最后抛出了一个问题,Mysql可重复读的“快照”究竟是啥?mysql
是对当前数据的全量拷贝吗?每开启一个事务,都要把当前数据库的数据拷贝一份出来?sql
很明显不是。数据库
一方面,这样作太消耗内存了,另外一方面,这样会很慢。数组
那么Mysql是如何实现“快照”的呢?session
咱们仍是用上一讲的例子:并发
咱们已经知道,Session A在第二次select时,查询到的结果和第一次select时同样,也就是说,Session B的update,对Session A来讲,不可见,Mysql是如何作到的呢?3d
很简单,也很绝妙 —— 数据版本,也就是咱们常说的MVCC,多版本并发控制。cdn
下面讲具体实现。htm
Innodb里面,每行数据,均可以有多个版本,每一个版本都有一个字段trx_id,记录生成这个版本的事务的ID。
假设一开始,id=1这行数据,只有一个版本,trx_id是90,意味着生成这个版本的事务ID是90:
这时候Session A开始了,从上一讲,咱们已经知道,begin时并不会生成快照,快照在第一次select时才会生成,那么第一次select时,session A都作了什么呢?
session A只须要作一件事:用一个数组,来记录当前活跃的事务ID。
假设session A的事务ID是97,当前还有另外两个事务,事务ID是9四、96,因此session A会生成一个[94,96,97]的数组。
这个数组有什么用?后面你就知道了。
接着,session B执行了update语句,来更新id=1这一行数据,给这一行数据生成一个新的版本,假设session B的事务ID是98,所以这行数据就有了两个版本:
这时候,session A又来select了,当前版本是session B生成的,那session A是如何找到以前的版本的呢?
这时候,session A一开始生成的事务数组就派上用场了,session A的事务数组是[94,96,97],最小事务ID是94,最大事务ID是97,因此,当它遇到一行数据时,会先判断这行数据的版本号X:
好,如今session A开始遍历id=1这行数据的全部版本:
当前版本是98,大于97,因此不可见,继续看上一个版本;
再往上,版本是90,小于94,可见,就它了,因此session A select出来的id=1的数据,c的值是1。
固然,这样的人肉判断实在太麻烦了,在《Mysql实战45讲》里,丁奇给出了这样一个**“等价判断”可见性的原则**:
这其实就是可重复读的想要实现的效果。
最后再给一个复杂点的例子,你们运用上面的原则,来预测sql语句的查询结果:
小结一下:
问题又来了,这些不一样版本的数据,是物理存在于内存或者磁盘中的吗?