PostgreSQL码农集散地

老白说PG有隐藏的安全隐患, 真的吗?

文章开始前推荐2个学习环境: 

1、欢迎使用镜像快速体验PostgreSQL/DuckDB强大功能:《最好的PostgreSQL学习镜像》

2、欢迎使用云起实验室: 《免费体验PolarDB开源数据库》

3、PolarDB开源数据库内核、应用等学习图谱:  https://www.aliyun.com/database/openpolardb/activity 

1 刨坑

今天是女神节, 分析一下老白一篇关于PG数据文件被删除还能使用的文章:
https://mp.weixin.qq.com/s/4wX0KOiRXq0JJRLoSmgxhA

对于老白提到的场景, 原因是这样的:

  • 1 数据库启动状态下删了一个文件,只要未来有该文件的IO都会报错并导致停库,例如bgwriter或checkpoint写脏页,或者未命中buffer的读请求,理论上不可能没有察觉。

    • 但是存在一种情况, 删除的文件没有被修改过(没有被DML过、也没有被vacuum过、也没有修改过hint bit). 那目前来看没法被数据库主动察觉.

  • 2 如果数据库是正常重启肯定会刷脏,所以也应该会报错并发现问题。

    • 情况如2.

  • 3 immediate重启则会涉及恢复,开启了fpw是可以完全恢复的。这个问题可能和部分表空间恢复的场景类似,一个没有备份的表空间,在进行部分恢复时,元数据没有被剥开,整体恢复可以正常,但是由于原数据在系统表里,也就没备份数据但是备份了元数据,查询到没备份的表空间的表时才会报错。启动时检查元数据和数据文件的完整性可能可以作为一个功能选项更好。

情况与我之前写的部分表空间备份与恢复方法的文章类似:

  • 备份global文件(主要包含全局信息: 用户、数据库、表空间定义, schema定义, 控制文件等.) + wal归档 + 指定表空间文件.

详细参考:

  • 《PostgreSQL如何支持可选择性表空间(Selectivity Tablespace)备份和时间点(PITR)恢复?》

  • 《为什么PostgreSQL支持基于表空间的在线备份和完全恢复?》

  • 《PostgreSQL在线备份&恢复的3个细节问题&原理》

部分表空间备份与恢复其实有一个比较诡异的现象:

  • 在回放wal时, 由于有FPW, 未备份的表空间的数据文件也能正常的回放. 被修改过的块都能恢复出来.

  • 但是没有被修改过的块呢? 在file中是空洞的存在, 取决于file write接口, offset write?

导致的问题: 当我们读未备份的表, 数据就有可能是不准的. 整个逻辑是这样的:

  • 没有备份的表空间其实是不完整的.

  • 但全局数据是完整的, 没有备份的表空间对应的数据结构还在, 例如某个表a.

  • 如果我们去查a表, 它的数据当然可能是不准确的.

回过头来看老白的文章里提到的场景, 其实和上面部分表空间备份与恢复的情况类似.

  • PG规定了单个数据文件的大小上限, 当数据超过一个数据文件大小, 会写append文件, 文件suffix按数字编号, 如17889, 17889.1, 17889.2 .

  • 我们删除一个在最近一次检查点之后没有被DML过、也没有被vacuum过、也没有修改过hint bit的数据文件的append文件.

  • 如果进行count操作, 是发现不了的.

  • 但是索引查询引用到被删除的文件内的坏块时可以被发现.

2 填坑

1、发现坏块: 检查坏块, 开启checksum, 有现成的工具. pg_checksums
2、发现文件被删: 检查元数据和数据文件映射的完备性. 这个通常可以启动时检查, 目前社区没有该功能, 提issue吧. 或者运行过程中检查, 发现完备性问题, 应该要有报错.

pg_class   
pg_relation_filenode
pg_relation_filepath
pg_filenode.map

3 再挖坑

Oracle, 默认未开启fpw. 也有比较难发现的隐藏风险.

如果服务器崩溃, 可能出现partial write的块. 这种数据块也是极难发现, 只有读到时通过块的checksum才会校验出错误. 恢复则需要用到过去的全量备份和redo. 如果你的备份保留时间不够长, 当你发现时可能备份已经被清理了, 那就恢复不回来了.

欢迎关注我的github (https://github.com/digoal/blog) , 学习数据库不迷路.  

近期正在写公开课材料, 未来将通过视频号推出, 欢迎关注视频号:

Image

文章中的参考文档请点击阅读原文获得.