接到应用侧反馈GP数据库有些表经常出现突然无法查询的情况,或者表的某些数据查询不了了,并且报错信息都非常相似,报错信息均指向表对应的数据文件校验不一致,报错信息如下:
运行日志:【在Agent[1330162]执行结果:存储过程:p_dw_ulc_usr_prov_happy_ds执行完成,返回信息:参数名称:o_return_code参数值:XX000,参数名称:o_return_msg参数值:SQL5:从接口取原始数据出错! 错误信息:Block checksum does not match. Expected 0xBD17CA1E and found 0x450D9B9 (cdbappendonlystorageread.c:1210) …………………………Append-Only segment file/data1/primary/gpfs/gpseg186/33389/2435928/65966825.1537, block header offset in file = 50650432, bufferCount 2990,】 |
看到报错信息,立马想到是否是磁盘问题造成文件在写磁盘的时候出现了问题,按照这个思路抓紧核查磁盘。
RAID卡底层日志没有发现有reset、time out等异常打印
暂时排除磁盘问题,那还有哪里出问题会导致数据问题?顺其自然想到是否是GP数据库在primary实例和mirror实例数据同步的时候出现问题?
我们先来温故一下primary实例和mirror实例主备复制的机制。
大家都知道Greenplum 是基于PostgreSQL开发,PostgreSQL的主备也是通过流复制实现,但是Segment节点中的Primary和Mirror之间的数据同步是基于文件级别的同步方式实现的。为什么Primary和Mirror不能再使用流复制实现呢?
Append Only表不写WAL日志,所以Append Only表的数据就不能通过XLOG发送到Mirror再Apply;
pg_control等文件也是不写WAL日志,也只能通过文件方式同步到Mirror。
Primary和Mirror同步的内容主要有两部分,即文件和数据。之所以Primary和Mirror要同步文件,是因为Primary和Mirror之间可以自动failover,只有两者保持同步才能相互替代,如果只把数据同步过去,pg_control、pg_clog、pg_subtrans 没有同步,那么从Primary切换到Mirror就会出现问题。
GP master和GP slave却不用担心这些问题,Append Only 表的数据只会存在 Segment,所以WAL日志足够保持GP master和GP slave同步(只要是流复制,pg_control、pg_clog、pg_subtrans 这些文件Slave会自动更新,无需从Master同步)。
当GP master向Primary下发执行计划后,Primary开始执行,如果是DML操作,那么Primary会产生XLOG及更新page。会在SlruPhysicalWritePage函数中(写数据页)产生FileRepOperationOpen、FileRepOperationWrite、FileRepOperationFlush、FileRepOperationClose等指令消息(消息中包含具体要更新的文件page及内容),通过primary sender进程向Mirror发送Message,然后Mirror的mirror consumer等进程解析消息,执行变更。XLOG通过XLogWrite函数(写XLOG)执行同样的操作,把XLOG更新同步过去。
Primary的recovery进程会循环把Primary的 pg_control、pg_clog、pg_subtrans 等文件覆盖到Mirror。同时检查XLOG是否一致,如果不一致以Primary为主,对Mirror进行覆盖。除把Primary部分文件同步到Mirror之外recovery进程还会将Mirror上面的临时文件删掉。
根据以上的机制,在实验环境做了两个实验进行验证:
1. 将mirror的data目录设置为gpadmin无权限访问。
2. 在master上操作, 进行数据写入。
1. 写入操作卡住一段时间(等待mirror写入)。
2. primary发现无法与mirror同步, 因此将mirror设置为down的状态。
3. 数据写入继续在primary进行, 写入完成。
正常情况下, 一个事务必须在primary和mirror都写入完成的情况下才会提交, 如果primary和mirror无法同步, 那么会在超时后将mirror标记为 down的状态。
gpadmin=# insert into t1 select generate_series(1,100000) ; WARNING: mirror failure, could not complete mirrored request identifier pg_xlog/000000010000000000000001 ack state waiting for ack, failover requested (seg0 172.28.8.2:23168 pid=20162) HINT: run gprecoverseg to re-establish mirror connectivity CONTEXT: mirroring role primary role mirroring state sync segment state in shutdown process name(pid) filerep uninitialized process(20162) filerep state not initialized position ack begin 0x7f4a97f3e040 position ack end 0x7f4a97fbe040 position ack insert 0x7f4a97f69c58 position ack consume 0x7f4a97f69c58 position ack wraparound 0x7f4a97fbe040 insert count ack 43 consume count ack 44 WARNING: mirror failure, could not complete operation on mirror, failover requested (seg0 172.28.8.2:23168 pid=20162) DETAIL: identifier pg_xlog/000000010000000000000001 operation not specified relation type flat file message count -1 ... INSERT 0 100000 Time: 37849.733 ms |
1. 在master进行写入操作。
2. 在写入的同时, 破坏primary的table文件. (cat xxx >> [table file]), 模拟硬件/文件系统问题导致损坏的情况。
3. 写入完成后, 对表执行select操作。
1. 写入可以正常完成, 因为写入的操作的块文件正常完成(fsync)。
2. 由于文件被破坏, 因此在select的时候会报错。
3. 同时, 由于复制是基于块的同步, 因此primary的损坏操作(人为追加)不会同步到mirror上, 因此mirror上的文件没有问题。
1.当数据写入时, 数据块分两条线走, 一条发送给mirror, 一条到primary落盘。
2.GP只会检查数据块本身是否写入完成(fsync), 对于落盘之后的外部操作无法感知, 即硬件/系统问题引起的数据文件损坏在数据块落盘后是无法发现的
3.只有在之后读取到对应的数据文件时, 才会检查到数据块错误.
gpadmin=# insert into t1 select generate_series(1,100000000); INSERT 0 100000000 Time: 44379.438 ms gpadmin=# copy (select * from t1) to /dev/null; WARNING: page verification failed, calculated checksum 24539 but expected 27626 (seg0 slice1 172.28.8.2:23168 pid=22869) ERROR: invalid page in block 6129 of relation base/16384/16388 (seg0 slice1 172.28.8.2:23168 pid=22869) gpadmin=# packet_write_wait: Connection to 10.152.9.3 port 22: Broken pipe [gpadmin@sdw3 16384]$ md5sum 16388 83918050bc9ce93dfad130a24e105e61 16388 [gpadmin@sdw2 16384]$ md5sum 16388 48b7740a044bb3398283f4d44e01e46b 16388 ## 切换到mirror后: ## gpadmin=# copy (SELECT * from t1 ) to /dev/null; COPY 100000000 Time: 36918.063 ms gpadmin=# |
通过上面的实验可以了解到primary实例和mirror实例之间的同步机制:当数据写入时候, 数据块分两条线走, 一条发送给mirror, 一条到primary落盘,GP只会检查数据块本身是否写入完成(fsync), 对于落盘之后的外部操作无法感知, 即硬件/系统问题引起的数据文件损坏在数据块落盘后是无法发现的,只有在之后读取到对应的数据文件时, 才会检查到数据块错误。
上面实验报错明显的指出了是primary实例下的文件有校验不一致的情况发生,那我们接着往下看mirror的文件是否有问题。
这个该怎么验证?很简单,把出现问题的文件和对应mirror实例下的文件做个md5sum的校验,看结果值是否一致,如一致则表明mirror实例下的文件肯定也损坏了,如不一致,则把两个文件备份一份,再把mirror实例下的文件覆盖primary实例下有问题的文件,对有问题的表再做一次查询,看是否还报错,通过实验及测试,如果AO表使用上面的方法可以解决文件坏块的问题,则说明是文件损坏的问题,不是在primary实例和mirror实例之间同步数据造成,因为如果是同步数据造成的,mirror文件也会损坏。
问题分析到这里遇到了瓶颈,磁盘也没有问题,也不是数据库数据同步造成的,那该是什么原因造成的呢?
看来只能对损坏的文件进行分析了,把之前报错的文件全部收集出来对文件块进行分析,
数据文件 7398846.1: Block 124473 checkusm 错误
- 日志显示损坏位置为 block header offset in file = 644305640
- 通过解析该数据文件, 对应的block为 124473
- 数据文件显示block 21406存在checksum错误, head中的checksum与实际不符
- 同时, Mirror的文件正常
***** Block 124473 Start ***( Position: 644305640) ************************* HeaderKind: 1 Header Checksum Valid (checksum -1229617606) (AoHeaderKind_SmallContent) executorBlockKind: 1 rowCount: 67 headerLen: 24 uncompressedLen: 32302 compressedLen: 4421 firstRowNumber: 8904392 Content checksum doesnt match storedChecksum: 945093312, computedChecksum: 1985286725 |
数据文件 15165123.1: Block 21406 checksum 错误:
- 日志显示损坏位置为 block header offset in file = 182856032
- 通过解析该数据文件, 对应的block为 21406
- 数据文件显示block 21406存在checksum错误, head中的checksum与实际不符
- 同时, mirror的数据文件正常。
***** Block 21406 Start ***( Position: 182856032) ************************* HeaderKind: 1 Header Checksum Valid (checksum 454188836) (AoHeaderKind_SmallContent) executorBlockKind: 1 rowCount: 270 headerLen: 24 uncompressedLen: 32716 compressedLen: 8298 firstRowNumber: 5759608 Content checksum valid (checksum: -482616586) |
目前我们可以确定, 数据文件的确存在受损的问题, 且损坏只发生在primary segment上。
下面我们对出现文件损坏的表在进行分析下:
- 我们在深入检查table文件之后, 发现primary并没有缺损, 事实上primary从08ed2000位置开始被写了"0"
- primary从08ed2000 到 08ee03e0 都是0, 而mirror并不是
- 同时, 16进制的08ed2000,08ee03e0分别对应10进制的149757952和149816288, 恰好都是是4096的整数倍(即这个位置是一个4096大小的block的开始)
2 < 08ed2000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 3 < * << 这里的星号表示都是0 4 --- 5 > 08ed2000 4c 4b d6 af 07 f2 31 74 10 4b e7 f5 8f fc 77 f9 |LK....1t.K....w.| 6 > 08ed2010 1c 7e 02 39 49 16 21 3b 70 f4 39 52 f3 e5 df 8c |.~.9I.!;p.9R....| 7 > 08ed2020 76 c4 99 ea bf af 73 8b 33 ae 76 38 d2 5f 15 a0 |v.....s.3.v8._..| |
- 同样另外一张表, primary的01110000开始到0111ecf0变成了0, 而mirror不是:
1 1118209,1118210c1118209,1122000 2 < 01110000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 3 < * 4 --- 5 > 01110000 cf 9c 87 4d 9e 61 d7 61 97 4f b0 5f 66 ff 84 fd |...M.a.a.O._f...| 6 > 01110010 2b f6 ef d9 5f b0 b7 ed bc 6b 47 ef 84 97 ac f0 |+..._....kG.....| |
- 16进制01110000和0111ecf0分别对应10进制17891328和17951984, 同样是4096的整数倍。
综上所述, 经研发部门确认该问题并非GPDB造成, 因为GPDB从来不会对table文件写0。所以将从软硬件方面来继续排查问题 (XFS? LVM? SAN? RAID card?)。
问题分析到这里基本可以得出结论,造成Block checksum does not match问题的不是磁盘,也不是数据库软件本身的问题,但是还需要从主机系统等层面进行分析,已联系操作系统等相关厂商一起进行分析,后续有进展将继续分享。
这个问题目前的处理方法很简单,有两种:
1.找到该实例对应的mirror实例,在对应mirror实例下找到对应的数据文件,通过MD5SUM工具来校验mirror和primary实例下数据文件的值是否一致,通过mirror的文件来覆盖primary下的文件来解决;
2. 一般这种表都为历史时间分区表,数据可以通过hadoop集群重新写入,所以如果出现问题,则可以通过把该表删除,并重新入数来解决;
上面的讲到了GP数据库中文件的损坏,及排查思路,那怎么确定损坏的数据文件和表之间的关系,下面在一起了解下这个关系如何确定;
首先介绍下在GP数据库中表的存储格式:Heap表和AO表(AORO表,AOCO表)
Heap表:这种存储格式是从PostgreSQL继承而来的,目前是GP默认的表存储格式,只支持行存储。
AO表:AO表最初设计是只支持append的(就是只能insert),因此全称是Append-Only,在4.3之后进行了优化,目前已经可以update和delete了,全称也改为Append-Optimized。AO支持行存储(AORO)和列存储(AOCO)。
从上面的这个错误信息可以看出表在primary实例对应的数据文件有损坏,遇到这个问题,首先想到的就是这个数据文件对应的是那个表,但是该如何通过数据文件查到对应的表呢?
熟悉GP数据库的工程师立马会想到通过pg_class 中的relfilenode 这个信息来找到对应的表例如:
这样就找到了对应的表,大家肯定会说这也太简单了,这种方式一般都是用来查heap表和一些刚好是AO表的数据文件对应的应用数据表。
但是GP数据库中AO表是一种比较特殊的表,每个AO表都有一个关联的系统表,该系统表用于管理文件系统上的段文件,所以我们可以查到下面两种结果,仔细看这两种结果有什么不同:
AO表的行存和列存在数据库中存储时名称是有区别的,区别如下:
AORO(行存)表名: [pg_aoseg.pg_aoseg_xxxxxx>]
AOCO(列存)表名: [pg_aoseg.pg_aocsseg_xxxxxx>]
这明显不是我们想要的表,遇到这种结果我们该怎么通过这个系统表来找到对应的表?
当年一些有经验的老鸟曾告诉过一种比较偷懒的办法是AO表的系统表结构中结尾都是数字结束的,这些数字就是该表所对应的relfilenode,我就试了下,结果如下:
还真的查到了,快捷方便,所以在接下来的一段时间里,我一直使用这种方式也解决了很多问题。
直到一次故障,让我意识到偷懒的办法只能用来偷懒,在遇到更棘手的故障时,这种纯经验式解决方法是行不通的。事情是这的,应用侧反馈又出现了Block checksum does not match的报错,把对应的数据文件号发给我后。按照老方法根据文件号,通过pg_class 中的relfilenode来找对应的表,找到后发现是张AO表,然后又根据AO表的系统表后面的数字找对应的表,结果这次没找,确认了好几次就是这个文件号,但是查出来的结果却是为空;
结果如下:
这让我意识到该办法不科学不严谨,不能予以依赖,通过基于原理的办法步骤来找对应的表。
首先想到AO表所有信息会存在pg_appendonly数据字典中,然后一步步按照流程来查询,最终找到了的对应表;
在后续通过几次实验得出了一个两张数据字典表的关联关系:
ao表的系统表 pg_class 的oid等于pg_appendonly 的segrelid;
ao表的基表中 pg_class 的oid等于pg_appendonly 的relid
今天的GP故障分享就到这里,后续会继续带来现场各种问题处理的案例分享和心得体会,我们下次见。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/130221.html
摘要:上有主节点和从节点两部分,两者主要的功能是生成查询计划并派发,以及协调并行计算,同时在上保存着,这个全局目录存着一组数据库系统本身所具有的元数据的系统表。 前言:近年来,互联网的快速发展积累了海量大数据,而在这些大数据的处理上,不同技术栈所具备的性能也有所不同,如何快速有效地处理这些庞大的数据仓,成为很多运营者为之苦恼的问题!随着Greenplum的异军突起,以往大数据仓库所面临的很多...
阅读 1343·2023-01-11 13:20
阅读 1679·2023-01-11 13:20
阅读 1130·2023-01-11 13:20
阅读 1852·2023-01-11 13:20
阅读 4095·2023-01-11 13:20
阅读 2703·2023-01-11 13:20
阅读 1383·2023-01-11 13:20
阅读 3590·2023-01-11 13:20