Mysql innodb undo与redo (2)

CPU与Mem,Mem与Disk一级一级的速度差别,使得我们不断寻找可以提高速度
的方式;例如,页面速度的提高:使用squid、varnish、nginx cache等页面
缓存提高页面的访问速度,使用memcache等数据缓存提高应用层访问速度。
数据库怎么减少离散磁盘读写,提高数据访问速度。oracle 从i到g都在不断
优化(之间是回滚段到回滚表空间),对redo和undo日志的利用越来越高。但
mysql中事务类型innodb存储引擎的具体情况是怎样呢?
在对付用户每次有导致数据变更的请求中,Innodb引擎把数据和索引都载入到
内存中的缓冲池(buffer pool)中,如果每次修改数据和索引都需要更新到磁盘,
必定会大大增加I/O请求,而且因为每次更新的位置都是随机的,磁头需要频繁定
位导致效率低,数据暂放在内存中,也一定程度的提高了读的速度。所以Innodb
每处理完一个请求(Transaction)后只添加一条日志log,另外有一个线程负责智
能地读取日志文件并批量更新到磁盘上,实现最高效的磁盘写入。(^-^听着听着
像看到Oracle的一些影子吧,因为本人以前是弄Oracle所以现在简单mysql都联想
到oracle的架构体系,其实个人认为每个数据原理上是差不多。)
innodb既然利用Mem buffer提高相应的速度,那当然也会带来数据不一致,术语为
脏数据,Mysql称之为dirty page。发生过程:当事务(Transaction)需要修改某条
记录(row)时,InnoDB需要将该数据所在的page从disk读到buffer pool中,事务
提交后,InnoDB修改page中的记录(row)。这时buffer pool中的page就已经和disk
中的不一样了,mem中的数据称为脏数据(dirty page)。Dirty page等待flush到disk上。
知道mysql中术语的dirty page(我认为这个dirty page放在哪个缓存工具层面上
都适合)。
dirty page既然是在Buffer pool中,那么如果系统突然断电Dirty page中的数据
修改是否会丢失?答案是肯定的,buffer pool中的数据并不是永久性。
系统故障造成数据库不一致的原因有两个:
1.未完成事务对数据库的更新可能已写入数据库。
2.已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库。
在这里我们先说恢复的一般方法:
(1)正向扫描日志文件(从头到尾),找出故障发生前已经提交的事务(存在begin transaction
和commit记录),将其标识记入重做(redo)队列。同时找出故障发生时未完成的事务
(只有begin transaction,没commit),将其标识记入(undo)队列
(2)对undo队列的各事务进行撤销处理。进行undo的处理方法是,反向扫描日志文件,对每个undo
事务的更新操作执行反操作,即将日志记录中“更新前的值”写入数据库。
(3)对重做日志中的各事务进行重做操作。进行redo的处理方法是,正向扫描日志,对每个redo事务
重新执行日志文件登记操作。即将日志中“更新后的值”写入数据库。

以上三个步骤放于四海皆行。
但mysql为了防止buffer pool数据掉失,在日常的操作中也建立了redo和undo这两个日志,记录相关
的信息,redo log在每次事务commit的时候,就立刻将事务更改操作记录到redo log。所以即使buffer
pool中的dirty page在断电时丢失,InnoDB在启动时,仍然会根据redo log和undo log中的记录完成数据恢复。
具体操作是:

redo log 也不能无限制放任不断增长,dirty page什么时候flush到disk上?
   1. The redo log, which is organized as a ring buffer, is full. To free up some space we will write 
      out dirty pages in redo log order so that we can advance the trailing pointer of the redo log ring 
      buffer an make some room.
      
      redo log是一个环(ring)结构,当redo空间占满时,将会将部分dirty page flush到disk上,然后释放部分redo log。 
      
      This situation is called an Innodb_log_wait and will be registered in the status counter of the same name.
      这种情况称为Innodb_log_wait,会被记录在Mysql status 中。

   2. InnoDB requires a free page from the InnoDB buffer pool but cannot find one. Usually we can free a page in
      the buffer pool by giving up a page that is not marked dirty. When a page is not marked dirty its contents
      can be reloaded from disk at any time and so we can safely give it up in memory. But when the buffer pool 
      holds only dirty pages this is impossible and we actually have to flush dirty pages to disk before we can 
      free them up for other uses.
      当需要在Buffer pool分配一个page,但是找不到这样的一个page,因为所有的page都是被标注为dirty

      This situation is called Innodb_buffer_pool_wait_free and will be registered in a status counter of the 
      same name. InnoDB tries to avoid this situation: Whenever more than innodb_max_dirty_pages_pct percent many 
      pages are marked dirty a checkpoint is forced and dirty pges will be written.

      这种情况称为Innodb_buffer_pool_wait_free,并将会记录到Innodb_buffer_pool_wait_free Mysql的系统变量中。
      一般地,可以可以通过启动参数innodb_max_dirty_pages_pct控制这种情况,当buffer pool中的
       dirty page到达这个比例的时候,将会强制设定一个checkpoint,并把dirty page flush到disk中。


   3. InnoDB feels idle and will write out batches of 64 pages each to disk once a second.
      
      检测到系统空闲的时候,会flush,每次64 pages

      This is normal and will not be specifically registered (but will of course bump Innodb_pages_written like everything else).

以上三种情况涉及的主要两个参数为:innodb_flush_log_at_trx_commit、innodb_max_dirty_pages_pct;
可通过状态参数:Innodb_log_wait、Innodb_buffer_pool_wait_free进行查询

