瀚高数据库
目录
环境
文档用途
详细信息
环境
系统平台:Linux x86-64 Red Hat Enterprise Linux 7
版本:14,N/A
文档用途
用途
使用checksum,对数据库提供块校验,以发现隐藏的块损坏问题,注意仅适用于原生PG或HGDB企业版,或未使用SM4加密的HGDB安全版。
HGDB安全版假如使用了SM4加密,会与checksum冲突导致数据文件损坏。
什么是坏块
坏块通常是指读取或写入的数据文件,由于存储介质故障、突然断电等问题,导致的数据块中存储的信息丢失,或由于没来得及写入完成导致的块断裂,坏块通常会带来数据损坏甚至数据丢失的风险。
checksum
PostgreSQL在9.3版本开始,引入了checksum,使用checksum可以检验数据块的损坏
checksum校验发生在数据页落盘之前和进入sharedbuffer之前。
checksum写入
当将页面从缓冲区缓存写入到操作系统页面缓存(OS Cache)时,就会计算checksum值并写入数据页中。
checksum校验
每次读取该块时,checksum值就会重新计算,并与所存储的checksum值进行比较。这可以检验数据是否损坏。
使用pg_checksums命令的注意事项
pg_checksums 命令:启用、禁用或检查PostgreSQL数据库集簇中的数据校验和(checksum)
在运行pg_checksums之前必须彻底关闭服务器。
验证checksum时:
(1)如果没有校验和错误,则退出状态为零;
(2)如果检测到至少一个checksum校验失败,则退出状态为非零。
(3)启用或禁用checksum时,如果操作失败,退出状态为非零。
验证checksum时,会扫描数据库集簇中的每个文件。
(1)启用校验和时,每个具有已更改校验和的relotion file block都会立刻重写。
(2)禁用checksum只会更新文件pg_control。
额外注意点:
(1)在大型数据库集簇中启用checksum可能需要很长时间。在此操作过程中,不得启动数据库或其他写入数据目录的程序,否则可能会导致数据丢失。
(2)当使用replication或执行关系文件块直接复制的工具(例如pg_rewind)时,如果没有在所有节点上做相同的操作(例如主库打开了checksum,备库没打开),
启用或禁用校验和可能会导致checksum计算错误。
(3)假如流复制环境下,那么建议停止和删除所有备库,在主库上执行启用checksum,最后重做所有备用数据库。
(4)如果在启用或禁用校验和时pg_checksums被中止,集群的数据校验和不会改变,可以通过重新运行pg_checksums来继续打开或关闭checksum。
详细信息
checksum使用示例
1. 检查数据库是否开启checksum
[postgres@centos7 data]$ pg_controldata -D $PGDATA |grep checksum
Data page checksum version: 0 <--0表示未启用,1表示打开
2. 创建并查询表的物理位置
create table tab1(a int primary key ,b text);
insert into tab1 values (1,'hello postgres');
select pg_relation_filepath('tab1');
物理位置如下:
postgres=# select pg_relation_filepath('tab1');
pg_relation_filepath
----------------------
base/5/16397
(1 row)
3. 关闭数据库,启用checksum
[postgres@centos7 data]$ pg_checksums -D $PGDATA --enable --progress
22/22 MB (100%) computed
Checksum operation completed
Files scanned: 953
Blocks scanned: 2816
Files written: 786
Blocks written: 2816
pg_checksums: syncing data directory
pg_checksums: updating control file
Checksums enabled in cluster
–enable :-e, --enable 启用data checksums
–progress :-P, --progress 显示进度报告
检查pg_controldata
[postgres@centos7 data]$ pg_controldata -D $PGDATA |grep checksum
Data page checksum version: 1 <--checksum已经打开
4. 手动制造坏块
先看一下当前块的内容:物理文件是8KB,只有一个块
[postgres@centos7 5]$ dd bs=8192 count=1 seek=1 of=/data/highgo/data/base/5/16397 if=/data/highgo/data/base/5/16397
1+0 records in
1+0 records out
8192 bytes (8.2 kB) copied, 0.000373582 s, 21.9 MB/s
解释:使用seek=1偏移8K,也就是在原来的一个块后再追加一个块,这样假如未开启checksum,实际是能查出2行数据的
由1个块变成2个块
5. 此时启动数据库,查询该表
发现:
postgres=# select * from tab1;
WARNING: page verification failed, calculated checksum 5783 but expected 5782 <--提示checksum不一致了,事务直接中止
ERROR: invalid page in block 1 of relation base/5/16397
6. 停掉数据库,使用checksum检查数据页的错误
[postgres@centos7 5]$ pg_checksums -D $PGDATA --check
pg_checksums: error: checksum verification failed in file "/data/highgo/data/base/5/16397", block 1: calculated checksum 1697 but block contains 1696
Checksum operation completed
Files scanned: 953
Blocks scanned: 2817
Bad checksums: 1
Data checksum version: 1
这里提示的:1696是16进制,转换为10进制,正好是5782,和数据库中的报错相同
7. 关闭checksum,检查发现异常块也可以被读取
[postgres@centos7 5]$ pg_checksums -D $PGDATA --disable --progress
pg_checksums: syncing data directory
pg_checksums: updating control file
Checksums disabled in cluster
[postgres@centos7 5]$ pg_ctl start
[postgres@centos7 5]$ psql
postgres=# select * from tab1;
a | b
---+----------------
1 | hello postgres
1 | hello postgres <-- 原来只有1行,dd到文件后追加了1个块,导致查出2行了
(2 rows)
8. 如何忽略坏块错误?
可以通过:
(1). ignore_checksum_failure:该参数仅在开启checksum下生效,作用就是忽略checksum的报错,读出坏块中的数据,但是假如page header错误,那么也读不出来,事务依旧会中止。
(2). zero_damaged_pages:数据库会在内存中将损坏的page都置为空,然后获取剩下的结果,即使page header损坏,执行事务也不会退出,只会报warning,这个参数,官方文档上说不会写入物理文件,但是实际情况下,还是建议先原样备份后,再打开该参数。
下面进行测试:
重新开启checksum并制造坏块:
pg_ctl stop
pg_checksums -D $PGDATA --enable --progress
dd bs=8192 count=1 seek=1 of=/data/highgo/data/base/5/16397 if=/data/highgo/data/base/5/16397
发现有坏块了:
[postgres@centos7 5]$ pg_checksums -D $PGDATA --check
pg_checksums: error: checksum verification failed in file "/data/highgo/data/base/5/16397", block 1: calculated checksum 8E26 but block contains 8E27
Checksum operation completed
Files scanned: 953
Blocks scanned: 2817
Bad checksums: 1
Data checksum version: 1
8.1 使用ignore_checksum_failure参数忽略checksum
该参数仅当启用checksum时才有效,在读取过程中检测到checksum失败通常会导致PG报告错误,从而中止当前事务。设置ignore_checksum_failure为 on 会导致系统忽略故障(但仍报告警告),并继续处理。此行为可能会导致崩溃、传播或隐藏损坏或其他严重问题。但是,如果page header仍然正常,它可能允许您克服错误并检索可能仍然存在于表中的未损坏的元组。如果page header损坏,即使启用此选项,也会报告错误。默认设置为off。只有超级用户和具有适当SET权限的用户才能更改此设置。
postgres=# show ignore_checksum_failure;
ignore_checksum_failure
-------------------------
off
(1 row)
postgres=# set ignore_checksum_failure = on;
SET
postgres=# select * from tab1;
WARNING: page verification failed, calculated checksum 36390 but expected 36391
a | b
---+----------------
1 | hello postgres
1 | hello postgres <--忽略了checksum的报错,直接读出2个块的数据
(2 rows)
8.2 使用参数 zero_damaged_pages忽略坏块
该参数不要求启用checksum,它总会让数据库忽略坏块。
检测到损坏的page header通常会导致PostgreSQL报告错误,从而中止当前事务。设置zero_damaged_pages = on 会导致系统报告警告,将内存中损坏的页面清零,然后继续处理。此行为会破坏数据,即损坏页上的所有行。但是,它确实允许您克服错误并从表中可能存在的任何未损坏的页面中检索行。如果由于硬件或软件错误而发生损坏,它对于恢复数据很有用。通常,在您放弃从表的损坏页恢复数据的希望之前,不应将其设置为打开。清零页不会强制写入磁盘,因此建议在再次关闭此参数之前重新创建表或索引。默认设置为off。只有超级用户和具有适当SET权限的用户才能更改此设置。
该参数是最后的希望,在使用该参数之前,必须要对原始数据进行完整的物理备份。
[postgres@centos7 5]$ pg_ctl restart
[postgres@centos7 5]$ psql
postgres=# show zero_damaged_pages;
zero_damaged_pages
--------------------
off
(1 row)
postgres=# set zero_damaged_pages=on;
SET
postgres=# select * from tab1;
WARNING: page verification failed, calculated checksum 36390 but expected 36391
WARNING: invalid page in block 1 of relation base/5/16397; zeroing out page
a | b
---+----------------
1 | hello postgres
(1 row)
可以看到,该参数会忽略实际损坏的块,而不是像ignore_checksum_failure那样,仅是忽略checksum错误。
注意:使用这两种方式后,应当尽快采用逻辑导出对应表,并重建表后导入。
9.数据库恢复期间遇到坏块怎么办?
还有一个其他的参数,仅在恢复期间使用的
ignore_invalid_pages(boolean)
如果设置为off(默认值),则数据库在恢复期间检测到引用无效page的WAL记录,会导致PostgreSQL引发PANIC级错误,从而中止恢复。设置ignore_invalid_pages为on会导致系统忽略 WAL 记录中的无效页面引用(但仍报告警告),并继续恢复。此行为可能会导致崩溃、数据丢失、传播或隐藏损坏或其他严重问题。但是,它可能允许您克服 PANIC级错误,完成恢复并启动服务器。该参数只能在服务器启动时设置。它仅在recovery或standby模式下有效。