baoshengfei
作者baoshengfei·2016-02-04 21:25
软件架构设计师·EM

DB2的日志管理

字数 5570阅读 2778评论 1赞 2

        首先解决一个大家一直很好奇的问题。日志什么时候从log buffer刷到磁盘上?多数的答案会是:

        1. 事务提交

        2. log buffer满了

        3. 一秒钟一次

        但是,我看到的算法是这样的:

        int interval = 50ms

       while(1)

        {

            rc = timedout_wait(......, interval);

            interval *= 2;

            flush_logbuf_to_disk (  );

            if (interval = 800ms)

                interval = 50;

        }

        什么意思呢?db2loggw这个线程基本上就在这个循环中运行。首先会在timeout wait上等,如果有commit,就flush log,如果等interval个毫秒还没有commit,就直接flush一次,并且把interval乘以2. 然后回去继续循环。

        这个说法的正确性,大家可以使用trace跟踪db2loggw这个EDU来证实。

        好,进入正题,开始讨论数据库日志。数据库的日志是数据库理论里面包括的内容,是数据库一致性不可缺少的内容。所有的数据库写操作,都是先把数据写到bufferpool里面,但是直到日志写到磁盘上以后,事务才算完成了。为什么数据写到bufferpool,日志要写到磁盘呢?为什么数据不直接写磁盘呢?因为数据在磁盘上是随机IO,而日志是顺序IO。随机IO如果每次都写磁盘,性能就太差了。

       日志里面都记了什么东西呢?虽然每个数据库记得内容都不一样,但是有一个答案是准确的,日志里面记得是变化的数据。比如说对于一条update,日志里面记了变化前的数据和变化后的数据,也就是update前,在相关的page、slot里面的数是什么和update后,相关的page、slot里面存放的是什么数据。这些数据,都可以使用db2readlog api(在sqllib/sample/目录下有相关的代码)读取出来。这里,日志说的是redo log,MySQL的binlog算不上事务日志。在DB2里面,一个更新操作没有提交前,每一个对数据的更改都对应了一条或者几条redo日志记录,同时,生成相应的undo日志记录,保存在预留的日志空间中,当事务提交以后,每个事务就会产生最后一条commit日志记录。这条commit记录就是事务终止的标记,然后undo日志就删除了。

       DB2的日志文件的文件名长得SQL0000001.LOG,SQL000002.LOG这个样子,是顺序用的。也就是1写满了,就开始用2,然后3。平时所说的log file number,也就是这个文件名中的数字。例如,first active log number,first archive log number,这些变量的数值就是对应的文件名中的数字。LSO是标记每个日志记录位置的号,在DB2中,一个LSO就代表一个字节。日志文件中的LSO随着日志记录的不断写入而不断增加。

       简单描述一下数据库崩溃恢复的过程:在数据库运行过程中,有两个位置在在某种条件下会被写到磁盘上。一个位置是缓冲池中最早的那么没有把数据写到磁盘上的那个事务的LSO(简单理解为V10以前的LSN);另一个位置是第一个没有提交的事务的LSO。这两个位置也是我们所说的checkpoint的重要部分。数据库启动以后,就首先从控制文件中找到到这两个LSO中较小的那个,然后从那个位置开始replay redo日志。redo日志的过程中,不断的建立事务的链表,如果最后读到某个事物的commit日志记录,就把这个事务从链表中删除,否则就一直保存着事务。redo到end of log以后,如果这个链表中还有事务,既存在open transaction,就将这些事务进行undo。然后这个数据库就变成了可用的数据库。整个这个过程,可以从db2diag.log中看出来。

        所以,怎样让数据库启动以后尽快成为可用数据库呢(减少crash recovery的时间)?在数据库的配置里面,一是在应用上避免大的事务,而是设置合适的softmax这个参数的值。

        其实,整个crash recovery的过程非常复杂。可以调整的参数也很多,上面只是最表面的内容。下面,聊一聊和日志相关的EDU吧。使用db2pd -edus | grep -i log可以在不同的阶段看到不同的内容。

         db2loggw:DB2里面将日志从log buffer刷到磁盘上的线程。本文最开始的算法就是这个线程做的事情。

         db2logmgr:DB2里面负责归档日志到归档设备、从归档设备retrive日志到活动目录的线程。比如一个日志写满了,就是这个线程进行归档的。

         db2loggr:分配日志的线程。数据库一起来,就是这个线程讲活动日志进行创建和初始化的。日志的重命名和格式化也是这个线程做的。

         db2redom:读区活动日志文件的线程。例如crash recovery的时候,就是这个线程读文件。这个线程是按照日志页去读文件的。

         db2shred:将以页为单位的日志解析成一个一个的log record,并且按照table space放到一个一个的queue中。

         db2redow:从queue中不断的去读log record,然后进行redo。

        看到这,大家应该能看出来日志怎样归档,崩溃恢复的时候各个线程怎样合作了。

        下面,聊一聊几个和日志相关的概念和配置吧。

        不可恢复数据库和循环日志:这个概念很容易理解,就不多说了。使用循环日志的DB2数据库以为日志文件不断的被覆盖使用,因此是不支持前滚的。

        可恢复数据库和归档日志:和上面的概念相反,归档日志是日志用完了以后,就归档保存起来。如果有一天想恢复到昨天某个时点的语句。就使用这个时点以前的一个备份镜像首先进行恢复,然后再使用归档保存的日志文件进行前滚,前滚到需要的时点的数据。数据库创建以后,默认是使用循环日志的。在实际的生产系统上,都应该使用可恢复数据库。将循环日志修改为归档日志,需要设置logarchmeth1或者logarchmeth2,设置以后,数据库处于backup pending状态,需要进行一次全量数据库备份。如果数据库太大,备份时间太长,而恢复业务的时间又很紧张,可以使用db2dart直接修改数据库状态。

        日志缓冲区大小(logbufsiz):就是前面提到的存放还没有提交的事务的一段内存区。每个事务产生的日志都会被append到这段内存区域,并且有db2loggw使用本文最开始的办法将数据刷新到磁盘上。在DB CFG中,这个配置的大小以4K为单位。这段内存设置的原则是不能发生logbuffer full。

        活动日志文件:所谓活动日志,就是数据库崩溃恢复还需要的日志文件。也就是首活动日志以后的保存了日志的文件。什么是首活动日志?前面我们说过了两个LSO(一个位置是缓冲池中最早的那么没有把数据写到磁盘上的那个事务的LSO(简单理解为V10以前的LSN),我们叫minbuflso;另一个位置是第一个没有提交的事务的LSO,lowtranlso)。首活动日志就是保存有min(lowtranlso,minbuflso)这个LSO的日志文件。简单讲,也就是崩溃恢复开始位置的那个文件。活动日志包括两部分配置,一个是logprimary,定义了首先要使用的日志文件的个数,这部分日志在数据库服务器启动的时候有db2loggr首先初始化好;一部分是logsecond,这部分日志文件在数据库启动的时候不初始化,也不会创建,在运行的时候,如果logprimary不够存放所有的日志,就开始分配。这部分日志一旦分配,除非数据库重启,否则不会自动缩减。logprimary这个参数是不能在线修改的,但是logsecond是在线生效的。当logprimary和logsecond都用完的时候,数据库的日志就用尽了。

        想单独提一下首活动日志,因为有几次,我们的系统报错说日志用尽了。用snapshot for database看的时候,首活动日志的号确实很小,也就是说。然后看holding latest log的应用的状态(snapshot for applications)是UOW Waiting状态。我最终不知道为什么一个处于UOW Waiting状态的应用为什么会holding first active log。解决的办法就是使用force application命令将这个应用force掉。如果有可能,建议大家在应用里面一定要显式的去close一个transaction。

        归档日志文件:随着事务的提交和缓冲池的刷新,首活动日志不断的增加。当首活动日志增加一个日志以后,上一个日志其实对数据库崩溃恢复就没有用了。但是这个日志可能会用来进行前滚恢复,所以,DB2提供了几个对这个日志的几种处理方式。第一种是logretain,也就是让这些不活动的日志保留在活动日志目录下面,又用户自己删除。第二种是log archive,也就是通过设置logarchmeth1和logarchmeth2来设置将不活动的日志如何归档,归档到什么位置。例如,可以直接将活动日志归档到TSM活着归档到其它的文件系统(DISK:/path)。

        日志文件大小(logfilsiz):这个就容易理解了,日志是保存在日志文件里面的。日志文件越大,每个日志文件能保留的日志越多。这个就应该根据事务的特点进行设置了。

        日志文件位置:保存活动日志文件的位置可以通过newlogpath来设置。活动日志还有一个配置叫mirror log,也就是将活动日志保存两份。当其中一份的文件系统坏了的时候,另外一份还可以继续用。mirror log的位置可以通过配置mirrorlogpath来设置。

        日志就写到这吧,作为入门的内容,更深更细的请读者去信息中心探索。

        因为很多朋友有反应DB2 HADR重做日志的性能,这里,想简单聊一聊DB2 HADR。DB2 HADR基本上就是备机不断的在进行数据库的前滚(rollforward)。DB2 HADR的性能在我这篇《DB2 HADR性能调优及HADR模拟器》里面写的比较详细。想重点提一下的就是:1. 要认真规划表空间的数量及表的分布,这样可以增加前滚的并发度;2. 要使用尽量大的extend size。3. 可以合理设置一下这三个参数:PREC_NUM_AGENTS;PREC_NUM_QSETSIZE;PREC_NUM_QSETS。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

2

添加新评论1 条评论

cwhcwh数据库管理员cwh
2016-02-19 09:01
謝謝分享
Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广