MVCC
MVCC,多版本并发控制,主要是为了解决读写冲突不加锁。这里所指的读是快照读。
快照读和当前读就不解释了。
MVCC是一种较为抽象的概念。其意义是为了维持一个数据的多个版本。这样可以在不加锁的情况下,实现读写不冲突。
MVCC实现了事务的隔离级别。在mysql中MVCC的实现使用到了三个隐式字段、read view、undo日志
三个隐式字段
##### DB_TRX_ID 最后一次修改或者插入的事务id,大小为6B
DB_ROLL_PTR 回滚指针,指向上一次写入undo_log的最新数据,如果该行数据没有被更新过,则为null,大小为7B
DB_ROW_ID 如果没有指定主键,则InnoDB会根据该字段生成一个聚簇索引。大小为7B
read view
read view 是读视图,在进行快照读时,会产生读视图,记录并且维护了一个全局唯一自增id,通过readview可以得到当前快照读可以读取到哪个版本的记录。
当进行快照读时,会生成read view,记录当前活跃的事务id,并且根据上述提到的id,与当前活跃的id进行比较,判断活跃的事务id是否对当前id可见。说白了,就是来做可见性判断的。
主要有两个参数: m_up_limit_id(活跃列表中的最小活跃的id)、 m_low_limit_id(目前出现过的最大的事务id的下一个,即maxID+1)
通过与这两个id的比较来判断当前事务对活跃事务的可见性。
undo_log
undo_log主要分为插入insert 和 update。
对于insert,当事务被提交之后,undo_log就会被丢弃。因为插入操作对其他事务时不可见的,为什么不可见可以参照上述可见性算法。
对于update,或者delete,产生的undo_log,可以需要提供MVCC机制,即在进行快照读的时候可能还需要,所以不会删除。
为了节省磁盘空间,对于update undo_log, 有一个purge线程专门来进行清理,而purge线程也维护了一个自己的read view,并且在进行update或delete操作时,会设置老记录的delete_bit = true, 与此同时,read view的可见性也得到满足时,那么就会安全的删除undo_log
总结
所以整个过程可以总结成如下:
当进行update或者delete操作时,首先会给这行数据加锁,然后通过undo_log将当前数据记录到日志中,记录在undo_log的最新数据的DB_ROLL_PTR指向前一条数据,所以可以看出,记录的数据会形成一个单向链表,而链表头就是最新记录的数据。然后,就会进行数据的修改,最后释放锁。
RR和RC
我们都知道RR解决了不可重复读,RC则没有解决,其实MVCC对于两者的实现逻辑是完全一样的。只是在进行快照读时,两者生成read view的时机不同。
对于RR: 在对数据进行第一次快照读的时候,就会生成一个read view, 并且记录当前活跃的事务id,第二次、第三次… 在对这条数据进行快照读时,得到的read view其实都是同一个,这也是为什么RR可以解决不可重复读的问题。
对于RC: 则每次进行快照读的时候,都会生成一个新的read view,所以每次读取的数据不一定是一样的。
1 | PS: 那么mysql中默认使用RR,就不怕幻读吗? |
1 | 最后一个问题? |