MVCC
Mysql中MVCC是在Innodb存储引擎中得到支持的,Innodb为每行记录都实现了三个隐藏字段:
DB_TRX_ID: 6字节的事务ID, 用来标识该行所述的事务。
DB_ROLL_PTR: 7字节的回滚指针。
DB_ROW_ID:隐藏的ID,随着数据的增加而增加。
redo log
redo log就是保存执行的SQL语句到一个指定的Log文件,当Mysql执行recovery时重新执行redo log记录的SQL操作即可。当执行每条SQL(更新语句)时,redo log会被首先写入redo buffer,redo buffe是一个环形缓冲;当执行COMMIT命令时,redo buffer中的内容会被视情况刷新到磁盘redo log。redo log在磁盘上作为一个独立的文件存在,即Innodb的log文件。
每次操作都先记录到redo日志中,当出现实例故障(像断电),导致数据未能更新到数据文件,则数据库重启时须redo,重新把数据更新到数据文件。因为每次commit时,将数据的修改立即写到redo log中,但是并不一定同时将该数据的修改写到数据文件中。因为该数据已经提交,但是只存在联机日志文件中,所以在恢复时需要将数据从联机日志文件中找出来,重新应用一下,使已经更改数据在数据文件中也改过来。
undo log
记录更改前的一份copy,当系统rollback时,把这份copy重新覆盖到原来的数据。与redo log相反,undo log是为回滚而用,具体内容就是copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘(如果采取日志立即刷入磁盘,那么数据的一致性得到保证,若不是则也会存在丢数据风险)。undo buffer与redo buffer一样,也是环形缓冲,当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘;与redo log不同的是,磁盘上不存在单独的undo log文件,所有的undo log均存放在主ibd数据文件中(表空间)。
undo log 分为insert undo log 和 update undo log ,Insert undo logs只会在事务回滚的时候使用,事务一旦提交,就可以丢弃相应的undo logs。但是update undo logs还会在一致读操作中使用,只有当系统中所有现存的事务使用的快照版本不需要依赖update undo logs来构建数据行的历史版本时,才会进行物理删除。
当使用delete操作时,并不会立即执行行物理删除。只有当该delete操作对应的update undo log可以被丢弃时,才会物理删除这一行。这个删除操作被称为purge。
segment
回滚段这个概念来自Oracle的事物模型,在Innodb中,undo log被划分为多个段,具体某行的undo log就保存在某个段中,称为回滚段。可以认为undo log和回滚段是同一意思。
update的事务过程
1. 初始数据行
2:事务1更改该行的各字段的值
此时进行了如下操作:
- 用排他锁锁定该行
- 记录redo log
- 把该行修改前的值Copy到undo log,即上图中下面的行
- 修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行
3:另一个事务修改该行的值
与事务1相同,此时undo log中有两行记录,并且通过回滚指针连在一起。
因此,如果undo log一直不删除,则会通过当前记录的回滚指针回溯到该行创建时的初始内容,所幸的是在Innodb中存在purge线程,它会查询那些比现在最老的活动事务还早的undo log,并删除它们,从而保证undo log文件不至于无限增长。
Insert 事务
当insert时,原始的数据并不存在,所以回滚时把insert undo log丢弃即可,而update undo log则必须遵守上述过程。
MVCC优点
与悲观锁相比,避免了大粒度和长时间的锁定,能更好地适应对读的响应速度和并发性要求高的场景。
MVCC缺点
Innodb的实现真算不上MVCC,因为并没有实现核心的多版本共存,undo log中的内容只是串行化的结果,记录了多个事务的过程,不属于多版本共存。但理想的MVCC是难以实现的,当事务仅修改一行记录使用理想的MVCC模式是没有问题的,可以通过比较版本号进行回滚;但当事务影响到多行数据时,理想的MVCC据无能为力了。
相关链接: