上一篇: 下一篇:

InnoDB之Dirty page、Redo log

2009-08-13  |  09:55分类:技术细节  |  标签:  |  1,620 views

在InnoDB中,buffer pool里面的dirty page一方面可以加快数据处理速度,同时也会造成数据的不一致(RAM vs DISK)。本文介绍了dirty page是如何产生,以及InnoDB如何利用redo log如何消除dirty page产生的数据不一致。

  1. 当事务(Transaction)需要修改某条记录(row)时,InnoDB需要将该数据所在的page从disk读到buffer pool中,事务提交后,InnoDB修改page中的记录(row)。这时buffer pool中的page就已经和disk中的不一样了,我们称buffer pool中的page为dirty page。Dirty page等待flush到disk上。
    dirty_pages
  2. dirty page既然是在Buffer pool中,那么如果系统突然断电Dirty page中的数据修改是否会丢失?这个担心是很有必要的,例如如果一个用户完成一个操作(数据库完成了一个事务,page已经在buffer pool中修改,但dirty page尚未flush),这时系统断电,buffer pool数据全部消失。那么,这个用户完成的操作(导致的数据库修改)是否会丢失呢?答案是不会(innodb_flush_log_at_trx_commit=1)。这就是redo log要做的事情,在disk上记录更新。
  3. redo log在每次事务commit的时候,就立刻将事务更改操作记录到redo log。所以即使buffer pool中的dirty page在断电时丢失,InnoDB在启动时,仍然会根据redo log中的记录完成数据恢复。
  4. redo log的另一个作用是,通过延迟dirty page的flush最小化磁盘的random writes。(redo log会合并一段时间内TRX对某个page的修改)
    dirty_pages AND redo_log
  5. 正常情况下,dirty page什么时候flush到disk上?
    1).redo log是一个环(ring)结构,当redo空间占满时,将会将部分dirty page flush到disk上,然后释放部分redo log。这种情况可以通过Innodb_log_wait(SHOW GLOBAL STATUS)观察,情况发生该计数器会自增一次。
    2).当需要在Buffer pool分配一个page,但是已经满了,并且所有的page都是dirty的(否则可以释放不dirty的page),通常是不会发生的。这时候必须flush dirty pages to disk。这种情况将会记录到Innodb_buffer_pool_wait_free中。一般地,可以可以通过启动参数innodb_max_dirty_pages_pct控制这种情况,当buffer pool中的dirty page到达这个比例的时候,将会强制设定一个checkpoint,并把dirty page flush到disk中。
    3).检测到系统空闲的时候,会flush,每次64 pages。
  6. 涉及的InnoDB配置参数:innodb_flush_log_at_trx_commit、innodb_max_dirty_pages_pct;状态参数:Innodb_log_wait、Innodb_buffer_pool_wait_free。

参考文献

  1. http://mysqldump.azundris.com/archives/78-Configuring-InnoDB-An-InnoDB-tutorial.html
  2. http://dev.mysql.com/doc/refman/5.0/en/innodb.html
喜欢本文,那就收藏到: Del.icio.us Google书签 Digg Live Bookmark Yahoo书签 Facebook 百度搜藏 添加到饭否 QQ书签 Digbuzz我挖网

