1、DB2数据库的日志原理
事务日志记录数据库中所有对象和数据的改变,在早前版本中最大可达256G,其大小为( logprimary + logsecond ) * logfilsiz,其中logprimary + logsecond的值小于或等于256,logfilsiz的最大为262144,在9.5版本中,日志最大已经可以达到512G,其中logfilsz的大小更改为524286。
DB2数据库的日志分为主日志和辅助日志,其中主日志在第一个连接到达数据库或者数据库被激活后立即分配,而辅助日志在主日志大小不够的时候动态分配。所以需要注意一点,日志所在的文件系统的大小必须大于主日志文件与辅助日志文件的大小之和。
DB2数据库有2种日志配置方式,循环日志与归档日志。
循环日志:这是数据库默认的日志使用方式,主日志用来记录所有的更改,当事务提交后,日志文件会被重用。当主日志文件达到限制时,辅助日志文件将被使用。这种日志方式可以进行崩溃恢复和版本恢复,不能进行前滚恢复,不支持在线备份。
当活动事务的使用空间超过主日志和辅助日志的限制或者日志空间超过磁盘可使用空间,将会得到日志满的错误。
归档日志:启用logarchmetd1、logarchmetd2或打开logretain参数,注意,在9.5版本中,不推荐使用logretain参数,其所有的设置值将被忽略。在数据库归档日志规划时,建议不再使用logretain的方法。日志文件将不会被删除-保持在线或者离线状态。支持前滚恢复和在线备份。
疑问:归档日志下,日志一直保留,持续生成新日志,为什么还会出现日志满的错误?
归档日志下,其可用的活动日志大小依然受到主日志与辅助日志大小之和的限制,所以,即使在归档日志下,日志满的场景与活动日志下是完全一样的。
2、日志使用中的问题与解决方法
在日常使用中,我们遇到最多的问题就是日志满,现在用几个实际的例子来看如何分析和解决日志满的问题,一般的,日志满可以分以下几个场景:
A、 环境准备,并介绍数据库日志使用大小评估方法:
数据库参数设置如下:
日志文件大小(4KB) (LOGFILSIZ) = 10000
主日志文件的数目 (LOGPRIMARY) = 3
辅助日志文件的数目 (LOGSECOND) = 2
日志总大小为200M.
创建测试用表:
1 2 3 |
C:Documents and Settingsadministrator>db2 "create table test_log(col int, col2 char(10) ,col3 timestamp,col4 varchar(100),col5 varchar(100),col6 varchar(100),col7 varch ar(100),col8 varchar(100))" |
DB20000I SQL命令成功完成。
创建插入数据的存储过程:
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
C:Documents and Settingsadministrator>db2 -td@ -vf proc_testlog.sql create procedure proc_testlog(v1 int) begin declare time int default 0; while (time < v1) do insert into test_log values(1,'testlog',current timestamp,'testlogtestlogte stlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestl og','testlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestl ogtestlogtestlogtestlog','testlogtestlogtestlogtestlogtestlogtestlogtestlogtestl ogtestlogtestlogtestlogtestlogtestlogtestlog','testlogtestlogtestlogtestlogtestl ogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlog','testlogtestl ogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogtestlogt estlog'); set time = time + 1; end while; end |
DB20000I SQL命令成功完成。
我们来评估下插入使用日志的情况,以便构造日志满的场景,使用db2pd来查看事务日志的使用。
分别打开2个db2cmd会话窗口,在窗口1中我们执行:
C:Documents and Settingsadministrator>db2 +c call proc_testlog(1)
返回状态 = 0
会话2中执行:
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 |
C:Documents and Settingsadministrator>db2pd -db sample -transactions Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:29:20 Transactions: Address AppHandl [nod-index] TranHdl Locks State Tflag Tflag2 Firstlsn Lastlsn LogSpace SpaceReserved TID AxRegCnt GXID 0x7FC21A80 7 [000-00007] 2 7 WRITE 0x00000000 0x00000 000 0x000027718800 0x000027718800 110 700 0x000000004F13 1 0 |
可以看到这个写操作占用的日志大约为700个字节,在回话1中再重复执行上面的命令,会话2中在看db2pd的输出:
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 |
C:Documents and Settingsadministrator>db2pd -db sample -transactions Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:45:55 Transactions: Address AppHandl [nod-index] TranHdl Locks State Tflag Tflag2 Firstlsn Lastlsn LogSpace SpaceReserved TID AxRegCnt GXID 0x7FC21A80 7 [000-00007] 2 8 WRITE 0x00000000 0x00000 000 0x000028E385B8 0x000028E38806 154 1334 0x000000004F57 1 0 |
1334-700=634,我们可以这样评估,单个事务每执行一次表插入,插入一行占用的日志约为700字节,在一个事务中,插入多条记录,插入一行记录占用的日志约为634字节,当然,实际上当插入多行时,日志的大小会比计算值略大。
使用这个方法可以根据业务运行情况来评估需要数据库应该配置的日志大小,也可以评估单个大事务需要的日志空间。
根据估算,200M总日志大小,200*1024*1024/635=330781。因此可以一次插入大约33W记录来构造日志满的场景。
B、 事务日志满场景一:当前未提交的事务太大,超过日志的限制。
在会话1中执行:
C:Documents and Settingsadministrator>db2 commit
DB20000I SQL命令成功完成。
提交前面未提交的事务。
C:Documents and Settingsadministrator>db2 +c call proc_testlog(330000)
SQL0964C 数据库的事务日志已满。 SQLSTATE=57011
这时候我打开另外一个session,执行一个不相关的插入操作。
C:Documents and Settingsadministrator>db2 "insert into test values(1112,1,’sdfsdfsdfsdf’,’sdfsdfsdfsdfsdf’,’sdfsdfsdffsdfsd’)
DB21034E 该命令被当作 SQL 语句来处理,因为它是无效的“命令行处理器”命令。在
SQL 处理期间,它返回:SQL0964C 数据库的事务日志已满。 SQLSTATE=57011
可以看到,当日志满的时候其他的任何记日志的操作都将不能进行,所以整个系统基本处于不可用的状态,除非等事务回滚结束。
OK,事务日志满的情况出现,现在我们就根据日志满的日志,来逆向分析是哪个操作导致的该问题,分析步骤如下:
首先,确定哪个应用的事务占用了大量的日志空间:
在回话2中执行:
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 10 11 |
C:Documents and Settingsadministrator>db2pd -db sample -transactions Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:02:27 Transactions: Address AppHandl [nod-index] TranHdl Locks State Tflag Tflag2 Firstlsn Lastlsn LogSpace SpaceReserved TID AxRegCnt GXID ….. 0x7FC21A80 7 [000-00007] 2 10 WRITE 0x00000000 0x00000 000 0x00003D86000C 0x000048C4FCD0 14014572 201955470 0x000000004F91 1 0 ….. |
可以看到上面红色部分, AppHandl为7的应用的一个事务占用了大量的日志。如果有多个应用占用了大量的日志,我们可以按照下面的方法逐个分析,看每个应用是执行了什么sql导致的占用如此大的日志。
然后使用db2pd确定这个日志执行了什么语句导致占用了大量的日志:
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 10 11 |
C:Documents and Settingsadministrator>db2pd -db sample -applications Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:02:36 Applications: Address AppHandl [nod-index] NumAgents CoorEDUID Status C- AnchID C-StmtUID L-AnchID L-StmtUID Appid WorkloadID WorkloadOccID ….. 0x7AED8080 7 [000-00007] 1 1572 UOW-Waiting 0 0 185 1 *LOCAL.DB2.081111100729 1 1 ….. |
Application handle为7的应用,对应的L-AnchID为185,L-StmtUID为1。在回话2中继续使用db2pd找到对应的sql语句:
双击代码全选 |
|
1 2 3 4 5 6 |
db2pd -db sample -dynamic ….. Dynamic SQL Statements: Address AnchID StmtUID NumEnv NumVar NumRef NumExe Text 0x7EA7D540 185 1 1 1 1 1 CALL proc_testlog(?) … |
对应AnchID为185, StmtUID为1的语句,是CALL proc_testlog(?),通过上面的分析,我们可以找到,是调用存储过程proc_testlog导致占用了大量的日志,从而找出导致日志满的罪魁祸首。
解决方案:
首先,尽量规避超大事务的操作,对于必须执行的这种大操作,可以考虑是否可以分解成几个事务进行,如果可以,尽量分解为小事务的方式进行;如果业务上不可以分解,是否可以考虑采用不记日志的方式?比如,load代替insert?表针对这个操作,暂时改为不记日志的方式等等。
注意:当进行不记日志的操作时,必须非常清楚这样的操作的影响,比如,归档日志下数据库前滚的影响,hadr与复制的数据同步影响,操作失败结果如何等等。
其次,总有些我们无法预料的操作发生,可能某个维护人员某天发出一个不适当的命令,删除了大量的数据,导致日志满,整个系统无法运行,如何规避这样的操作带来的系统运行影响呢?可以设置参数:max_log和DB2_FORCE_APP_ON_MAX_LOG注册变量。
max_log此参数指示一个事务可以消耗的主日志空间的百分比。该值是为 logprimary 配置参数指定的值的百分比。如果该值设置为 0,那么对一个事务可以消耗的总的主日志空间的百分比没有限制。我们可以配合设置DB2_FORCE_APP_ON_MAX_LOG注册变量来规定如果应用程序违反了 max_log 配置,我们对该应用如何处理,DB2_FORCE_APP_ON_MAX_LOG设置为true,则超过max_log的应用回被强制与数据库断开连接,事务将被回滚,并且将返回错误 SQL1224N。如果 DB2_FORCE_APP_ON_MAX_LOG 注册表变量设置为 FALSE,则违反了max_log设置的的事务将失败,并返回错误 SQL0964N。该应用程序仍然可以提交在工作单元中由先前语句完成的工作,它也可以回滚已完成的工作以撤销该工作单元。
通过次设置我们可以保证即使有大事务操作,总有(1-max_log/100)*log_primary+log_second的日志可以用来处理日常交易,从而避免系统中断。
注意: 由 max_log 配置参数施加的限制不适用于下列 DB2 命令:ARCHIVE LOG、BACKUP DATABASE、LOAD、REORG TABLE(联机)、RESTORE DATABASE 和 ROLLFORWARD DATABASE。
C、 事务日志满场景二:某个事务一直未提交,占用的日志不能被重用,导致日志满
现在看另外一个场景,我在一个会话中执行了如下命令:
C:Documents and Settingsadministrator>db2 +c call proc_testlog(3)
SQL0964C 数据库的事务日志已满。 SQLSTATE=57011
显然,数据库日志已满,于是,根据上面的方法,我找是哪个事务占用了日志。
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
C:Documents and Settingsadministrator>db2pd -db sample -transactions Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:10:12 Transactions: Address AppHandl [nod-index] TranHdl Locks State Tflag Tflag2 Firstlsn Lastlsn LogSpace SpaceReserved TID AxRegCnt GXID 0x7FC21A80 12 [000-00012] 2 7 READ 0x00000000 0x00000 000 0x000000000000 0x000000000000 0 0 0x0000000053A9 1 0 0x7FC22780 13 [000-00013] 3 0 READ 0x00000000 0x00000 000 0x000000000000 0x000000000000 0 0 0x00000000538F 1 0 0x7FC23480 14 [000-00014] 4 0 READ 0x00000000 0x00000 000 0x000000000000 0x000000000000 0 0 0x0000000053BE 1 0 0x7FC24180 15 [000-00015] 5 0 READ 0x00000000 0x00000 000 0x000000000000 0x000000000000 0 0 0x000000005391 1 0 0x7FC24E80 16 [000-00016] 6 0 READ 0x00000000 0x00000 000 0x000000000000 0x000000000000 0 0 0x000000005394 1 0 0x7FC25B80 17 [000-00017] 7 4 WRITE 0x00000000 0x00000 000 0x0000538A93B7 0x0000538A9455 184 408 0x00000000539A |
很奇怪,从结果显示,我没有发现任何一个占用大量日志的应用程序,日志的使用显然都非常的小,那为什么日志还会满呢?我们再注意下占用日志的应用,查看下各自使用的日志文件。
双击代码全选 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
C:Documents and Settingsadministrator>db2pd -db sample -logs Database Partition 0 -- Database SAMPLE -- Active -- Up 0 days 00:12:34 Logs: Current Log Number 4 Pages Written 9498 Method 1 Archive Status n/a Method 1 Next Log to Archive n/a Method 1 First Failure n/a Method 2 Archive Status n/a Method 2 Next Log to Archive n/a Method 2 First Failure n/a Address StartLSN State Size Pages Filename 0x7FBECBD4 0x0000537F0000 0x00000000 10000 10000 S0000000.LOG 0x7FBECC74 0x000055F00000 0x00000000 10000 10000 S0000001.LOG 0x7FBECD14 0x000058610000 0x00000000 10000 10000 S0000002.LOG 0x7EABB2F4 0x00005AD20000 0x00000000 10000 10000 S0000003.LOG 0x7EABB394 0x00005D430000 0x00000000 10000 10000 S0000004.LOG |
分析发现,这个占用日志的应用的日志开始lsn为0x0000538A93B7,结束lsn为0x0000538A9455,正好落在第一日志文件中,因为这个事务一直没有被提交,所以S0000000.LOG一直不能被重用,这样业务在将主日志和辅助日志用完后,无法重新开始使用日志文件,导致出现日志满的错误。同样,使用上面的方法,我们可以查找出这个Applications handle为7的一直没有提交的小事务执行的是什么操作。
上面的情况模拟方法:
在一个回话中执行一个小事物,比如
C:Documents and Settingsadministrator>db2 +c "insert into test values ( 1112,1, ’sdfsdfsdfsdf’ , ’sdfsdfsdfsdfsdf’ , ’sdfsdfsdffsdfsd’ )
在另外一个回话中执行占用事务比较大的操作,比如:db2 call proc_testlog(300000),在这个回话中的操作都及时提交,直到配置的日志文件被使用完,再执行小操作db2 call proc_testlog(3),就可以出现上面的日志满的情况。
解决方案:
可以看出,不是日志满的问题一定是由于应用占用大量的日志导致的,一个被忽略的未提交的操作也可能导致系统的日志无法被重用而导致日志满,在应用中,这是我们应该尽量避免的。但是总是如果无法保证所有的操作都及时的提交,我们可以设置num_log_span参数来规避这个问题,参数指定是否对一个事务可以跨越多少个日志文件具有限制以及该限制是多少,当设置这个参数后,未提交的事务所在的日志与当前日志跨越的个数超过这个值,将被中断,从而避免事务长时间存在导致系统日志满。另外大事务可以跨越的日志也不能超过这个限制,所以当设置max_log和num_log_span后,一个事务所可以使用的事务日志将取2者中比较小的值。
当启用了无限活动日志空间时,max_log和 num_log_span 配置参数非常有用。如果打开了无限记录(即,logsecondary 为 -1),那么事务数不受日志文件数的上限(logprimary + logsecond)限制。当到达 logprimary 的值时,DB2 将开始归档活动日志,而不是使事务失败。这样可能会导致问题,例如,有一个长期运行的事务,但一直未落实它(可能是由于应用程序不正确导致的)。如果出现这种情况,那么活动日志空间会不断增长,从而可能使得崩溃恢复性能很差。为了防止这样,可以为 max_log 和/或 num_log_span 配置参数指定值。
注意:系统临时表的使用,系统临时表的数据操作是不记日志的,但是表的定义是有少量日志记录的,所以,临时表定义了一直没操作,不提交也可能会引起部分小日志的一直被占用。