PostgreSQL码农集散地

为什么PG有Double Cache?

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

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

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

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

为什么PG有Double Cache?

https://www.bilibili.com/video/BV1km4y1D7BW/

社区版本:
《DB吐槽大会,第6期 - PG Double Cache》

PG在读、写数据文件时, 使用了Buffer IO的系统调用, 因此会用到Page Cache, 再加上数据库自身的shared buffer, 实际上占用了两份内存.

  • 1、存在浪费内存的现象.

  • 2、多一次内存拷贝的动作, 访问路径变长, 增加了读操作延迟. 影响比较大的可能是大范围数据扫描(OLAP场景).

  • 3、数据库的buffer命中率数据将不准确, 无法判断未命中的block是从page cache读取的还是从块设备中读取的. 使得PG 系统表中命中率的统计参考意义不大.

扩展知识:
1、了解了double cache的问题, 那么为了快速的释放page cache, 可以安装pgfincore插件, 设置open file 的system call的flag为优先释放(对于冷数据). 当然也可以设置为尽量不释放的flag(对于热数据). 这个方法需要业务自己判断, 熟悉哪些是热表哪些是冷表.

2、wal 文件的IO操作略有不同: 可以自动选择DIO或者bufferIO. 当开启了归档、或者存在下游流复制节点时采用bufferIO, 否则使用DIO. 为什么呢?

  • 因为下游流复制节点通常会立即读取刚写入的WAL内容, 采用bufferIO可以避免从块设备读取WAL内容. 归档的原因一样.

  • 没有下游节点时, 使用DIO可以绕过page cache, 省去再调用fsync刷盘的操作.

wal receiver进程接收到wal后也不会使用DIO来写wal文件, 因为startup进程立马就要读出来进行恢复. (除非配置了延迟恢复到参数, 但是这个PG代码没有进行区别对待, 只要是wal receiver写WAL都会使用buffer io.)

3、page cache并不是一无是处:

  • 重启数据库时, page cache中还有热数据. 所以虽然重启后数据库shared buffer中没有热数据, 但是由于数据可能存在page cache中, 就算立即有高并发的访问, 性能抖动也不会太厉害.

  • bgwriter 采用异步IO, 在性能较差的块设备上, append only的写性能依旧可以很好而且很平顺. ( 指: 开启异步wal、或者group commit的情况下. 避免WAL性能问题带来的测试影响. )

  • bgwriter 采用bufferIO时, 一个block如果被更新多次并且刷出去多次, 但是在操作系统层面持久化到物理存储的次数可能更少, 可以间接减少物理IO的调用次数.

  • 操作系统层面会采用IO合并, 例如连续的block可能一次刷出, 减少IO次数.

PolarDB:
1、采用DIO, 是选择, 也是架构使然. 因为采用计算存储分离架构, DIO更便于控制数据刷盘的动作, RW节点只要专注数据库自己的shared buffer写出机制, 而不需要担心操作系统的background flush操作扰乱刷脏机制.
2、怎么解决重启实例后性能抖动的问题? 做了持久化内存池. 在数据库重启时BufferPool并不销毁,如下图所示:crash和restart期间BufferPool不销毁。
内核中的共享内存分成2部分:

  • 全局结构,ProcArray等。

  • BufferPool结构;其中BufferPool通过具名共享内存来分配,在进程重启后仍然有效。而全局结构在进程重启后需要重新初始化。

BufferPool中并不是所有的Page都是可以复用的,比如:在重启前,某进程对Page上X锁,随后crash了,该X锁就没有进程来释放了。因此,在crash和restart之后需要把所有的BufferPool遍历一遍,剔除掉不能被复用的Page。另外,BufferPool的回收依赖k8s。该优化之后,使得重启前后性能平稳。
参考: https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/blob/main/doc/PolarDB-CN/Architecture.md

本期问题1:
下面哪个插件可以控制page cache的释放机制: 优先释放、尽量不被释放?

  • a. pg_hint_plan

  • b. pgfincore

  • c. pageinspect

  • d. pg_buffercache

答案:

  • b

解释:

  • pgfincore插件, 设置open file 的system call的flag为优先释放(对于冷数据). 当然也可以设置为尽量不释放的flag(对于热数据). 这个方法需要业务自己判断, 熟悉哪些是热表哪些是冷表.

本期问题2:
下面哪些是double cache带来的问题?

  • a. 写性能下降

  • b. 浪费内存

  • c. 从磁盘读大量数据的性能下降

  • d. 内存命中率显示不准确

答案:

  • bcd

解释:

  • 1、存在浪费内存的现象.

  • 2、多一次内存拷贝的动作, 访问路径变长, 增加了读操作延迟. 影响比较大的可能是大范围数据扫描(OLAP场景).

  • 3、数据库的buffer命中率数据将不准确, 无法判断未命中的block是从page cache读取的还是从块设备中读取的. 使得PG 系统表中命中率的统计参考意义不大.

本期问题3:
PolarDB采用DIO后, 怎么解决重启实例shared buffer清空导致的性能抖动问题?

  • a. pg_prewarm

  • b. 持久化bufferpool

  • c. 实时记录热数据元信息, 启动时自动加载热数据

  • d. 内存镜像

答案:

  • b

解释:

  • PolarDB 做了持久化内存池. 在数据库重启时BufferPool并不销毁,crash和restart期间BufferPool不销毁。

本期问题4:
WAL日志什么时候会采用DIO?

  • a. pg_basebackup 接收wal

  • b. wal sender

  • c. 没有开启wal sender、没有使用归档、非wal receiver进程

  • d. 使用windows时

答案:

  • c

解释:

  • wal 文件的IO操作略有不同: 可以自动选择DIO或者bufferIO. 当存在下游流复制节点时采用bufferIO, 否则使用DIO. 为什么呢?

    • 因为下游流复制节点通常会立即读取刚写入的WAL内容, 采用bufferIO可以避免从块设备读取WAL内容.

    • 没有下游节点时, 使用DIO可以绕过page cache, 省去再调用fsync刷盘的操作.

  • wal receiver进程接收到wal后也不会使用DIO来写wal文件, 因为startup进程立马就要读出来进行恢复. (除非配置了延迟恢复到参数, 但是这个PG代码没有进行区别对待, 只要是wal receiver写WAL都会使用buffer io.)

src/backend/access/transam/xlog.c  

/*
* Optimize writes by bypassing kernel cache with O_DIRECT when using
* O_SYNC/O_FSYNC and O_DSYNC. But only if archiving and streaming are
* disabled, otherwise the archive command or walsender process will read
* the WAL soon after writing it, which is guaranteed to cause a physical
* read if we bypassed the kernel cache. We also skip the
* posix_fadvise(POSIX_FADV_DONTNEED) call in XLogFileClose() for the same
* reason.
*
* Never use O_DIRECT in walreceiver process for similar reasons; the WAL
* written by walreceiver is normally read by the startup process soon
* after it's written. Also, walreceiver performs unaligned writes, which
* don't work with O_DIRECT, so it is required for correctness too.
*/

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

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

Image

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