简介:面向需要借助NetBackup完成DB2数据库异机恢复的DBA与运维人员,这份文档围绕DB2 Agent配置、数据库参数调整、备份脚本编写及db2.conf策略设定展开,涵盖Agent配置、参数设置、备份脚本、db2.conf策略及归档日志处理等模块,重点说明如何启用用户出口程序、归档日志与增量备份,并对userexit、logretain、trackmod等参数的作用和启用顺序给出提示,同时说明完整备份与归档日志备份的关键配置思路。压缩包内仅含1个doc文件,大小约314KB,内容以配置参数、脚本片段、策略示例和归档日志处理说明为主,便于按知识点查阅与对照实践。已有340人学习,适合作为DB2异机恢复场景的速查手册和实验参考,也可帮助读者梳理从备份策略到恢复验证的流程,理解db2_backup脚本、db2.conf文件与备份计划的配合关系,减少配置遗漏和排错盲区。
1. 异机恢复:为什么备份文件明明在,restore 却找不到它
很多人对 DB2 异机恢复的第一印象,是把备份片拷到新机器上敲一条 restore。真到生产上换机器才发现,备份介质根本不在本地磁盘,restore 里load后面跟的那个库文件,才是把 DB2 和备份系统缝在一起的那根线。它同时踩在三条链路上:数据库的日志链要完整到能前滚到目标时间点,备份系统的镜像目录要能跨客户端定位到源客户端的备份片,恢复端的配置要明确告诉备份服务器「去谁家取数据」。任意一条断了,恢复就变成玄学。下面从日志模式这一层开始,一路拆到 restore 的每个参数。
2. 归档日志模式:userexit 和 logretain 改的到底是什么
restore 能把数据库拉回来,但「拉回到哪一刻」取决于日志。默认配置下的 DB2 有一堆日志文件,看着挺多,实际上它们是一个环,写满就覆盖最老的。这种状态下的备份只能恢复到备份结束那一刻的一致性点,中间发生的事务一概丢失。要让它能前滚,必须先把日志管理模型从「循环」换成「归档」,而这一步是异机恢复所有能力的地基。
2.1 循环日志与归档日志的分水岭
循环日志(circular logging)的设计目标是省空间。DB2 维护固定数量的主日志文件,写满之后启用次要日志,事务提交、日志不再需要用于崩溃恢复时,最老的文件立刻被回收重用。数据库永远只保留「回到最近一次一致点」所需的最小日志量,剩下的历史被主动丢弃。这种模式下你甚至不能安全地做在线备份——备份期间产生的事务日志,备份一结束就可能被覆盖掉,恢复时链条直接断掉。
归档日志(archive logging)反过来了:日志文件写满之后不再复用,而是留在日志目录里,等外部程序把它搬走。DB2 只负责「写」,不负责「删」。一旦打开了这个模式,数据库的日志目录就成了一个只进不出的队列,必须有人在另一端消费,否则磁盘迟早被撑爆。这个「另一端」就是 userexit。
真正切模式的动作是两条 update db cfg 命令,但它们有个硬性前提:数据库上不能有任何活动连接,否则参数根本落不了地:
# 以实例用户执行,先把所有连接踢掉,否则参数修改会被拒绝 db2 force application all db2 terminate db2 deactivate db sample # userexit on:日志写满时调用 db2uext2 把它归档出去 db2 update db cfg for sample using userexit on # logretain on:日志写满后不再复用,保留在日志目录等待处理 db2 update db cfg for sample using logretain on # trackmod yes:打开页面级修改跟踪,增量备份才有判断依据 db2 update db cfg for sample using trackmod yes # 改完必须回读确认,光看命令返回成功不算数 db2 get db cfg for sample | grep -Ei "userexit|logretain|trackmod"这三个参数的关系值得说清楚。logretain决定日志「留不留」,userexit决定日志「谁来搬」,trackmod决定增量备份「有没有依据」。老版本 DB2 里它们就是三个独立的开关,组合起来才有完整语义;后来版本把归档方式收敛成LOGARCHMETH1/LOGARCHMETH2这类参数,语义其实是一样的——指定一个出口,让日志出了数据库就有人接手。所以看到老文档里写userexit on加logretain on,不要理解成两个都要开的冗余配置,它们是两件事。
2.2 db2uext2:DB2 把日志归档「外包」出去的那个口子
备份软件的 Agent 装完之后,第一件要做的事是跑/usr/openv/netbackup/bin/db2_config。这个脚本干的活很朴素:把备份系统自带的那份db2uext2可执行程序,复制到<db2instance>/sqllib/adm目录下。为什么要放这个位置?因为 DB2 在需要归档日志时,会去实例的sqllib/adm下找名为db2uext2的程序,找到就调用,找不到就在db2diag.log里记一笔然后干瞪眼。
DB2 调用它时传的是一组命令行参数,大致包含动作类型(归档、取回、删除)、实例路径、数据库名、分区号、日志所在目录、日志文件名。程序内部再去读配置文件,判断这个数据库该走哪个策略、哪个 Schedule,最后调备份系统的接口把日志文件送出去。这个设计的精妙之处在于解耦:DB2 完全不知道磁带、存储单元、保留周期这些东西,它只知道「我这儿有个日志文件,你拿去处理,成功返回 0,失败返回非 0」。
失败路径最值得警惕。db2uext2返回非 0 时,DB2 不会替你兜底——日志文件继续留在日志目录里,下一次写满继续调用,继续失败,目录越来越大,直到某个时刻数据库报日志满并挂起。这是归档模式最常见的事故形态:备份看起来「每天都在跑」,但归档出口早就断了,谁也没注意。所以配完这个环节,第一件验证的事不是去备份服务器看作业状态,而是直接看实例的日志目录有没有堆积文件:
# 切到实例用户,看归档日志目录是否在持续堆积 su - db2inst1 ls -lt $HOME/sqllib/logdir 2>/dev/null | head # 更直接的证据在诊断日志里,搜 db2uext2 相关的返回码 grep -i "db2uext2" $HOME/sqllib/db2dump/db2diag.log | tail -20如果日志目录里的文件数量稳定在某个水位不再增长,说明出口是通的;如果一路涨上去,先别怀疑备份策略,先确认db2uext2有没有真的被复制到sqllib/adm,以及它读的那份配置文件里的策略名和备份服务器上实际存在的名字是否一致。
2.3 trackmod:增量备份的开关和一个隐藏前提
trackmod打开之后,DB2 会在表空间层面维护一张页面级的修改跟踪位图。做增量备份时,它不再扫全库,而是只读被标记过的页。这就是增量备份能比全备快一个数量级的原因——省掉的是 I/O,不是压缩。
但这里有个坑,很多人开完trackmod就直接去跑增量,然后报错。原因是增量备份需要一个基线:trackmod只负责记录「哪些页改过」,它不知道「从哪个点开始算改过」。这个起点只能由一次完整的数据库备份来建立。所以顺序是死的:先trackmod yes,再做一次全备,之后才能谈增量。
DB2 的增量还分两种,语义完全不同:
| 备份类型 | 依据的基线 | DB2 关键字 | 恢复时需要的前置 |
|---|---|---|---|
| 完全备份 | 无 | 无 | 无 |
| 累积增量 | 最近一次全备 | INCREMENTAL | 对应全备 |
| 差异增量 | 最近一次任意备份 | INCREMENTAL DELTA | 对应全备 + 中间链条 |
这个区别直接决定了恢复链的长度。累积增量恢复时只需要「全备 + 最后一次累积」,链条短、恢复快、占空间大;差异增量恢复时要「全备 + 每一个差异备份」,链条长、备份快、恢复慢。生产上常见的折中是每周一次全备、每天一次累积、高峰期加差异,具体怎么排取决于你的恢复时间目标,而不是备份窗口。
3. Vendor I/O Library:一条 load 命令如何把存储控制权交出去
db2 backup database sample to /tmp这条命令里,DB2 自己管文件读写,备份片就是磁盘上的普通文件。可一旦加上load /usr/openv/netbackup/bin/nbdb2.sl,性质就变了:DB2 不再往文件系统写,而是把整个备份介质的读写接口交给这个共享库。理解了这一层,才能理解为什么 restore 也必须带同样的load——读和写走的是同一条路。
3.1 db2 backup ... load 到底发生了什么
DB2 在备份开始前动态加载指定的库文件,然后按约定好的函数名去找入口点:初始化、打开、写、读、定位、截断、关闭、删除。备份过程中每一个数据块(extent)的产生,都变成对这些函数的一次调用。库内部再把块转交给备份系统的客户端进程,最终落到存储单元上。DB2 全程不知道数据去了磁带还是磁盘,它只知道「我调 write,你返回成功」。
这就解释了两个看似奇怪的现象。第一,备份产物在文件系统里找不到:因为它压根没写成文件。第二,restore 必须带同一个库:镜像的格式由这个库定义,DB2 自己解不开。
库文件的选择完全取决于实例位数,选错立刻失败:
# 先确认实例位数,这决定了库文件名 db2level | grep -i "bit" # 平台与位数的对照关系(按实际安装路径确认) # Linux / Solaris 32 位 -> nbdb2.so # Linux / Solaris 64 位 -> nbdb2.so64 # AIX / HP-UX 32 位 -> nbdb2.sl # AIX / HP-UX 64 位 -> nbdb2.sl64 ls -l /usr/openv/netbackup/bin/nbdb2.s*32 位实例加载 64 位库,报的错通常很含糊,db2diag.log里只有一句「加载库失败」,不会告诉你位数不匹配。所以换机器做异机恢复时,第一件事是对比源端和恢复端的db2level输出,位数不一致的话,要么装对应位数的客户端,要么整个实例重装——没有第三条路。
3.2 db2.conf:块结构与解析规则
db2.conf是db2uext2和备份脚本共同读取的配置文件,它的结构比一般 ini 更简单:由若干个OPER到ENDOPER的块组成,每个块以DATABASE开头,声明这个块服务于哪个数据库。解析是顺序的,遇到DATABASE就切换上下文,所以同一个数据库可以出现在多个块里,靠OBJECTTYPE区分用途。
# 数据备份块:restore 时靠它找到备份镜像 DATABASE BI OBJECTTYPE DATABASE POLICY DB2_Backup SCHEDULE Default-Application-Backup CLIENT_NAME gdccas670 ENDOPER # 归档日志块:rollforward 时靠它把日志取回来 DATABASE BI OBJECTTYPE ARCHIVE POLICY User_Backup SCHEDULE UserBackup CLIENT_NAME gdccas670 ARCFUNC SAVE #ARCFUNC COPY #ARCDIR /home/db2inst1/arcdir #RETDIR /home/db2inst1/arcdir ENDOPER逐项说清楚。DATABASE指定数据库名,注意它只是个标签,DB2 传进来的库名要能和它对上,否则整块配置被跳过。OBJECTTYPE只有DATABASE和ARCHIVE两种,前者对应备份镜像,后者对应归档日志,两者走的策略类型完全不同——数据块用 DB2 类型的策略,归档块用 Standard 类型的策略,混了就会在取日志时失败。POLICY和SCHEDULE必须和备份服务器上真实存在的名字逐字符一致,大小写敏感,多一个下划线都算错。CLIENT_NAME只有异机恢复时才需要,作用是告诉备份服务器「这份数据属于谁」,后面单独讲。
ARCFUNC决定日志往哪去:SAVE表示归档到备份系统的存储单元,COPY表示只在本地换一个目录,此时ARCDIR是归档目标、RETDIR是取回源目录。大多数生产环境用SAVE,因为日志和数据在同一套介质管理里,恢复时不用去翻另一台机器的磁盘。原文里把ARCDIR和RETDIR注释掉,正是因为SAVE模式下这两个参数不参与。
DATABASE这个名字在备份时其实是可选项,脚本里拼的db2 backup命令已经指明了库名;但恢复时必须写对,因为 restore 的时候没有别的途径告诉配置「我要恢复哪个库」。
3.3 db2_backup 脚本:环境变量从哪来
这个脚本不是人手敲的,是备份服务器按 Schedule 触发的。触发器启动脚本之前,会往环境里注入一批变量:客户端名、服务器名、策略名、计划名,以及三个互斥的调度类型标志DB2_FULL、DB2_CINC、DB2_INCR。脚本的全部职责,就是把这三个标志翻译成 DB2 备份命令的选项。
#!/bin/sh # 备份系统注入的调度标志,三者互斥,同一时刻只可能有一个为 1 echo "DB2_FULL=$DB2_FULL" # 完全备份 echo "DB2_CINC=$DB2_CINC" # 累积增量,基于最近一次全备 echo "DB2_INCR=$DB2_INCR" # 差异增量,基于最近一次任意备份 MY_LIB=/usr/openv/netbackup/bin/nbdb2.sl # 必须与实例位数匹配 MY_DB2=DWDB MY_USER=dwccbxm # 实例用户,不是 root # 把调度类型翻译成 DB2 的备份选项 if [ "$DB2_FULL" = "1" ] ; then MY_SCHED="" elif [ "$DB2_CINC" = "1" ] ; then MY_SCHED="INCREMENTAL" elif [ "$DB2_INCR" = "1" ] ; then MY_SCHED="INCREMENTAL DELTA" # 差异增量必须显式带 DELTA else MY_SCHED="" fi CMD_LINE="db2 BACKUP DATABASE $MY_DB2 ONLINE $MY_SCHED LOAD $MY_LIB" echo "Executing: $CMD_LINE" su - $MY_USER -c "$CMD_LINE" # 把 DB2 的退出码原样返回,否则作业会被误判为成功 RETURN_STATUS=$? exit $RETURN_STATUS有几处必须自己盯住。ONLINE是写死的,意味着备份期间数据库保持可用,这也反过来要求归档日志模式已经开好——离线备份不需要日志,在线备份没有日志就是废的。su - $MY_USER -c不能省,db2 backup必须在实例用户身份下执行,用 root 跑出来的备份文件属主不对,恢复时会卡权限。最要命的是最后那个exit $RETURN_STATUS,如果脚本忘了返回真实状态码,备份服务器会认为作业成功,等你真要恢复的时候才发现根本没有可用的镜像。
原文脚本里DB2_INCR那条分支是空的,直接落到else里把MY_SCHED置成空串。后果是:计划里配置成「差异增量备份」的作业,实际执行的是完全备份。这种错误在监控面板上完全看不出来——作业状态是绿的,只是每次备份的时间和容量都跟全备一样大,等到发现磁带用量异常才回头查。写脚本的时候,每个分支都要有明确赋值,宁可让它报错,也别让它悄悄降级成全备。
4. 异机恢复的三个开关:No.Restrictions、CLIENT_NAME 与 to 参数
同机恢复只要能连上备份服务器就够了,因为是「自己恢复自己」,权限、索引、路径都是现成的。异机恢复是「用 B 机器去要 A 机器的备份」,本质上是一次跨客户端的取数请求,所以必须额外打开三道门:权限、归属、落点。
4.1 No.Restrictions:一个空文件就是授权
备份服务器默认是保守的:客户端只能恢复自己名下的镜像,不能去读别人的。这个规则防的是越权取数,代价就是异机恢复必须先显式授权。授权方式简单到有点反直觉——在 master server 上用 root 建一个空文件:
# 粗粒度:允许任意客户端恢复任意客户端的备份 touch /usr/openv/netbackup/db/altnames/No.Restrictions # 细粒度:只允许指定的几台机器恢复源客户端的备份 # 文件名 = 源客户端名,文件内容 = 允许发起恢复的客户端名,一行一个 echo "restore_host_b" > /usr/openv/netbackup/db/altnames/gdccas670No.Restrictions这个文件不需要任何内容,存在本身就是开关。它读的是文件系统状态,改完立刻生效,不用重启服务。细粒度方案更适合生产:按源客户端建文件,文件里列出允许恢复的目标机器,恢复演练做完把文件删掉即可。
注意这个文件的安全含义。No.Restrictions一旦存在,所有客户端都能读所有备份,等于把备份库变成了公共读资源。临时演练开一下可以,长期挂着就是给自己埋雷——尤其在有合规要求的环境里,备份数据的访问范围本身就是要被审计的。
4.2 CLIENT_NAME:告诉备份服务器去谁家取数据
备份镜像在服务器上是按客户端名索引的。恢复端发起请求时,默认用自己的主机名去查索引,异机场景下当然查不到,于是报「找不到备份」。CLIENT_NAME就是用来覆盖这个默认值的。
关键在于:DATABASE块和ARCHIVE块都要加。只加数据块的话,restore 能正常取到全量镜像,数据库也还原了,但接着执行 rollforward 时,取归档日志的那一步仍然用自己的主机名去查,依然找不到,最终报日志缺失。这个错误出现得特别晚——前面所有步骤都成功,就卡在最后一步前滚,很容易被误判成备份本身有问题。
# 两个块都要写 CLIENT_NAME,漏一个就会在 rollforward 阶段翻车 DATABASE BI OBJECTTYPE DATABASE POLICY DB2_Backup SCHEDULE Default-Application-Backup CLIENT_NAME gdccas670 ENDOPER4.3 taken at 与 to:定位镜像和重定向落盘
taken at指定恢复哪个时间点的备份,to指定恢复到哪个目录。这两个参数加上without prompting,构成异机恢复的完整骨架。定位时间点的办法是直接列镜像:
# 切到实例用户执行,以源客户端身份列出 DB2 类型的备份镜像 su - db2inst1 bplist -C gdccas670 -t 18 -R / # 输出路径里的时间戳就是 taken at 的取值 # 例如 /DB2/BI/node0000/20050226120711/BI.0.xxx.node0000.642.20050226120711.4 # 取 20050226120711 作为时间点 # 恢复到指定版本,库不存在时用 to 指定落盘目录 db2 restore db bi load /usr/openv/netbackup/bin/nbdb2.sl \ taken at 20050226120711 to /db_dir without promptingwithout rolling forward这个选项要看备份类型来定。离线备份期间数据库是停的,没有产生新日志,所以恢复完就应该是可用的完整状态,此时必须带without rolling forward,否则数据库会停在 rollforward pending 状态起不来。在线备份正好相反:镜像只是备份开始那一刻的快照,后面的事务都在日志里,必须紧接着执行 rollforward 才能追平,此时不能带这个参数。
# 在线备份恢复后,前滚到最新 db2 rollforward db bi to end of logs and complete # 或者前滚到指定时间点(老版本没有 using local time 选项) db2 rollforward db bi to 2005-02-26-12:07:11 using local time and completeusing local time这个选项在不同版本里有无不一。它的作用是告诉 DB2「我给的时间是操作系统本地时间,你自己换算成 UTC 去匹配日志」。如果版本不支持这个选项,而恢复端的系统时区又和备份端不一致,那么你写的时间点会被当成 UTC 解释,算出来的位置可能偏几个小时,表现出来就是「日志明明有,就是找不到对应时间点的那一条」。
5. 避坑与排查:五条让恢复卡住的细节
5.1 参数改完不生效
现象:db2 update db cfg返回成功,db2 get db cfg也显示是 on,但备份仍然报日志不可用。
原因:userexit、logretain这类参数修改时要求数据库处于静止状态。如果还有应用连接挂着,参数变更并不会真正作用到运行中的实例上,或者直接报 SQL1363N 之类「必须断开所有连接」的错误。原文里那句「必须在所有应用程序都与此数据库断开连接之后,更改才会生效」,说的就是这个。
解决:按顺序执行db2 force application all、db2 terminate、db2 deactivate db sample,然后再改参数,改完用db2 get db cfg回读确认。如果生产环境不能停,就安排一个维护窗口,别在业务高峰期改。
5.2 打开 trackmod 后第一次增量备份直接失败
现象:全备能跑,一切到db2 backup ... incremental就报错,提示没有可用的基准。
原因:trackmod只是打开了跟踪位图,增量需要一次完整备份作为起点。打开参数之后如果一次全备都没做过,DB2 不知道该「从哪个点开始算改动」。
解决:打开trackmod yes之后立刻手工跑一次全备,不要等调度系统自动触发。这次的备份就是后续所有增量的基线,也决定了恢复链的起点。
5.3 异机恢复报找不到镜像
现象:restore 一开始就失败,备份系统提示找不到该客户端的文件,或者 DB2 报无法访问备份镜像。
原因:三种可能——CLIENT_NAME只写在DATABASE块、ARCHIVE块漏了;授权文件没建;或者POLICY、SCHEDULE的名字与备份服务器上实际存在的对不上(大小写、下划线都算)。
解决:先用bplist -C <源客户端> -t 18 -R /验证镜像能否被枚举出来,这一步能过说明授权和客户端名是对的;再逐个核对db2.conf里的POLICY和SCHEDULE,最好从备份服务器控制台上把名字复制出来,别手敲。
5.4 rollforward 报时间点超出日志范围
现象:restore 成功,rollforward 报找不到某个日志文件,或者提示时间点无法到达。
原因:归档日志没有全部取回,链条中间缺了一段;或者恢复端系统时间与备份端有偏差,to <timestamp>算出来的位置偏了。老版本没有using local time选项时,这个偏差会直接导致时间点解释错误。
解决:先确认ARCHIVE块的策略配置正确,能取回全部归档日志;再检查两端的系统时间差,不支持using local time的版本要手工把时间戳按差值调整。恢复演练最好选在业务低峰,这样即使多试几次也不影响生产。
5.5 restore 一执行就返回错误
现象:命令刚发出去就失败,db2diag.log里只有一句「加载库失败」,没有更多信息。
原因:库文件位数与实例不匹配,或者路径写错了。32 位实例加载 64 位库是最常见的情况,尤其在异机恢复时——源端是 64 位,恢复端装的是 32 位客户端。
解决:用db2level | grep -i bit对比两端,确认库文件名后缀(.so还是.so64,AIX/HP-UX 是.sl还是.sl64),再用ls -l确认文件存在且可执行。位数不一致只能重装客户端,没有绕过办法。
6. 设计取舍:为什么把备份介质的控制权交出去反而更安全
DB2 完全可以自己管备份文件,它也确实提供to /tmp这种纯文件系统备份。但生产上几乎没人这么用,原因不是文件备份不好,而是它解决不了备份真正难的那部分问题:介质管理、保留周期、跨机器定位、离线存放、磁带轮转、审计追溯。这些是备份系统的专职工作,数据库去做只会做出一个半成品。
所以 DB2 选了「把自己的 I/O 接口打开」这条路。load一个 vendor library,备份数据的读写就走别人的通道;userexit打开,日志归档就交给外部程序。代价很明确——恢复链路从此依赖外部目录。这就是为什么异机恢复需要CLIENT_NAME这种看似奇怪的东西:镜像索引不在数据库里,而在备份服务器的目录库里,数据库只能告诉它「去查谁的名下」。
这个取舍还带来一个隐性成本:版本耦合。库文件的位数、备份系统的客户端版本、DB2 实例版本,三者必须互相兼容。升级其中任何一个,都要重新验证恢复链路能不能走通。真正稳妥的做法不是升级完看备份作业是否还是绿的,而是定期做恢复演练:restore到一个临时目录,rollforward到某个时间点,然后db2 connect上去查几张表确认数据对得上。备份的成功率是给别人看的,恢复的可用性才是给自己兜底的。
我踩过一次印象很深的坑:有一次换了台机器做恢复,前面所有步骤都顺利,卡在 rollforward 上,日志怎么都取不回来。查了两个小时才发现ARCHIVE块里的CLIENT_NAME忘了加,数据块加了所以 restore 能过,日志块没加所以前滚必挂。从那以后我每次做异机恢复,都强制先把两个块并排贴出来对照一遍,再开始敲命令。希望帮到你。
本文还有配套的精品资源,点击获取