所谓数据库损坏也就是说数据库中的某些页面被损坏了。对于DB2而言,页面是操作系统分配的最小数据单元,它在主内存与任何其它辅助存储(比如硬盘驱动器)之间传输。坏页问题可能会对系统造成严重的性能冲击。在某些情况下,它可能会导致频繁的数据库宕机。
一、坏页的产生
数据库坏页可能在页面的写入、读取、存储、传输或处理过程中发生,这会向原始数据引入一些非计划中的更改。数据库坏页问题的一些常见原因:
文件系统的损坏。
硬件故障。
内存损坏。
DB2 BUG。
I/O和网络问题。
不正确的应用程序编码。
缓冲池 (sqldPage) 和文件系统中存储的页面的值不一致。
虽说损坏问题由各种原因而致,确切地查明是什么导致了数据损坏是极具挑战的。当 DB2检测到一个页面损坏时,它会停止所有处理,将数据库关闭,阻止进一步的数据损害或丢失。
二、坏页的类型
1.数据页面损坏
2017-03-14-10.13.05.261082-360 I3442A358 LEVEL: Error PID : 393470 TID : 1 PROC : db2pfchr 0 INSTANCE: inst1 NODE : 000 FUNCTION: DB2 UDB, buffer pool services, sqlbReadAndReleaseBuffers, probe:13 RETCODE : ZRC=0x86020001=-2046689279=SQLB_BADP "page is bad" DIA8400C A bad page was encountered. 2017-03-14-10.13.05.264301-360 I3801A437 LEVEL: Error PID : 393470 TID : 1 PROC : db2pfchr 0 INSTANCE: inst1 NODE : 000 FUNCTION: DB2 UDB, buffer pool services, sqlbReadAndReleaseBuffers, probe:13 DATA #1 : String, 158 bytes Obj={pool:9;obj:20;type:0} State=x27 Parent={9;20}, EM=1120, PP0=1152
如上错误消息所示,DB2 遇到了一个表空间 ID 为 9、表 ID 为 20 的坏页。坏页的对象类型被标记为0,表明有数据页面损坏。
可以通过查询目录表,确定哪个表有损坏的页面:
db2 "select char(tabname,30),char(tabschema,30) from syscat.tables where tableid=20 and tbspaceid=9";
2.索引页面损坏
2017-03-14-10.35.50.952434+000 I29308542A2532 LEVEL: Severe PID : 1175792 TID : 33926 PROC : db2sysc 0 INSTANCE: inst1 NODE : 000 DB : SAMPLE APPHDL : 0-7 APPID: *LOCAL.inst1.120130013528 AUTHID : TP0ADM EDUID : 33926 EDUNAME: db2redow (TP0) 0 FUNCTION: DB2 UDB, buffer pool services, sqlb_verify_page, probe:3 MESSAGE : ZRC=0x86020001=-2046689279=SQLB_BADP "page is bad" DIA8400C A bad page was encountered. DATA #1 : String, 64 bytes Error encountered trying to read a page - information follows : DATA #2 : String, 23 bytes Page verification error DATA #3 : Page ID, PD_TYPE_SQLB_PAGE_ID, 4 bytes 23046981 DATA #4 : Object descriptor, PD_TYPE_SQLB_OBJECT_DESC, 72 bytes Obj: {pool:9;obj:11076;type:1} Parent={8;11076}
如上错误消息所示,DB2 遇到了一个表空间中ID 为9,索引ID 为1322的坏页。该索引所在表的表空间 ID 为 8,表 ID 为 1215。坏页的对象类型被标记为 1,表明有索引页面损坏损坏。
可以通过查询目录表,确定哪个表的那个索引有损坏的页面:
db2 "select char(a.tabname,30) tabname,char(a.tabschema,30) tabschema,b.indname from syscat.tables a,syscat.indexes b where a.tabname=b.tabname and a.tabschema=b.tabschema a.tableid=1215 and a.tbspaceid=8" and b.iid=1322;
3.CBIT损坏
CBIT是DB2使用的一种方法,用于确认从磁盘读入缓冲池的一个页面不是一个部分页面,或者没有从某种形式的损坏改变而来。
CBIT 背后的基本思想是,在写入页面之前将页面上每个扇区(512 字节)的一个位数设置为相同的值。在DB2将一个页面刷新到磁盘上之前,计算校验和并记录在页面上。在缓冲池中读回一个页面时,重新计算这一校验和,并根据存储的值对其进行检查。如果有些位数不同,表明有一个部分页面写操作或磁盘损坏。
2017-03-14-10.45.17.559235-240 I1104A2616 LEVEL: Severe PID : 2551866 TID : 1 PROC : db2pfchr INSTANCE: inst1 NODE : 000 FUNCTION: DB2 UDB, buffer pool services, sqlbVerifyCBITS, probe:1110 MESSAGE : ZRC=0x86020019=-2046689255=SQLB_CSUM "Bad Page, Checksum Error" DIA8426C A invalid page checksum was found for page "". DATA #1 : String, 64 bytes Error encountered trying to read a page - information follows : DATA #2 : String, 95 bytes CBIT verification error bitExpected is 0, userByte is 33, sector 7 (from head of page, 0 based) DATA #3 : Page ID, PD_TYPE_SQLB_PAGE_ID, 4 bytes
注意,如果损坏不触及存放CBIT的字节,在DB2以外对页面做出的更改(文件系统故障、磁盘故障等)不会被注意到。CBIT 错误(校验和错误)多半是因初始写或读期间的硬件或 OS 错误引起的。
4.事务日志损坏
DB2中事务日志只是对数据库中发生的所有变更的一个记录。要跟踪事务做出的变更,需要一个方法来对数据变更和日志记录加时间戳。在DB2中,这一时间戳机制是使用一个日志序列号 (LSN) 执行的。如果遇到一个损坏的日志,可能会在db2diag.log中有如下类似的错误消息。
2017-03-14-11.37.21.143998+120 I8673583A553 LEVEL: Severe PID : 2498668 TID : 27358 PROC : db2sysc 56 INSTANCE: inst1 NODE : 056 DB : SAMPLE APPHDL : 998-22947 APPID: *N998.inst1.100607192315 AUTHID : LOADDSF EDUID : 27358 EDUNAME: db2agntp (SAMPLE) 56 FUNCTION: DB2 UDB, data protection services, sqlpgrlg, probe:291 DATA #1 : < reformatted > Error -2028994519 when reading LSN 00000B1C261B4FD3 from log file S0119292.LOG tellMe 1 dpsAcbFlags 1000 setSkipOutputBuf 0 2017-03-14-11.37.21.144202+120 I8674137A487 LEVEL: Severe PID : 2498668 TID : 27358 PROC : db2sysc 56 INSTANCE: inst1 NODE : 056 DB : SAMPLE APPHDL : 998-22947 APPID: *N998.inst1.100607192315 AUTHID : LOADDSF EDUID : 27358 EDUNAME: db2agntp (SAMPLE) 56 FUNCTION: DB2 UDB, data protection services, sqlpgrlg, probe:291 DATA #1 : < preformatted > HeadLsn 00000B1B8B996EB3, copyLookForLsn 00000B1C261B4FD3 2017-03-14-11.37.21.158065+120 I8675153A549 LEVEL: Error PID : 2498668 TID : 27358 PROC : db2sysc 56 INSTANCE: inst1 NODE : 056 DB : SAMPLE APPHDL : 998-22947 APPID: *N998.inst1.100607192315 AUTHID : LOADDSF EDUID : 27358 EDUNAME: db2agntp (SAMPLE) 56 FUNCTION: DB2 UDB, data protection services, sqlptudo, probe:1010 RETCODE : ZRC=0x87100029=-2028994519=SQLP_BADLSN "Invalid LSN value." DIA8538C An invalid log sequence number (LSN), the value was "".
如果日志被损坏了,在数据库前滚、崩溃恢复和 HADR 日志回复等需要回应日志的情况下可能会引发严重的问题。数据库前滚和崩溃恢复能够维护数据库中的一致性。它通过应用记录在数据库日志文件中的事务来恢复数据库。前滚是在还原数据库或表空间备份映像之后调用的一个过程。HADR可以提高数据的安全性。
二、排查坏页
1.db2dart
db2dart的默认行为是要检查整个数据库。在这种情况下只有数据库名称必须提供。默认情况下,db2dart会创建一个名为 databaseName.RPT 的报告文件。对于单分区数据库环境,文件会被创建到了当前目录中。对于多分区数据库环境,则文件会被创建到了诊断目录的一个子目录中。子目录名为DARTnnnn,其中 nnnn 是分区号。
如果数据库很大,且只对一个表空间感兴趣,可以使用/TS选项。使用该选项时,必须在命令行上提供表空间 ID(通过指定/TSI参数)或者可以根据db2dart提示输入该 ID。类似的,可以使用/T选项检查一个表及其相关对象(LOB 和索引等)。使用该选项时,必须提供表名称或对象 ID 以及表所在的表空间的ID。要确定一个表的对象 ID 和表空间 ID,可以查询 SYSIBM.SYSTABLES 目录表。
eg:db2dart <dbname>
2.inspect
inspect命令类似于db2dart命令。它允许检查数据库、表空间和表的架构完整性,方法就是检查数据库的页面,看页面是否一致。两个命令之间的显著区别在于,在运行db2dart之前需要停用数据库,而inspect需要一个数据库连接,且可在同时有多个活跃的数据库连接时运行。
eg:db2 "inspect check database results keep inspect.rel" ;
三、修复坏页
根据之前的讨论,可以通过db2dart和inspect工具识别损坏页面。通过查看生成的报告文件,可以估计损坏范围。根据数据损坏的数量和涉及到的复杂度,需要决定最好的恢复计划。
1.从数据损坏中修复
a.如果有数据库备份,还原数据库并前滚到日志的末尾。如果可行的话这是最简洁的方法,如果数据库很小时首选该方法。
b.还可以还原表空间并前滚到日志的末尾。如果损坏是小范围的,这可能是最好的方法。
c.如果没有有效的备份映像以及重新创建表的任何方式,可以使用db2dart以及/ddel来挽救数据。在此之前,需要使用db2look查找表的DDL。
参考命令:
db2look -d <dbname> -e -z <schema_name> -t <table_name> -o <output_file_name>
db2dart $DBNAME /DDEL /RPT 根据提示输入表信息。
2.从索引损坏中修复
如果在 db2diag.log 和/或 db2dart 报告中有索引损坏的迹象,可以使用db2dart将索引标记为无效,并去除坏的索引然后重建索引。db2dart有一个可将索引标记为无效且使其处于删除暂挂状态的选项。可以使用db2dart /MI将受损的索引标记为无效。
参考命令:
db2dart <dbname> /MI /TSI 9 /OI 1322。
3.从 CBIT 错误中修复
要修复 CBIT 损坏错误,通过至少在受损表上运行db2dart(最好对整个数据库运行它)来检查问题的严重程度。可以根据CBIT错误是在数据页面上还是索引上来决定上述讨论的方法。
4.从日志损坏中修复
可以使用db2cklog检查归档日志文件的有效性,确定在数据库或表空间的前滚恢复、崩溃恢复、HADR回放日志的过程中是否可以使用日志文件。
a.如果在前滚过程中由于坏的日志而发生错误,可以做的第一件事就是检查 DB2 在为哪个日志文件报告错误。db2flsn可用于返回包含日志记录的文件的名称,该日志记录根据指定日志序列号 (LSN) 识别。因此如果在db2diag.log中有'bad_lsn'消息,可以使用db2flsn查找相应的日志文件。如果日志损坏是因为一个缺失的日志文件或一个不正确的日志链上的一个日志文件,则可以查找正确的日志文件。如果前滚操作由于一个损坏的日志而失败,则可以选择时间点前滚。前滚操作的指定时间点必须等于或晚于最小恢复时间。最小恢复时间 (MRT) 是当一个数据库一致时前滚过程中最早的时间点。如果不能将日志至少前滚到 MRT,就需要联系 IBM 支持团队获取协助。
b.如果在崩溃恢复中因一个坏的日志文件而出现问题,需要还原最新的备份或联系 IBM 支持团队获取协助。
c.如果在HADR备用数据库上,在日志回复期间,备用服务器可能会因为一个坏的日志而崩溃。可以检查备用服务器上的 db2diag.log,找到坏的日志文件并尝试从主服务器上传送该日志文件的一个良好备份。一旦有了一个好的日志文件,就可以启动 HADR:
在备用节点上对 HADR 运行 start:db2 start hadr on db <dbname> as standby。
在主节点上对 HADR 运行 start:db2 start hadr on db <dbname> as primary。
如果上述尝试失败了,可能需要使用来自主服务器上的全新备份重新配置 HADR,并在备用服务器上还原它。
四、表坏页批量修复
1.a.txt
tablename tableid tabspaceid
2.dart script:
DBNAME=sample
SCHEMA=db2inst1
RPTDIR=/backup/dartdata
sed 's/,/ /g' a.txt|while read line
do
echo $line | awk -F"," '{
tablename=$1
tableid=$2
tabspaceid=$3
printf("%s,%s,0,999999\nY\n%s\n",tableid,tabspaceid,tablename)
}' >inputfile
if [ -s inputfile ]
then
db2dart $DBNAME /DDEL /RPT $RPTDIR /RPTN ${tablename}.DEL < inputfile
fi
done
3.import script:
DBNAME=sample
SCHEMA=db2inst1
RPTDIR=/backup/dartdata
db2 connect to $DBNAME
sed 's/,/ /g' a.txt|while read line
do
echo $line | awk -F"," '{
tablename=$1
tableid=$2
tabspaceid=$3
db2 "import from /backup/dartdata/${tablename}.DEL of del commitcount 100000 insert into ${SCHEMA}.${tablename}";
done