Chinaunix首页 | 论坛 | 博客
  • 博客访问: 645843
  • 博文数量: 66
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 2204
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-26 21:43
个人简介

曾就职于阿里巴巴担任Oracle DBA,MySQL DBA,目前在新美大担任SRE。[是普罗米修斯还是一块石头,你自己选择!] 欢迎关注微信公众号 “自己的设计师”,不定期有原创运维文章推送。

文章分类

全部博文(66)

文章存档

2017年(2)

2016年(3)

2015年(7)

2014年(12)

2013年(42)

分类: Mysql/postgreSQL

2014-11-05 22:21:05



    InnoDB 是一个平衡高可用和高性能的多功能存储引擎。innodb存储引擎和其他关系型数据库一样,支持事物和完整的ACID特性。需要强调的是,InnoDB的持久性是通过Redo Log来保证的。
这篇文章将提供Redo Log或者说是InnoDB日志子系统的概览。我们将探讨如下的细节:

  1.全局的日志系统对象,提供了访问InnoDB重要的数据结构和信息
  2.mini-transaction  在Redo Log记录创建的时候产生
  3.全局的In-memory log buffer存放从mini-transaction buffer copy过来的redo log.此log buffer将会周期性的刷到磁盘上的log file中去
  4.磁盘上的redo log文件和相关的内部结构
  5.我们将会讨论LSN的概念,以及不同的LSN的值来实现write-ahead logging。

Redo Log 的产生
   在文章Data Organization in InnoDB中,我们已经看到了InnoDB用户的数据文件是由一系列相等的page组成,每一个page在InnoDB系统内部都由space_id,page_no 的组合来唯一标识。如果你想读取或者修改任何的page,你必须将这个page load进内存。所以,这里就有了两份page的copy--磁盘上的和内存里的。这里从比较高的层面来描述redo log产生的步骤:

  1.任何对page的修改首先在内存中完成。这些在内存中修改但是没有flush到磁盘的pages被标记位diry pages。
  2.任何与此修改的redo log在内存中产生,存放于本地的mini transaction buffer中,之后将被copy 到内存中全局的redo log buffer中。
  3.redo log记录从redo log buffer中随后被flush到磁盘上的redo log file上。redo log record写内存和flush disk是两个独立的步骤。这里同样也要考虑OS层面的file buffering。
  4.之后的checkpoint操作将会将内存中的dirty pages flush到磁盘

以上的步骤的顺序非常重要,redo log记录的修改必须在dirty page之前flush到disk。这个就是write-ahead logging (WAL)的概念。

产生的redo log记录,包含必要修改信息,以便在数据库恢复期间能够重现同样的修改操作 。所以redo log不仅包含原始的page集合的相关信息,而且还包含对page修改操作。由于redo log记录的是dirty pages的改变信息,所以在恢复期间我们就可以使用redo log来重建重建这些drity pages。


Redo Log 文件
默认情况下,Innodb 在MySQL的数据文件中生成两个日志文件ib_logfile0和ib_logfile1.在MySQL5.6.8及以上的版本中,每个redo log文件的默认大小是 48M。这些可以在服务器选项innodb_log_file_size来进行配置。log 文件的数量可以通过服务器参数innodb_log_files_in_group来配置。

一个日志文件组包含多个日志文件,每个日志文件有相同的大小。在MySQL5.6中,InnoDB只支持每个组一个log file,所以这里不做过多的讨论。
redo log通过循环的方式来使用。每个日志文件从头写到尾,然后写入下一个日志文件,只到写到最后一个日志文件。当最后一个日志文件写完之后,redo log将再从第一个日志文件开始写入。

日志文件被看做是由一系列的"log blocks"组成,每个block的大小由OS_FILE_LOG_BLOCK_SIZE来控制,默认是512 bytes。每一个日志文件包含一个Log Header,大小由LOG_FILE_HDR_SIZE来定义,为 4*OS_FILE_LOG_BLOCK_SIZE.

Redo Log文件头
每个日志文件头占据的四个log blocks包含如下信息
1.开头的四个字节包含的是该log file所属的Log Group Number。
2.接下来的8个byte包含的该日志文件开始的LSN
3.第一个checkpoint域位于第二个log block的开始部分
4.第二个checkpoint域位于第四个log block开始的部分

上面提到的checkpoint域,包含标识一个checkpoint的必要信息--一个checkpoint号,checkpoint对应的LSN,checksum信息等等。