14条评论 关于 “InnoDB之Dirty page、Redo log”

  1. Taobao DBA Team » InnoDB Double write 发表于: 三月 12th, 2010 12:35

    [...] 涉及到的概念:Buffer Pool简称BP,Dirty Page,Log file,Flush,innodb tablespace。 [...]

  2. 匿名 发表于: 五月 6th, 2010 14:31

    我想问下 flush到disk的时候!~ LSN是怎么记录的,就比喻发生在第二种情况 dirty page满了,它flush到disk的时候是会把LSN记录到data file里的,假设此时并没有事务发生时,也就是说log buffer没有数据记录到redo log里 ,此时data file的LSN 比redo log的LSN 小还是一样!~ 有没有情况data file的LSN会比redo log的LSN大的情况

  3. orczhou 发表于: 五月 14th, 2010 09:40

    数据库始终是以“日志先行”的方式运行的,所以redo log里面最后一条LSN始终是最大的。Buffer Pool中的Page记录了两个LSN,一个是已经Flush到data file中最后一个LSN,还一个是修改了该Page的最后一个LSN。

  4. slevin 发表于: 五月 14th, 2010 16:43

    每当插入一条数据,如果未提交,innodb会不会将当前行设为新增行的版本号?如果要提交才会增加的话,那它未提交前数据是存在哪里呢?

  5. slevin 发表于: 五月 18th, 2010 10:49

    呵呵!~发现问题有点小白了!~未提交的数据肯定是存在内存里,没提交它肯定不会把当前行设为新增行的版本号

  6. orczhou 发表于: 五月 18th, 2010 20:36

    问题没有“小白”啊,不过猜想应该是在内存中的。

    第一,没提交的事务是没有写到redo中的,如果不是在内存而是在磁盘的话,崩溃恢复会非常麻烦,即使崩溃时未flush的dirty page不多,InnoDB仍然需要扫描每一个数据页,看看有没有没有提交的事务的数据,如果数据量大,这个过程是很痛苦的。事实上,也并不是这样,如果崩溃时事务量不大,无论数据量大小恢复都是很快的。

    第二,因为如果事务隔离级别是uncommit read的话,这样的数据其他的事务也能读取,说明这个数据是放在公共的地方的,所以猜想内存中的数据页应该已经写入了这些数据和这些数据的事务ID。

    猜想,猜想而已。

    数据行的版本号就是事务ID号,事务开始了就有事务ID,写到磁盘也不是没有可能,只是这样做的话,回滚和崩溃恢复是代价很大,事实上,回滚和崩溃恢复并不经常发生。

    再想想怎么去求证

  7. slevin 发表于: 五月 19th, 2010 10:23

    我一直在想读提交为什么会产生幻读和重复读,google百度也搜不到什么好的答案,而在innodb的REPEATABLE-READ级别,由于在MVCC和锁的控制,使它不会产生幻读,由于REPEATABLE-READ级别每次去读取数据都要去比较版本,所以会有CPU的开销,而uncommit read级别,我估计它只是在事务的开始去比较版本,在事务发生的过程中确不会,降低一点开销,所以会产生幻读和重复读,呵呵!~我也是猜想而已!~要等高手研究源码才知道

  8. slevin 发表于: 五月 19th, 2010 10:36

    刚刚测试了把!~上面那段话说错了,应该是不锁记录的情况下是看不到幻读的.

  9. slevin 发表于: 五月 21st, 2010 11:18

    MVCC只支持读提交和重复读提交级别,MVCC针对select 在高性能mysql是这样定义的
    innodb对比当前事务的版本号和找到行的版本号/删除号,当满足如下条件时,行被select出来:

    1、 行版本号不大于事务版本号。这确保了该行在事务开始时已存在,或者由当前事务创建、更新;

    2、 行删除号不存在,或者删除号大于事务版本号。这确保事务开始前行未被删除;

    在事务发生过程中,别的事务插入了一条数据,并且提交,在MVCC机制里,select会不会读取行版本大于事务版本的,而在读提交级别,select却可以读到大于事务版本的行,是不是MVCC的机制在读提交的定义不同?

  10. orczhou 发表于: 五月 21st, 2010 13:33

    @slevin 这里你可能混淆了MVCC的概念,MVCC指的是数据库(DBMS)实现多版本控制的一种机制。InnoDB有自己的MVCC机制(上面你也提到了)。在MVCC这种机制下,InnoDB实现了自己的事务隔离级别:read uncommit | read commit | reapeatable read | serializable。你上面提到是的在MVCC机制下reapeatable read的实现,当然也就和commit read有所不同。

  11. slevin 发表于: 五月 21st, 2010 14:59

    那MVCC在commit read实现是怎样的呢?为什么在读提交级别,select可以读到大于该事务版本号的行。

  12. orczhou 发表于: 五月 21st, 2010 17:35

    因为InnoDB的每条记录上都有事务ID,所以当前事务始终可以根据事务ID来判断,这个记录是在自己之前或者之后开始的。

  13. slevin 发表于: 五月 24th, 2010 10:34

    你的意思是说read commit级别可以读到大于自己事务ID的记录,而reapeatable read不可以咯!不过这也感觉到矛盾啦!为什么要让read commit级别读到大于自己事务ID的记录呢,符合SQL事务级别标准吗,还是可以增加并发和性能?

  14. InnoDB Double write | OurMySQL | 我们致力于一个MySQL知识的分享网站 发表于: 七月 9th, 2010 23:07

    [...] 涉及到的概念:Buffer Pool简称BP,Dirty Page,Log file,Flush,innodb tablespace。 [...]


发表您的评论