innodb_flush_log_at_trx_commit  
默认值1的意思是每一次事务提交或事务外的指令都需要把日志写入(flush)硬盘,这是很费时。
特别是使用电池供电缓存(Battery backed up cache)时。设成2对于很多运用,特别是从MyISAM
表转过来的是可以的,它的意思是不写入硬盘而是写入系统缓存。日志仍然会每秒flush到硬盘,
所以一般不会丢失超过1-2秒的更新。设成0会更快一点,但安全方面比较差,即使MySQL挂了也
可能会丢失事务的数据。而值2只会在整个操作系统挂了时才可能丢数据。 

innodb_max_dirty_pages_pct
his is an integer in the range from 0 to 100. The default value is 90. 
The main thread in InnoDB tries to write pages from the buffer pool so that the 
percentage of dirty (not yet written) pages will not exceed this value. 

具体映射到Mysql的图示

PS:这图也解答了网页中“Mysql 有undo relog log?” 这样的提问了

参考文章:

http://mysqldump.azundris.com/archives/78-Configuring-InnoDB-An-InnoDB-tutorial.html

当你使用UPDATE, INSERT, DELETE语句更新数据的时候,
你就改变了两个地方的数据:log bufferdata buffers
Buffers是固定长度的内存块,通常是512字节。

LOG BUFFER           DATA BUFFER
=================    ===============
= Log Record #1 =    = Page Header =
= Log Record #2 =    = Data Row    =
= Log Record #3 =    = Data Row    =
= Log Record #4 =    = Data Row    =
=================    ===============

例如:INSERT INTO JOBS VALUES(1,2,3)语句执行之后,log buffer将增加一个新的log记录,称为Log Record #5,它包含一个rowid和新记录的内容。同时,data buffer也将增加一个新行,但是,它会同时在页头标识:该页最新的log记录是Log Record #5。在这个例子中#5Log Sequence NumberLSN),它对于接下来操作的时序安排是至关重要的。
 
下面是data-change的一些细节:
1.          一个INSERT log记录仅包含一个新数据,它对于在页上重做操作是足够的了,因此被称为一个redo条目。

2.          LSN不是log记录的一个域,它是文件中的一个绝对地址的相对偏移值。

InnoDB改变了log bufferdata buffer之后,接下来就是写盘了。这就是复杂的地方。有多个线程在监控buffer的活动情况,有三种情况――overflow checkpointcommit――可以导致写盘操作。
 
Overflows情况下发生了什么?
Overflow是很少发生的情况,因为InnoDB采用pro-active措施来防止buffers被填满。但是我们还是来看看下面两种情况:
1.          如果log buffer满了,InnoDBInnoDBbuffer的末尾写log。那么情况向下面的图一样(log buffer只有四条记录的空间,现在插入第五条记录):
LOG FILE(S) BEFORE WRITING LOG RECORD #5
=================
= Log Record #1 =
= Log Record #2 =
= Log Record #3 =
= Log Record #4 =
=================

LOG FILE(S) AFTER WRITING LOG RECORD #5
=================
= Log Record #5 =
= Log Record #2 =
= Log Record #3 =
= Log Record #4 =
=================
logs不可能永远增长。即使InnoDB使用了某些压缩算法,log文件还是会由于太大而不能放到任何磁盘驱动器上。因此InnoDB采取循环写的办法,也就是说将会覆盖前面就的log记录。
2.          如果data buffer满了,InnoDB将最近使用的buffer写入到数据库中,但是不可能足够的快。这种情况下,页头的LSN就起作用了。第一,InnoDB检查它的LSN是否比log文件中最近的log记录的LSN大,只有当log赶上了data的时候,才会将数据写到磁盘。换句话说,数据页不会写盘,直到相应的log记录需要写盘的时候。这就是先写日志策略。
 
CheckPoints的时候发生了什么?
前面说过InnoDB采取了一些pro-active措施来保证不发生overflows,其中最重要的措施就是checkpointing。有一个分离的线程,或者说从一组修改buffers的线程中分离出来的一个线程。在特定的时间间隔,checkpointer将醒来,检查buffer的改变,并保证写盘操作已经发生了。
大部分DBMS在这个时候,将会把所有的buffer写盘,这样可以保证所有改变了但是没写盘的buffer都写盘。就是说DBMS将通过”Sharp Checkpoint” flush所有”dirty”buffers。但是InnoDB只保证:(alogdata buffers不会超过某个限制点;(blog始终比data先写盘;(c)没有哪个data buffer的页头LSN等于被覆盖写的log记录。也就是说InnoDB”Fuzzy Checkpoint”
在COMMIT的时候,InnoDB不会将dirty data page写盘。之所以强调这个是因为,很容易让人想到,提交改变就是将所有东西写到一个持久媒介上。其实,只有log记录需要写。写dirty data page只可能发生在overflow或checkpoint时刻,因为它们的内容是多余的。
 
Recovery
recovery里面可以看到log是非常必要的:当数据库发生异常的时候,数据是可以恢复的。
对于不是损坏磁盘驱动器的异常,恢复是自动进行的。InnoDB读取最新的checkpoint日志记录,检查dirty pages是否在异常发生前写到磁盘上了,如果没有,则读取影响该页的log记录并应用它们。这被称为”rolling forward”。因为有LSN,所以InnoDB只需要比较这个数字就可以进行同步。
转自:http://zhumeng8337797.blog.163.com/blog/static/100768914201121552052960/?suggestedreading&wumii

你可能感兴趣的:(MYSQL)