Log Blocks
每一个日志文件可以被看做是一系列的log blocks。所有的log blocks,除了属于log file header的部分,都包含一个block header和一个block footer。
每个log block header有LOG_BLOCK_HDR_SIZE定义其大小(定义为12 bytes)。 Log Block的尾部大小由LOG_BLOCK_TRL_SIZE来定义(4个bytes).
log block header 包含如下信息:
log block number :4 bytes
写入到该block的log大小(bytes):2 bytes
第一个mtr log group的起始偏移量,如果没有为0
该log block所属的checkpoint


Log Block的尾部包含该log block内容的checksum

 

日主序列号(LSN)
日志序列号是一个非常重要的概念。LSN是redo log file的偏移量。在InnoDB内部,日志序列号是通过类型lsn_t来表示,是一个8 byte的无符号整形。
这里有列出一些我们感兴趣的LSN值。下面的列出了本文将要讨论的LSN的值。(标注:log_sys是一个日志系统的全局对象,下一部分将会提到)

LSN                                                                   描述
log_sys->lsn                                将要产生的redo log record的下一个lsn
log_sys->flushed_to_disk_lsn        flush到磁盘的LSN. 所有那些redo log记录的LSN  < flushed_to_disk_lsn 都被认为已经安全的flush到disk上.
log_sys->write_lsn                      当前正在运行的写操作的最大的LSN
log_sys->current_flush_lsn          当前正在写入并且flush的最大的LSN


每个LSN都对应一个dirty page,redo log record和redo log file。每个redo log record被copy到内存中的log buffer中时,都会获得一个相应的 LSN。当每个database的page被修改的时候,redo log record就产生了。所以每个database的page也关联一个LSN。这个关联的LSN保存在每个page的header部分。这个page的LSN告诉InnoDB,在flush这写database的page之前,哪些redo log file需要被先flush到disk。
全局的日志系统对象
全局的日志系统对象log_sys(log_t类型)记录了InnoDB日志子系统重要的信息。这里我们只讨论关于redo log缓冲区和redo log文件的部分。全局的log_sys 标识了log buffer中"活跃区域“部分,这部分区域包含那些需要写入磁盘但还未写入的部分。它同样标识出这个区域在log file中的位置,即在log buffer中的那些"活跃区域"需要写入/flush到redo log filed的位置。



 


1.成员log_sys->buf指向的是内存中redo log buffer的位置。mtr_commit执行时会将redo log records写入这个buffer中。该buffer的大小由log_sys->buf_size(单位bytes)来控制。
2.成员log_sys->buf_free是内存中下一个redo log record将要写入的位置偏移量。也是下一个将要flush到磁盘的redo log的结束偏移量。
3.成员log_sys->buf_next_to_write指向还没有被flush到disk的redo log的起始偏移量。当redo log下一次flush 到磁盘的时候,它将会从这个地方开始 flush。即下一个即将要flush 到disk的redo log的起始偏移量。
4.成员log_sys->flushed_to_disk_lsn标识的是在该lsn之前的redo log已经全部flush到disk.所以在此lsn之前,redo log是安全的存在与disk上.这个成员记录的值总是小于或等于log_sys->write_lsn和 log_sys->lsn。
5.成员log_sys->lsn代表的是当前的lsn,该成员在每次调用mtr_commit()都会被更新.函数mtr_commit()写入一组由mini transaction产生的一组redo log records到全局的/系统范围的redo log buffer中。这个值通常大于或者等于log_sys->flush_to_disk_lsn 和 log_sys->write_lsn.这个LSN是redo log record在log_sys->buf_free中写入record的位置。
6.成员log_sys->write_lsn是当前正在执行的写操作在redo log buffer中的结束lsn。且满足(log_sys->flushed_to_disk_lsn<=log_sys->write_lsn<=log_sys->lsn).
7.成员log_sys->current_flush_lsn代表的是当前执行写+flush操作的结束的lsn。且大部分情况下等于log_sys->write_lsn。

全局的log_sys对象指向了内存中redo log buffer和disk上redo log文件中的不同位置。下图展示了全局变量log_sys对象所指向的位置。这张图清晰的
描述了redo log buffer与redo log file的映射关系。

redo log buffer和redo log file之间的关联关系

 






全局的内存redo log buffer
redo log buffer在内存中是全局的,用户产生的redo record 将会被写入到该buffer。该缓存的大写是可配置的,而且有变量innodb_log_buffer_size控制。该redo log buffer默认的大小是8MB。

当一个事物正在运行而且修改了database内容,它将会在redo log buffer产生redo logs。该redo log buffer将会写入/flush到log file,该过程发生在事物提交或者log buffer 满了的时候。

