ITPub博客

首页 > > > PostgreSQL 可靠性分析

PostgreSQL 可靠性分析

作者:wangliyun 时间:2020-02-20 18:56:52 0 删除 编辑


背景

PostgreSQL 可靠性与大多数关系数据库一样,都是通过REDO来保障的。

群里有位童鞋问了一个问题,为什么PostgreSQL的REDO块大小默认是8K的,不是512字节。

这位童鞋提问的理由是,大多数的块设备扇区大小是512字节的,512字节可以保证原子写,而如果REDO的块大于512字节,可能会出现partial write。

那么PostgreSQL的redo(wal) 块大小设置为8KB时,靠谱吗?本文将给大家分析一下。

什么情况下会出现partialwrite?

1. 当开启了易失缓存时,如果写数据的块大小大于磁盘原子写的大小(通常为512字节),掉电则可能出现partial write。

例如disk cache,没有掉电保护,而且操作系统的fsync接口不感知disk cache,如果你调用了fsync,即使返回成功,数据其实可能还在disk cache里面。

当发生掉电时,在disk cache里的数据会丢失掉,如果程序写一个8K的数据,因为磁盘的原子写小于8K,则可能出现8K里有些写成功了,有些没有写成功,即partial write。


(ps: 某些企业级SSD可以通过电容残余的电量,将DISK CACHE里的数据持久化下来,但是请不要相信所有磁盘都有这个功能)

2. 当开启了易失缓存时,如果写数据的块大小小于或等于磁盘原子写的大小(即"原子写"),掉电时也可能出现partial write。

对于MySQL来说,REDO的写为512字节的,其中包含12个字节的头信息,4个字节的校验信息。

这个怎么理解呢,为什么没有对齐则可能出现。


不对齐的坏处

1. 前面提到了,如果没有对齐,并且开启了易失缓存,原子写是没有用的,同样会出现partialwrite。

2. 如果没有对齐,会造成写放大,本来写512字节的,磁盘上会造成写1024字节(将两个扇区数据读出来再与要写的数据合并, 分成两个扇区回写)。

原子写不能抵御什么风险?

1. 开启易失缓存时,原子写一样会丢失易失缓存中的数据。

2. 当未对齐时,原子写并不是真的原子写。

数据库只靠REDO的原子写,如果不考虑以上两个因素,起不到保证数据可靠性和一致性的作用。

PostgreSQL如何保证数据库可靠性

1. shared buffer 中的dirty page在write前,必须要保证对应的redo已经持久化(指已经落到非易失存储介质)。

2. 在检查点后出现的脏页,必须要在redo中写dirty page的full page。

这2条保证的是数据文件的一致性。

3. 在不考虑standby的情况下,当设置为同步提交的事务在事务提交时,必须等待事务产生的REDO已持久化才返回(指已经落到非易失存储介质)。

参考

《PostgreSQL 9.6 同步多副本与 remote_apply事务同步级别》

4. 当设置为异步提交的事务在事务提交时,不需要等待事务产生的REDO持久化。

由于有第一条的保护,所以即使使用异步事务,丢失REDO buffer中的数据后,也不会出现不一致(比如一半提交,一半未提交)的情况,仅仅丢失redo buffer中未提交的事务而已。

一致性由PostgreSQLMVCC的机制来保证,不会读到脏数据。

建议

1. 在使用COW的文件系统(如btrfs, zfs)时,可以关闭full page write,因为这种文件系统可以保证不会出现partial write。

2. 对齐,可以避免写放大的问题。

3. 不要使用易失缓存,但是可以使用有掉电保护的易失缓存。

PostgreSQL认为系统提供的fsync调用是可靠的,即写到了持久化的存储。

如果连fsync都不可靠了,管它是不是原子写,都是不可靠的。

包括DirectIO在内(PostgreSQL支持REDO使用DirectIO),也无法感知disk cache,所以请慎重。

PostgreSQL redo block不是原子写,安全吗?

首先,前面已经分析了,原子写并不能抵御易失存储导致的丢数据。

1. PostgreSQL redo block是有checksum的,可以保证块的一致性,不会APPLY不一致的块。

2. 事务提交时,返回给用户前,一定会保证REDO已持久化。

所以用户收到反馈的事务,一定是持久化的,不可能存在partialwrite。

而没有收到反馈或未结束的事务,才有可能包含partialwrite,那么问题就简化了:

这些没有收到反馈或未结束的事务产生的REDO 出现partial write会不会导致数据不一致?

回答是不会,参考前面"PostgreSQL如何保证数据库可靠性",MVCC机制可以保证这些。

模拟redo block partial write

数据库参数

wal_level = logical  
 
便于观察,验证  

产生测试数据

pgbench -i -s 100

模拟压力测试

pgbench -M prepared -n -r -P 2 -c 32 -j 32 -T 1000

观测到产生了一些XLOG,约200秒后,测试过程中强制停库,下次启动会进入恢复状态

pg_ctl stop -m immediate 

记录接下来要纂改的REDO文件以及之前的文件最后的内容

纂改的前一个文件的末尾的一些内容,用于判断已持久化的记录
能看到几笔commit rec就行了

 




被纂改的文件的头部的内容,用于判断未持久化的记录
这里显示的都是将要纂改掉,对PG来说就是未持久化的事务,数据库恢复后是不会显示的.

pg_xlogdump -b -n 20 0000000100000116000000F8 0000000100000116000000F9







纂改redo

cd $PGDATA/pg_xlog
 
找到最后两个xlog文件,纂改一下offset 100字节后的中间位置,模拟partial write。   
dd if=/dev/zero of=./0000000100000116000000F8 bs=1 count=10000 skip=100
dd if=/dev/zero of=./0000000100000116000000F9 bs=1 count=10000 skip=100

启动数据库,进入恢复状态,当读到checksum不一致的block,停止继续往前,也就是说数据库恢复到这里截至。

未恢复的事务造成的变更,对用户不可见。




PostgreSQL redo block size可配置

./configure --with-wal-blocksize=?
 
Allowed values are 1,2,4,8,16,32,64.

参考

1. https://www.pgcon.org/2012/schedule/attachments/258_212_Internals%%20Of%%20PostgreSQL%%20Wal.pdf

如果要深入了解PostgreSQLredo的内部机制,可以参考以上文档以及源码。


 

 


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69917105/viewspace-3161877/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-04-19

  • 博文量
    213
  • 访问量
    317