MYSQL的redolog binlogundolog以及MVCC
Posted hesorchen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MYSQL的redolog binlogundolog以及MVCC相关的知识,希望对你有一定的参考价值。
目录
前置知识
重要概念:
逻辑日志:可以简单的理解为记录的是SQL语句
物理日志:记录的是数据的实际变更
Crash-safe:崩溃安全,数据库在遇到崩溃、断电等极端情况,可以恢复内存尚未刷新到磁盘的数据。
WAL:write-ahead logging,先写日志,再写磁盘。
Innodb要对数据的更新时,先将数据加载到内存的Buffer pool中,对Buffer pool中的数据进行更新,最后找合适的时间的刷新到磁盘。如果这时候遇到断电crash且没有日志,那么更新就会丢失(数据还没刷新到磁盘)。
Binlog
逻辑日志。主要用于主从复制和数据恢复。用于主从复制:在master端开启binlog,然后将binlog复制到slave端,在slave端重播binlog。
Redolog
物理日志。因为InnoDB中,数据是以页为单位进行交互的,有时候更新只有几个字段值,太浪费时间了。Redolog只记录了数据的更新,而且采用的是顺序IO。顺序IO比随机IO要快很多。每次当数据库crash需要恢复时,直接读取日志。Redolog占用的空间很小,每次更新,会产生若干条redo日志,记录了更新的内容。InnoDB中,为了解决磁盘速度过慢的问题,引入了Buffer Pool,redolog也同理,需要设置一个log Buffer,log Buffer被分为了若干个redolog block,log buffer默认大小16MB。redo日志首先会写入到redo buffer,当发生以下情况时,会将redolog buffer的数据刷新到磁盘:
- log buffer写了50%的数据
- 事务提交
- 正常关闭服务器
- checkpoint
- 后台线程,大约一秒一次将buffer中的数据刷新到磁盘。
两阶段提交:
写入redolog分为两个阶段:prepare和commit,这是为了使redolog和binlog保持一致。
- 更新buffer pool
- 写入redolog,此时redolog置为prepare状态。
- 写入binlog
- 把redolog置为commit状态
这样一来如果redolog为prepare状态,那么检查binlog有没有对应的日志,有的话把状态改为commit,否则判定日志失效。
Redolog和binlog比较
- Redolog是innodb实现的。Binlog是server层实现的。
- Redolog循环写。Binlog追加写。
- Redolog适合数据恢复。Binlog适合主从复制和数据恢复。
- Redolog是物理日志。Binlog是逻辑日志。
undolog
Undolog和redolog一样是innodb特有的,不过是逻辑日志。每次记录进行一次改动,都会记录一条反操作的undo日志,每条undo日志都有一个roll_pointer,连接形成版本链用于回滚。
MVCC
前置知识:
每条记录都有两个隐藏列:
- Trx_id:最近修改这条记录的事务的id
- Roll_pointer:链接版本链,当更新记录时,会把roll_pointer写入undo日志,可以通过它找到undo日志进行版本回滚。
Readview
也即“一致性视图”,每个事务的readview是独立的(拥有自己的readview),目的就是用来判断当前事务是否可以读取该版本的记录,具体实现下面讲,它包括四个属性:
- M_ids:创建该Readview时,活跃的事务id集合。
- Min_trx_id:创建该readview时,最小的事务id,也即M_ids中的最小值。
- Max_trx_id:要分配给下一个事务的id。不是当前的最大事务ID,应该是当前的最大事务(不一定活跃,可能已经结束)id+1
- Creator_trx_id:创建该readview的事务id,也即该readview的拥有者。
当某事务T有了readview后,就可以通过以下方式来判断该版本的记录是否可以读取:
- Creator_trx_id = 该记录的trx_id。说明这条记录是事务T修改的,可以读取。
- 该记录的trx_id < min_trx_id。说明该记录已经在生成readview之前被提交了,可以读取。
- min_trx_id <= 该记录的trx_id < max_trx_id。这时需要在m_ids中查找,如果trx_id在m_ids中,说明修改这条记录的事务还没提交,按照读已提交和不可重复读的要求都不能读取这条记录,需要通过版本链进行版本回滚。否则说明修改这条记录的事务已经提交,可以读取。
- 该记录的trx_id >= max_trx_id。说明这个版本的记录是在创建readview之后才更新的,因此我们不可能去读取(这种情况只可能发生在可重复读隔离状态下,因为读已提交隔离状态下,每次读取数据都会生成readview,不会发生max_trx_id<=trx_id的情况)。
我们只需按照上面的规则,结合版本链,回滚至第一个我们可以读取的版本即可。
需要注意的是,由于读已提交和不可重复读的隔离级别要求不同。创建readview的时机也不同:
-
读已提交:我们要保证读取的数据是别的事务已经提交的,而不是更新未提交的,所以我们要在每一次读取数据的时候都创建新的readview。
-
可重复读:我们要保证每次读取的数据都和第一次的数据一致,因此只需要在第一次读取数据的时候创建readview即可。
readview只有在读取操作的时候创建,更新记录是直接更新,并添加undo日志、更新版本链。
以上是关于MYSQL的redolog binlogundolog以及MVCC的主要内容,如果未能解决你的问题,请参考以下文章