ParnassusData 诗檀软件 Oracle 数据库修复专家 Maclean Liu Oracle 数据块损坏知识 Know More about Oracle Database Block Corruption
古希腊的 Delphi( 世界中心 ), 屹立着 Parnassus Mount( 诗檀山 ), 山上有一座阿波罗神庙, 庙中住着女祭司 (Oracle)
Oracle Data Block Oracle 中最小 I/O 单位 RDBMS 具体存放数据的区域 一个 Block 对应磁盘上的一定区域, 可能跨多个磁盘 微观的说, 空间分配以块为单位 数据块 数据库
Oracle Block 的构成 主要由 Cache 层, 事务层和数据层组成 Cache 层 事务层 数据层 Tail
Oracle Block 构成 Cache 层 Kcbh Cache 层包含用来检测块损坏的信息 Cache 层的 chkval_kcbh 用来确认, 上一次的写入到磁盘, 到现在读取出来磁盘上的 Block 的内容是否一致 Cache 层存放的信息 本数据块存放何种数据 type_kcbh(data,index) RDBA 和格式版本 本数据块的更新情况 几乎所有的数据块都有 Cache 层,KCBH
Oracle Block 构成事务层 ktbbh 在本数据块上执行着的或者执行过的事务信息 存放 SCN (System Change Number) ITL 一种事务列表 ktbbhitl 存放的数据类型 ktbbhtyp, KDDBTINDEX 代表索引, KDDBTDATA 代表数据 ktbbhflg, MSSM or ASSM, KTBFONFL or KTBFBSEG 不是每个块都有 ktbbh, 例如 Undo block 就没有 ktbbh
ITL(Interested Transaction List) XID:ktbitxid 事务 ID UBA: 指向事务最后一次修改对应的 Undo Flag: 是否提交了, 提交的情形 C B U T Lck: 在本数据块锁住的行数 Scn/Fsc: commit scn 或 control scn _ktbitfsc _ktbitwrp ktbitbas
Oracle Block 构造数据层 数据的存储信息 数据头信息 kdbh Table Direcotry kdbt, 针对簇表有多条记录 Row Directory kdbr, 记录了每一个 row piece 的块内偏移量 Row piece 数据行片, 一个 row piece 最多 255 字段, 因为 CC column count 是单字节最大 0xFF 根据存储数据类型的不同, 其结构也会有区别
kdbh 数据头信息 kdbhflag bit 位, KDBHFFK 0x01 Flushable Key kdbhntab Number of tables in the table index kdbhnrow Number of rows in the row index kdbhfrre first FRee Row index Entry kdbhfsbo Free Space Beginning Offset kdbhfseo Free Space Ending Offset kdbhavsp AVailable SPace in the block kdbhtosp TOtal Space that will be available
Table Directory kdbt kdbtoffs 对应表记录的偏移量 kdbtnrow 对应表在本块的行数 通常情况下一张表仅仅有一条 kdbt 记录 当时簇表 Cluster Table 情况下, 一个块中存放多张表记录,KDBT 中有多条记录
Row Directory kdbr 一个 kdbr 数组, 每一个成员为 2 个字节的 signed bytes 每一个成员代表一行数据的块内相对偏移量 相对偏移量从数据层的起始位置开始 相对偏移量 + 环境偏移量 = 块内的绝对偏移量
数据块的大小 默认数据块的大小取决于建库时的 DB_BLOCK_SIZE 可选项为 2k,4k,8k,16k,32k 默认数据块大小在建库后不可修改 可以混用非标准块大小的表空间, 例如 8k 默认块大小下建 16k 大小的表空间 注意 : 非官方的看法是 8k 是最稳定, 测试最多的块大小 少数情况下非 8k 的 Block Size 更容易触发部分 BUG
数据块的损坏 什么是 Oracle 数据块损坏? 根据不同的情况, 可以分成三种 : 物理损坏 Physical Corrupt 逻辑损坏 Logical Corrupt 写丢失 Lost Write
造成数据块损坏的原因 存储 / 硬盘断电, 机械老化故障 卷管理软件或文件系统 bug OS I/O API bug 人为的误操作 dd/lvm/bbed Oracle RDBMS/ASM 自身的 bug, 越来越少了
存储 / 硬盘故障 右图为 36 个月的硬盘留任率 一些案例 : ORA-1578 Transient Corruption Caused by Parity Error on EMC DMX4 (Doc ID 1486903.1) ORA-1578 Data Block Corruptions when using EMC Storage. Blocks with 0xc9 byte (Doc ID 1323108.1) Database Corruption due to Lost IO on Hitachi storage. ORA-600 [kdsgrp1] ORA-1499 ORA-1410 ORA- 600 [3020] (Doc ID 1512717.1)
卷管理软件或文件系统 bug 一些案例 : ORA-354 Redo log corruption when using Xisgo Driver (Doc ID 1498389.1) ORA-1578 ORA-353 ORA-19599 Corrupt blocks with zeros when filesystemio_options=setall on ext4 file system using Linux (Doc ID 1487957.1) ORA-1578 Misplaced blocks against datafiles stored in NFS filers (Doc ID 1525108.1) ORA-1578 ORA-354 ORA-600 [3020] Misplaced blocks by Symantec / Veritas after adding LUN (Doc ID 1323532.1) ORA-1578 Block overwritten with string DiskDescription cyl alt hd sec when using Symantec / Veritas (Doc ID 1313454.1) Block Corruption (ALL ZERO) detected after reclaiming space on Veritas Filesystem (Doc ID 1587427.1) 遇到过的 VxCFS 问题 : 32 位指针问题导致内存泄露 卷同步存在漏洞, 将实际未完成同步的数据返回给 Oracle 使用
数据块损坏的影响 乐观 : 不发生显性错误, 或仅少量 SQL 报错 悲观 : 业务几乎不可用, 大量 ORA-1578 ORA- 8103 ORA-1410 错误出现, 或者数据库干脆打不开 部分数据可能丢失 数据库宕机 长时间的数据恢复流程, 带库恢复几 T 数据? 上报保监会 银监会?
数据块损坏检测参数 DB_BLOCK_CHECKSUM 三种不同作用的检测保护机制, 但都不负责修复现有问题 DB_BLOCK_CHECKSUM 负责控制块的 cache 层的 chkval_kcbh 是否在块被写出时计算并写入到磁盘中 BBED> sum Check value for File 0, Block 500: current = 0x5327, required = 0x5327 BBED> p chkval_kcbh ub2 chkval_kcbh @16 0x5327
DB_BLOCK_CHECKSUM 数据块从 buffer cache 中写出时 Oracle 实例 Buffer バッファ Cache キャッシュ DBWn 计算 checksum 即 chkval_kcbh, 并写出
DB_BLOCK_CHECKSUM 数据块被从磁盘读取到 buffer cache 中时 Oracle 实例 Buffer バッファ Cache キャッシュ 验证块内的 checksum 和计算值是否一样
DB_BLOCK_CHECKSUM 检验发现 checksum 和计算值不一样 Oracle 实例 检测到物理损坏 Buffer バッファ Cache キャッシュ 验证块内的 checksum 和计算值是否一样 发现损坏 物理损坏 ORA-01578 报错
DB_BLOCK_CHECKSUM 通过一个校验值来检测出块损坏 : 由于断电或硬盘故障, 一个块仅写出了一半或更少内容, 此时 chkval 必然不等于整个块 checksum 从写出到下一次读取的时间中硬盘发生了故障, 块中的部分内容不正确 DB_BLOCK_CHECKSUM 控制是否计算和写 chkval_kcbh, 如果不写则 chkval_kcbh 为 0000, 读取时也不做检测
DB_BLOCK_CHECKSUM 默认值 TRUE(10g),Typical(11g) 当块要写出到磁盘时,DBWn 从块中的每一个字节计算 checksum 并存放到块头的 chkval_kcbh 有了 checksum 后,Oracle 能判断由底层磁盘存储系统引起的损坏 每一次数据块从磁盘中被读取都会检查 checksum 更新过程如下 : 服务进程修改数据块,checksum 被置 0 DBW 负责计算 checksum 并写出块 在上述改数据块和 checksum 之间存在时间,DBW 不清楚这中间的问题, 也不负责
db_block_checksum=true Buffer cache Block#=X Chkval=0 Block#=X Chkval=A DBWn later calculates a new checksum, stores it in block header and writes the block Block#=X Chkval=B Server process reads block X. Checksum is calculated and compared with the one in the block header. Block is modified and checksum=0 Datafiles
db_block_checksum=full Buffer cache Block#=X Chkval=B Block#=X Chkval=A DBWn later checks the checksum and writes the block Block#=X Chkval=B Server process reads block X. Checksum is calculated and compared with the one in the block header. Block is modified and new checksum is calculated =B Datafiles
db_block_checksum=false db_block_checksum=false, 那么仅 SYSTEM 表空间上的 block 还做 checksum _db_always_check_system_ts Always perform block check and checksum for System tablespace, 该隐藏参数控制是否对 SYSTEM 表空间做 checksum
数据块损坏检测参数 DB_BLOCK_CHECKING 对内存中的块内容做修改前, 是否对块做检测 考虑这样一个问题, 假设使用 FPE 工具 ( 一种古老的游戏内存修改工具 ) 修改了内存中一个块的内容, 这意味着这个块已经错了 然后 Oracle 不做任何检测继续在这个块上做修改, 那么就是错上加错了 DB_BLOCK_CHECKING 的义务
DB_BLOCK_CHECKING 当数据块在内存中被修改时, 检测块的事务层和数据层的完整性 Buffer Cache 数据块检测 数据块的更新 Oracle 实例
DB_BLOCK_CHECKING 对于更新过程中检测发现问题时, 阻止在这个缓存块中继续更新和回滚, 直接报 ORA-00600 错误 Oracle 实例 Buffer バッファ Cache キャッシュ 数据块检测 REDO ログ 发现问题 数据块的更新 ORA-600 报错
DB_BLOCK_CHECKING Oracle 实例 Buffer バッファ Cache キャッシュ REDO 日志 从磁盘中读取一个正常的数据块
DB_BLOCK_CHECKING Oracle 实例 Buffer バッファ Cache キャッシュ REDO 应用 REDO 应用 REDO 到故障点
DB_BLOCK_CHECKING 对于仅仅发生在 buffer cache 里的损坏, 通过 db_block_checking 检测并保护, 不让它写出到磁盘上, 不让错上加错发生 则大事化小小事化了 Oracle 实例 Buffer バッファ Cache キャッシュ 数据块恢复 REDO 可后续访问
DB_BLOCK_CHECKING 当数据块在内存中被修改时, 检测块的事务层和数据层的完整性 Row piece 没有重叠在一起 可用空间的大小是合理的 ITL 锁定的行数, 和实际有 lock bit 的行数是对应的 Oracle 定义了上百个检测 C 语言宏
DB_BLOCK_CHECKING False/OFF 除了 SYSTEM 表空间的块还做部分检测外, 其他表空间的块不做此类检测 LOW 基本的块头检测 ( 如 rbba,seqno) 会在内存中块内存修改后被检测, 例如 UPDATE/INSERT, 磁盘读或 RAC 节点间传输 MEDIUM 所有的 LOW 级别检测, 同时对堆表的数据块做语义检测 FULL/TRUE 所有 LOW 和 MEDIUM 检测的, 同时也对索引块做语义检测 DB_BLOCK_CHECKING 的表现实际收到 _DB_BLOCK_ADJCHK_LEVEL( 默认为 7) 的影响
DB_BLOCK_CHECKING 默认值为 FALSE 为了检测和避免逻辑损坏 典型情况下 block checking 的性能损耗为 1% 到 10%, 但更视乎合计负载类型, 某银行启用 DB_BLOCK_CHECKING=FULL 后 CPU 使用率增长了 20%~30% DB_BLOCK_CHECKING=MEDIUM 时性能损耗较少, 原因是其不检测索引块, 也不检测 IOT 如果发现了损坏, 报错常为 ORA-600 [kddummy_blkchk] 或 ORA-600 [kdblkcheckerror], 进一步损坏被避免
_db_block_check_for_debug=true 默认为 False 和 DB_BLOCK_CHECKING 配合使用, 如果没设 DB_BLOCK_CHECKING 那么不要用 当发现损坏时转出块以便诊断 该参数控制在修改块前拷贝块到 PGA 中, 若发现问题则将该拷贝转出到跟踪文件中 性能损耗在于每次修改块前都会拷贝块到 PGA 中, 其损耗实际并不大
DB_LOST_WRITE_PROTECT 啥是 Lost Write? 存储或卷管理软件 ( 如 Veritas) 都可能是引发 Lost Write 的层面, 表现为 : 因为是 Lost Write, 所以上一次写入的 Block 结构完全正常,Oracle 默认不会检测报错 提高了返回给用户不正确数据的可能 不正确的数据可能进一步污染其他业务数据 Lost Write 的业务影响 缺货还超卖 下单的记忆似乎不那么真实
DB_LOST_WRITE_PROTECT 针对写丢失的保护 需要 DataGuard 版本 11g 中出现 默认为 NONE 当在 DG 环境的 Primary 上设置为 TYPICAL, 则将 read-write 表空间上的 buffer cache 读记录到 redolog 中, 以便判断写丢失 则 Lost Write 可以在数据库 recovery 时被发现, 如物理备库
DB_LOST_WRITE_PROTECT DG 物理备库 Lost Write 检测机制 Primary Database 从磁盘读取 Block 的操作记录到 redo 中 Data File Number Data Block Address(DBA) System Change Number(SCN) 唯一指向一个数据块 Block 更新时会增长 DataGuard 中 redo 将传输到 Standby Database 在 Standby 上检验 redo 内的 SCN 和本地 block 如果 SCN 不一致, 则说明有 Lost Write 发生的可能
DB_LOST_WRITE_PROTECT Primary 和 Standby2 侧都要设置 Value Default Lost Write 检测对象 Primary Database Standby Database NONE N/A N/A N/A TYPICAL Read / Write 表空间从磁盘读取 block 到 Read / Write 表空间 buffer cache 中时生成检 FULL Read Only 表空间测用 Redo REDO APPLY 的一部分, MRP 将负责对比 REDO 里的 SCN Primary 或 Standby 上设置为 NONE 时是无效的 Primary 不生成相关 redo,standby 无法验证 Primary 生成 redo,standby 不验证
数据块损坏检测参数 DB_LOST_WRITE_PROTECT Lost Write 检测出的时机? 存在这样一种可能性, 即发生 Lost Write 后 Primary 立即发生了读取和修改, 那么 Standby 上 MRP 的验证已经晚了 一般情况下写丢失仅仅发生在少量块上, 只要不适用这些块 : 对其他块的查询更新都是正常的 错误的数据不会污染别的块
DB_LOST_WRITE_PROTECT 补充 相关验证 redo 仅限于 buffer cache 读 如果是 direct path read 直接路径读, 则不会生成 非 DataGuard 环境下也可以生成验证 Redo 可以在前滚时 rolling forward 时做检验 Standby 上也可能检测出 LostWrite 还可通过 ASM Mirror 或 ABR 自动修复
DB_LOST_WRITE_PROTECT DBA=A SCN=100 10 Primary Database DBA=A SCN=100 10 Data Guard Disk Buffer Cache 设置 1 REDO 2 Read DBA=B SCN=50 100 db_lost_write_protect 将写出读取操作以 redo log 形式记录下来 当前 SCN 150 DBA=A SCN=100 Standby Database 比较块的 SCN 版本 O K Buffer Cache DBA=A SCN=100 10 DBA=B SCN=50 100 DBA=A SCN=300 30 DBA=A SCN=100 10 Lost Write 发生 Read DBA=A SCN=300 30 丢失 DBA=A SCN=100 10 + 20 收到旧块的 redo 3 提交 DBA=A SCN SCN=100 300 30 当前 SCN 350 DBA=A SCN=100 4 O K 适用 N G DBA=A SCN=300 30 ORA-752 (Lost Write) 停止重做应用 DBA=B SCN=400 110 提交 DBA=B SCN SCN=50 400 110 不适用 DBA=B SCN=400 110
发现 Lost Write 流程 (Primary 侧 Lost Write) SQL> update TAB1 set COL2='BBB' where COL1=101 ; Primary Database Buffer Cache File7 Block131 SCN#3977658 101, AAA File7 Block131 SCN#3982682 101, BBB Buffer Flush Disk File7 Block131 SCN#3977658 101, AAA Lost Write 发生 Redo Record File7 Block131 SCN#3982682 COL2='BBB' Standby Database Buffer Cache File7 Block131 SCN#3977658 101, AAA File7 Block131 SCN#3982682 101, BBB SQL> select * from TAB1 where COL1=101 ; File7 Block131 SCN#3977658 101, AAA File7 Block131 SCN#3977658 Block Read 发现 Lost Write(ORA-752) SCN 3977658 < 3982682
DB_LOST_WRITE_PROTECT 发现 Lost Write 后的动作 负责通知 Lost Write 的是 Physical Standby 中的一个备库 其在 alert.log 中记录 ORA-752 为了保护备库, 自动停止 MRP 进程 后续的 redo 不被应用, 不用担心进一步数据污染 PRXX 进程对应的跟踪文件中记录了详细信息 包括对相关 redo 和 block 的转储 这些信息帮助 DBA 追查 Lost Write 问题
DB_LOST_WRITE_PROTECT 发现 Lost Write 的 Standby 端的 alert.log Wed Oct 23 19:18:08 2013 Hex dump of (file 7, block 131) in trace file /u01/app/oracle/diag/rdbms/orcls/orcls1/trace/orcls1_pr02_1401.trc Reading datafile '+DATA/orcls/datafile/lw.281.829593935' for corruption at rdba: 0x01c00083 (file 7, block 131) Read datafile mirror 'DATA_0004' (file 7, block 131) found same corrupt data (logically corrupt) Read datafile mirror 'DATA_0006' (file 7, block 131) found same corrupt data (logically corrupt) STANDBY REDO APPLICATION HAS DETECTED THAT THE PRIMARY DATABASE LOST A DISK WRITE OF BLOCK 131, FILE 7 NO REDO AT OR AFTER SCN 3987667 CAN BE USED FOR RECOVERY. Recovery of Online Redo Log: Thread 1 Group 7 Seq 103 Reading mem 0 Slave exiting with ORA-752 exception Errors in file /u01/app/oracle/diag/rdbms/orcls/orcls1/trace/orcls1_pr02_1401.trc: ORA-00752: recovery detected a lost write of a data block ORA-10567: Redo is inconsistent with data block (file# 7, block# 131, file offset is 1073152 bytes) ORA-10564: tablespace LW ORA-01110: data file 7: '+DATA/orcls/datafile/lw.281.829593935' ORA-10561: block type 'TRANSACTION MANAGED DATA BLOCK', data object# 87637 Wed Oct 23 19:18:12 2013 Recovery Slave PR02 previously exited with exception 752 Wed Oct 23 19:18:12 2013 MRP0: Background Media Recovery terminated with error 448 Errors in file /u01/app/oracle/diag/rdbms/orcls/orcls1/trace/orcls1_pr00_1395.trc: ORA-00448: normal completion of background process MRP0: Background Media Recovery process shutdown (orcls1)
DB_LOST_WRITE_PROTECT PRXX 进程 TRACE Block Dump Main Message Redo Dump Hex dump of (file 7, block 131) 发现 Lost Write 的数据块 Dump of memory from 0x00000000F03B0000 to 0x00000000F03B2000 0F03B0000 0000A206 01C00083 003CC55A 04010000 [...Z.<...]... STANDBY REDO APPLICATION HAS DETECTED THAT THE PRIMARY DATABASE LOST A DISK WRITE OF BLOCK 131, FILE 7 The block read on the primary had SCN 3977658 (0x0000.003cb1ba) seq 1 (0x01) while expected to have SCN 3982682 (0x0000.003cc55a) seq 1 (0x01) 1 Primary 侧 Block 的 SCN The block was read at SCN 3987667 (0x0000.003cd8d3), BRR: 2 Standby 侧 Block 的 SCN CHANGE #1 TYP:2 CLS:6 AFN:7 DBA:0x01c00083 OBJ:87637 SCN:0x0000.003cb1ba SEQ:1 OP:23.2 ENC:0 RBL:1... 3 Primary 侧读取该 block 时的 SCN REDO RECORD - Thread:1 RBA: 0x000067.00000128.0010 LEN: 0x0034 VLD: 0x10 SCN: 0x0000.003cd8d3 SUBSCN: 1 10/23/2013 19:18:16 (LWN RBA: 0x000067.00000126.0010 LEN: 0003 NST: 0001 SCN: 0x0000.003cd8cf) CHANGE #1 TYP:2 CLS:6 AFN:7 DBA:0x01c00083 OBJ:87637 SCN:0x0000.003cb1ba SEQ:1 OP:23.2 ENC:0 RBL:1 Block Read - afn: 7 rdba: 0x01c00083 BFT:(1024,29360259) non-bft:(7,131) scn: 0x0000.003cb1ba seq: 0x01 flags: 0x00000006 ( dlog ckval ) 3977658(1) < 3982682(2) 可判定 Primary 发生 Lost Write 3982682(2) 的更新被丢失了 3987667(3) 读取数据块时被检测到
DB_LOST_WRITE_PROTECT Standby 侧检测出 Lost Write SQL> select * from TAB1 where COL1=101 ; Primary Database Buffer Cache Buffer Flush File7 Block131 SCN#3982682 101, BBB Disk Redo Record File7 Block131 SCN#3982682 COL2='BBB' File7 Block131 SCN#3982682 Block Read Automatic Block Media Recovery Standby Database Buffer Cache File7 Block131 SCN#3977658 101, AAA File7 Block131 SCN#3982682 101, BBB Buffer Flush Disk File7 Block131 SCN#3977658 101, AAA File7 Block131 SCN#3977658 101, AAA 发现 Lost WRite ABR Lost Write 发生
DB_BLOCK_CHECKSUM 性能损耗测试与设置为 OFF 时的执行耗时对比
DB_BLOCK_CHECKING 性能损耗测试与设置为 OFF 时的执行耗时对比
DB_LOST_WRITE_PROTECT 性能损耗测试与设置为 OFF 时的执行耗时对比
www.parnassusdata.com 400-690-3643 Thank You