• 一个长事务引起的血案——Informix 长事务回滚失败引起的阻塞故障处理


    Informix 11.5数据库,双机hdr热备。这是背景。

    这两天出了个事情:由于一个大事务引起了逻辑日志耗尽,导致数据库状态进入出现了叫做“长事务阻塞Blocked:LONGTX”的状态中,停止服务。本文分析其原理与解决办法。

    1   长事务阻塞的原理

    当事务开始时,数据会在在所在的逻辑日志文件中记录一个检查点Check Point,事务运行时,这个检查点及之后的逻辑日志都处于未提交状态,要被保留一直到事务提交或是回滚,这些逻辑日志才能标志为使用过,并能再次被循环利用。

    如果事务中的操作很多,事务就会跨多个逻辑日志文件。当事务使用的逻辑日志文件超过一定量后,就判断为“长事务”。因为事务回滚也是需要使用逻辑日志的,所以,当数据库发现长事务使用的逻辑日志数目到达“回滚折返点”时,这就意味着如果继续执行事务,剩下的事务可能不足以保证此长事务回滚,数据库就会中断事务执行,并立即回滚。

    但因为回滚过程也是需要使用逻辑日志的,在回滚中一但出现逻辑日志不够用的情况,回滚操作用完了剩余的逻辑日志文件,却还没有回滚完成,数据库就会发生“长事务阻塞”。这种情况会出现在一个长事务回退时,另一个事务又快速消耗剩余日志等情况下。

    注意这里的逻辑日志不够用指得不是有没有ontape –c备份过,而是指因为事务的起始检查点在第N个逻辑日志文件里,而现在已经执行到了第N-1个逻辑日志文件里(informix的逻辑文件是循环的,执行到N-1号就意味着追了尾了),也就是所有的逻辑日志文件都处于未提交状态,但是当前事务还是不够用,这种情况下,就算所有的日志文件都已经被ontape –c备份过了,也不能被再次利用,因为事务的回滚或是提交还没有完成。

    请参考 http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-1001haodh/index.html

    2   现象与检查

    在这时候,如果你检查数据库的状态,会是如下的样子:

    infodb% onstat -
    IBM Informix Dynamic Server Version 11.50.FC6     -- On-Line (Prim LONGTX) -- Up 35 days 16:41:40 -- 3920896 Kbytes
    Blocked:LONGTX
    

    可以执行onstat –x来检查事务情况

    infodb% onstat -x
    
    IBM Informix Dynamic Server Version 9.40.FC7     -- On-Line (LONGTX) -- Up 35 days 16:41:56 -- 3920896 Kbytes
    Blocked:LONGTX
    Transactions
    address          flags userthread       locks  beginlg curlog  logposit   isol    retrys coord
    1c8b2b298        A---- 1c8ae9850        0      0       0       0x0        COMMIT  0     
    1c8b2b508        A---- 1c8aea078        0      0       0       0x0        COMMIT  0     
    1cd4d7918        A-B-- 1d44fdcb0        2      119408  119507  0x39722c   DIRTY   0     
    1cd4d8068        A---- 1cd576e38        1      0       0       0x0        COMMIT  0     
    1cd4d82d8        A---- 1cd577660        1      0       0       0x0        DIRTY   0     

    注意其中的flags为 “A-B—“ 的事务,B状态表示begined, 说明这个事务还在执行中。注意他的beginlg也就是开始时的逻辑日志文件号为119408,而当前的日志已经到了119507, 两者相差减加1就是100,正是这个informix系统里所设置的日志数量(您的系统可能不同),这说明这个事务把所有的逻辑日志文件全部用完了。

    如果执行onstat –l检查逻辑日志文件的使用情况,会看到下面的情况:

    2a273c368  27       U-B----  119506   7:250053            12500    12500   100.00
    2a273c3d0  28       U---C--  119507   7:262553            12500        0    18.50
    2a273c438  29       U-B---L 119408   7:275053            12500     2313   100.00
    2a273c4a0  30       U-B----  119409   7:287553            12500    12500   100.00
    2a273c508  31       U-B----  119410   7:300053            12500    12500   100.00

    所有的日志已经备份过了,但是由于L标示在119408,说明有事务从这里开始,现在还没有提交或是回退,而当前已经到了119507,所有的逻辑文件用了一圈,已经没有日志可以用了,但当时工作还没有做完。

    查看log文件(是指那个文本文件),会看到如下的内容:

    17:59:34  Aborting Long Transaction: tx: 0x1cd4d7918   username: informix uid: 300
    17:59:35  Long Transaction 0x1cd4d7918   Aborted. Rollback Duration: 0 Seconds
    … 这里是一大段检查点与逻辑日志用完和备份的信息,直到……
    18:03:22  ALERT: The oldest logical log (119408) contains records from
              an open transaction (0x1cd4d7918).  Logical logging will
              remain blocked until a log file is added.  Add the
              log file with the onparams -a command, using the -i
              (insert) option, as in:
     
              onparams -a -d <dbspace> -s <size> -i
     
              Then complete the transaction as soon as possible.

    这里的意思是说,最老的那个逻辑日志文件包括了一个开放着的事务,逻辑日志会阻塞,直到增加一个新的逻辑日志文件。要增加逻辑日志文件,可以用onparams –a,并使用-i选项,就像下面的命令,这样就能尽快结束这个事务了。

    onparams –a –d <dbspace> -s <size> -i

    这个命令的意思是:用dbspace的空间来增加一个逻辑日志文件,大小为size, 并插入在当前日志的后面。

    3   如果正常处理的话……

    用onstat –d 看一看你的数据里还没有4k块(因为逻辑日志只能用4k块的chunk)的dbspace可以用,比如我的:

    $ onstat -d
     
    Dbspaces
    address  number   flags      fchunk   nchunks  pgsize   flags    owner    name
    2a0e75028  1        0x40001    1        1        4096     N  B     informix rootdbs
    2a273fdc0  2        0x42001    2        1        8192     N TB     informix tempdbs01
    2a2740028  3        0x42001    3        1        8192     N TB     informix tempdbs02
    2a27401c0  4        0x42001    4        1        8192     N TB     informix tempdbs03
    2a2740358  5        0x42001    5        1        8192     N TB     informix tempdbs04
    2a27404f0  6        0x40001    6        1        4096     N  B     informix plogdbs
    2a2740688  7        0x40001    7        2        4096     N  B     informix llogdbs
    2a2740820  9        0x40001    19       70       8192     N  B     informix datadbs
     8 active, 2047 maximum
     
    Chunks
    address   chunk/dbs     offset     size       free       bpages     flags pathname
    2a0e751c0  1      1      0          500000     479443                PO-B- /informix.links/bej/rootchk
    2a27409b8  2      2      0          512000     511947                PO-B- /informix.links/bej/tempchk01
    2a2740ba8  3      3      0          512000     511947                PO-B- /informix.links/bej/tempchk02
    2a2740d98  4      4      0          512000     511947                PO-B- /informix.links/bej/tempchk03
    2a274b028  5      5      0          512000     511947                PO-B- /informix.links/bej/tempchk04
    2a274b218  6      6      0          512000     266947                PO-B- /informix.links/bej/plogchk
    2a274b408  7      7      0          2048000    172947                PO-B- /informix.links/bej/llogchk1
    2a274b5f8  8      7      0          2048000    2047997               PO-B- /informix.links/bej/llogchk2
    2a274b7e8  9      9      0          1024000    1023997               PO-B- /informix.links/bej/indxchk01
    2a274b9d8  10     9      0          1024000    1023997               PO-B- /informix.links/bej/indxchk02
    

    注意第一个表里,4k的有rootdbs和llogdbs, llogdbs我在安装时专用划分了用于逻辑日志的dbspace。注意第二个列表里,对应的llogdbs有两个chunck,而第一个chunk llogchk1里还有free 172947块,第二个llogchk2里则一点也没用过呢,整整8G.

    如果你事先没有留一些空间,那就只好从其他4k块的dbspace里找一些空间来用,如rootdbs, 如果没有,那就只好再增加新的chunk。这不在本文范围内。

    现在,要根据数据库的提示,为之增加逻辑日志文件,如下命令:

    Onparams –a –d llogdbs –s 500000 –i

    上面的容量单位是KB, 500000就是500M。我本来的逻辑日志是50M,但这次为了能足够空间用于回滚事务,就直接建立了500M的一个日志文件。

    如果正常,数据库就会有了新的逻辑日志用于继续执行事务回滚,如果还不够,就只好再加。不过最好一次加足。

    等一会儿事务回滚成功,长事务阻塞状态就解除了,原来的那个检查点被回退,所有的逻辑日志恢复到可回收利用的状态,恭喜你!

    已经增加的逻辑日志太大了,如果不愿意保留,可以通过下列的命令删除之

    onmode –l   #强制跳过当前日志文件,进入下一个日志文件。
    onmode –l   #强制跳过当前日志文件,进入下一个日志文件。这里多跳一个,保险一点。
    
    onmode –c   # 做一个检查点,阻塞或是解阻塞数据库服务器.
    onparams -d -l <逻辑日志文件编号> –y

    逻辑日志文件编号可以用onstat –l来看,找到你刚刚加上的那个,第2列就是它的编号。

    这样就全完事儿了。建议立即做0级备份数据库。

    4   但是有时运气不好,如果你还有备机……

    但是,昨天的实际情况是笔者没有那么幸运,在执行完增加逻辑日志文件操作之后,数据库立即down了,再起就起不来了,日志里显示

    19:34:32  Assert Failed: Unexpected virtual processor termination, pid = 213056, exit = 0x90009
     
    19:34:32  IBM Informix Dynamic Server Version 11.50.FC6   
    19:34:32   Who: Session(5450877, life2@WIN-3XZYO8F2ZGA.lifebj.int, 3124, 7000002b0b548d0)
                    Thread(5691992, sqlexec, 7000002a1335bb8, 1)
                    File: mt.c Line: 14124
    19:34:32  stack trace for pid 164620 written to /informix.dump/af.de4055c8
    19:34:32   See Also: /informix.dump/af.de4055c8
    19:34:36  mt.c, line 14124, thread 5691992, proc id 164620, Unexpected virtual processor termination, pid = 213056, exit = 0x90009
    .
    19:34:38  The Master Daemon Died
    19:34:38  PANIC: Attempting to bring system down
    19:34:38  semctl: errno = 22
    
    19:34:38  semctl: errno = 22
    -------------------------------以上是增加逻辑日志文件后的系统报错,以下是重启的日志---------------------------
    19:58:59  Log file 1 added to DBspace 7.
    19:58:59  Logical Log 59579 - Backup Completed
    19:58:59  Assert Failed: Dynamic Server must abort
    19:58:59  IBM Informix Dynamic Server Version 11.50.FC6   
    19:58:59   Who: Session(23, informix@bejlif, 0, 2a133e5b8)
                    Thread(60, fast_rec, 2a1308878, 5)
                    File: rslog.c Line: 3629
    19:58:59   Results: Dynamic Server must abort
    19:58:59   Action: Reinitialize shared memory
    19:58:59  stack trace for pid 176584 written to /informix.dump/af.4245b83
    19:58:59   See Also: /informix.dump/af.4245b83
    19:59:02  rslog.c, line 3629, thread 60, proc id 176584, Dynamic Server must abort.
    19:59:03  Fatal error in ADM VP at mt.c:13851
    19:59:03  Unexpected virtual processor termination, pid = 176584, exit = 0x100
    19:59:03  PANIC: Attempting to bring system down

     可以看到logfile的增加没成功,在重启后数据库又在试着执行上次当机时未完成的操作,但还是出错了。我也不知道为什么,估计这是informix软件的BUG之类的。这样一来,主机基本就算完蛋了,反正我的能力是没办法了。

    但技术解决不了的问题,由运维手段解决。我们的数据库是hdr主备机的。接下来我就在备机上看onstat –x,那个长事务也锁在那里,但新增的logfile没有同步过来。于是我只好把备机从”从机只读”切为独立模式。

    强烈建议在做这个之前,从备机里把数据能备份的备份出来,只读的数据库是可以取出数据来的,一但进入独立主用模式,未完成的长事务也会继续执行,也会进行长事务阻塞,你就不能连接数据库了。做为一个悲催的IT人,这是你最后的生命线。

    onmode –d standard

    备机也立即进入了长事务阻塞模式。这是必然的,备机需要继续回滚这个事务。

    然后执行相同的增加逻辑日志文件操作。

    onparams –a –d llogdbs –s 500000 –i

    这次成功了!备机很快回滚了事务,回到Online状态。继续执行前面说的:

    onmode –l   #强制跳过当前日志文件,进入下一个日志文件。
    onmode –l   #强制跳过当前日志文件,进入下一个日志文件。这里多跳一个,保险一点。
    onmode –c   # 做一个检查点,阻塞或是解阻塞数据库服务器.
    onparams -d -l <逻辑日志文件编号> –y

    检查备机数据,确认数据是最新的,业务数据到了阻塞时间点之前,这说明你没有损失数据。

    接下来要做的,是从备机上做一个0级的备份到磁带上去。

    Ontape –s –L 0

    把磁带放到主机上,执行恢复。

    Ontape –r

    完事后做onmode –m进入online模式。检查数据。

    主机恢复成功后,同一盘磁带放回备机上,执行物理恢复:

    Ontape –p

    完成就可以做主备机hdr同步了:

    主机上:

    onmode -d primary <备机实例名>

     

    备机上:

    onmode -d secondary <主机实例名>

    上述的备份、恢复与重建hdr过程我就不说了,请按自家手册进行。

    5   最坏的坏果

    上述过程失败了,而且您还没有没有备机?那你有过去的定期备份磁带吧,有的话就按您家的手册恢复。上次备份到现在更新的数据您就再想办法吧。

    还没有备份磁带?您干什么吃的呀!

    不过,听说IBM自己有办法,把损坏的逻辑日志删除或是修好,然后机器就又跑起来了。因为数据其实都在数据库里。

    不过IBM的informix的服务好贵,而且……已经停止对这个产品的服务了。

  • 相关阅读:
    C++之类和对象
    PHP程序设计基础
    PHP函数和MySQL数据库
    HTML语言基础
    文件和目录1(文件属性和权限)
    文件IO
    查找
    使用tcpdump抓包实例
    导入模块的2种方法
    ansible启用sudo执行命令
  • 原文地址:https://www.cnblogs.com/haoxiaobo/p/2854973.html
Copyright © 2020-2023  润新知