当redo log buffer满了,已经没有空间提供给mtr_commit来存放一组redo log records。这时会调用log_buffer_flush_to_disk来同步flush该log buffer到log file中。
该函数将从log buffer中将log_sys->buf_next_to_write和log_sys->buf_free的数据flush到disk。就LSN而言(针对于logfile),函数log_buffer_flush_to_disk
flush redo buffer中的日志写到log file文件中log_sys->flushed_to_disk_lsn到log_sys->lsn的位置。

微事物(mini-transaction) MTR
mini-transaction用于产生所有的redo log records。一个mini-transaction包含一个本地的buffer(被称作mini transaction buffer)用于存储产生的redo log records。如果我们需要产生一组redo log records,比如将一组中所有的redo log records全部写入log file或者全部不写入,这个时候我们将这些log records放到一个mini transaction中(通过事物的原子性来保证这一组redo log records的全部写成功或者全部失败)。mini transaction除了redo log records,还维护一个dirty pages的list。

mini transaction的使用过程如下:
1.创建一个mtr_t类型的事物对象
2.通过mtr_start()开始一个mini transaction。这个函数将会初始化mini transaction buffer.
3.利用mlog_write_ulint() 函数簇来产生redo log records。
4.通过mtr_commit()函数来提交mini transaction 。这个函数将会将redo log recods从 mini transaction buffer copy到全局的redo log buffer。将该mini transaction 维护的dirty page加入到buffer pool 中的flush list。


这里给出mini transaction的定义供你参考。成员mtr_t::log包含了mini transaction buffer,该buffer持有redo log records。而成员mtr_t::memo包含一个mini transaction 中dirty pages的list。

 


redo log 记录的类型
当我们更改一个数据库page的时候,一个redo log就产生了。redo log record包含了该page发生的改变信息(physical redo log)或者怎样再次重演这次改变(logical redo log).InnoDB使用physical redo log和logical redo log的结合来生成redo log。


为了理解上述提到的physical redo log+logical redo log,我们考虑到如下一个操作:page重组。如果我们为这个操作产生了一个physical redo log record,因此这个产生的redo log record的大小将和page 的大小相等。但是因为InnoDB使用logical redo log记录这次操作,因此将极大的减少redo log record的大小。在这个例子中,因为logical redo log record表示了这个page的重组,我们所需要的所有信息就是唯一标识这个page,同时和这个page发生的操作类型--page重组。

所有每一个redo log record都有一个类型。这个redo log的类型帮助我们,在做recovery的时候,选择特定的函数去apply或者执行redo log。redo log record的内容必须包含这个函数执行所需要的所有的属性和参数。

redo log record的生命周期

redo log record的生命周期如下:
1.redo log record 首先被mini transaction创建并存储在mini transaction buffer中,它包含在database recovery期间重做该操作所必要的信息。
2.当mtr_commit完成之后,该redo log record就从mini transaction buffer中传送到内存中的redo log buffer中。如果需要,该redo log buffer会flush 到redo log file,为新的redo log record腾出来位置。
3.每个redo log record都有一个与之相关的LSN。该关联关系是在mtr_commit函数期间产生,当redo log record被从mtr 传送到 redo log buffer中。一旦该redo log record的lsn被建立,这个redo log record在log file中的位置也就关联起来了。
4.该redo log record之后,在written + flushed执行的时候,从log buffer传送到redo log file。这意味着这些redo log record现在持久化在disk上。
5.每个redo log record都有一个dirty pages的列表,通过LSN来建立联系。在与之关联的dirty pages 被flush到disk之前,这个redo log record必须先被flush 到disk上。该redo log只有在与之相关的所有dirty pages被flush到disk上之后,才能被丢弃掉。
6.在数据库recovery期间,这个redo log record可以用来重建与之相关的dirty pages list。

总结
这篇文章提供了一个InnoDB 存储引擎的redo log子系统的概览。redo log子系统中使用的主要的数据结构包括mini transactio(mtr),内存中的redo buffer
和磁盘上的log files。InnoDB跟踪多个LSN值,从而确保write-ahead(WAL)能够正确的发生。

对于RDBMS来说,数据丢失是不可接受的,redo log对于成功的RDBMS来说是至关重要的。而且,由于redo logs随着DML操作的同步产生,所以如何保证高效也
非常重要。redo logs(修改的数据越小,redo log records也就越小)的大小保持的越小越好。




翻译:https://blogs.oracle.com/mysqlinnodb/entry/redo_logging_in_innodb



阅读(3200) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~