MYSQL的redolog 、binlog、undolog以及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的数据刷新到磁盘:

  1. log buffer写了50%的数据
  2. 事务提交
  3. 正常关闭服务器
  4. checkpoint
  5. 后台线程,大约一秒一次将buffer中的数据刷新到磁盘。

两阶段提交:

写入redolog分为两个阶段:prepare和commit,这是为了使redolog和binlog保持一致。

  1. 更新buffer pool
  2. 写入redolog,此时redolog置为prepare状态。
  3. 写入binlog
  4. 把redolog置为commit状态

这样一来如果redolog为prepare状态,那么检查binlog有没有对应的日志,有的话把状态改为commit,否则判定日志失效。

Redolog和binlog比较

扫描二维码关注公众号,回复: 15023067 查看本文章
  1. Redolog是innodb实现的。Binlog是server层实现的。
  2. Redolog循环写。Binlog追加写。
  3. Redolog适合数据恢复。Binlog适合主从复制和数据恢复。
  4. Redolog是物理日志。Binlog是逻辑日志。

undolog

Undolog和redolog一样是innodb特有的,不过是逻辑日志。每次记录进行一次改动,都会记录一条反操作的undo日志,每条undo日志都有一个roll_pointer,连接形成版本链用于回滚。

MVCC

前置知识:

每条记录都有两个隐藏列:

  1. Trx_id:最近修改这条记录的事务的id
  2. Roll_pointer:链接版本链,当更新记录时,会把roll_pointer写入undo日志,可以通过它找到undo日志进行版本回滚。

Readview

也即“一致性视图”,每个事务的readview是独立的(拥有自己的readview),目的就是用来判断当前事务是否可以读取该版本的记录,具体实现下面讲,它包括四个属性:

  1. M_ids:创建该Readview时,活跃的事务id集合。
  2. Min_trx_id:创建该readview时,最小的事务id,也即M_ids中的最小值。
  3. Max_trx_id:要分配给下一个事务的id。不是当前的最大事务ID,应该是当前的最大事务(不一定活跃,可能已经结束)id+1
  4. Creator_trx_id:创建该readview的事务id,也即该readview的拥有者。

当某事务T有了readview后,就可以通过以下方式来判断该版本的记录是否可以读取:

  1. Creator_trx_id = 该记录的trx_id。说明这条记录是事务T修改的,可以读取。
  2. 该记录的trx_id < min_trx_id。说明该记录已经在生成readview之前被提交了,可以读取。
  3. min_trx_id <= 该记录的trx_id < max_trx_id。这时需要在m_ids中查找,如果trx_id在m_ids中,说明修改这条记录的事务还没提交,按照读已提交和不可重复读的要求都不能读取这条记录,需要通过版本链进行版本回滚。否则说明修改这条记录的事务已经提交,可以读取。
  4. 该记录的trx_id >= max_trx_id。说明这个版本的记录是在创建readview之后才更新的,因此我们不可能去读取(这种情况只可能发生在可重复读隔离状态下,因为读已提交隔离状态下,每次读取数据都会生成readview,不会发生max_trx_id<=trx_id的情况)。

我们只需按照上面的规则,结合版本链,回滚至第一个我们可以读取的版本即可。

需要注意的是,由于读已提交和不可重复读的隔离级别要求不同。创建readview的时机也不同:

  • 读已提交:我们要保证读取的数据是别的事务已经提交的,而不是更新未提交的,所以我们要在每一次读取数据的时候都创建新的readview。

  • 可重复读:我们要保证每次读取的数据都和第一次的数据一致,因此只需要在第一次读取数据的时候创建readview即可。

readview只有在读取操作的时候创建,更新记录是直接更新,并添加undo日志、更新版本链。

猜你喜欢

转载自blog.csdn.net/hesorchen/article/details/124012764