暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

再聊聊PG数据库的shared buffers配置策略

数据库技术闲谈 2021-12-16
317

今天是 PGCONF 2021的第二天,我还是发一篇和PG相关的文章,算是凑个热闹吧。今天我们来一次比较有意义的炒冷饭,把以前很多人讨论过的 shared buffers 配置策略与 double buffering 的问题。在今天的分享中,我加入了一些我们最近研究的成果。正是因为 PG 和 ORACLE 在存储架构上的不同,形成了 PG 数据库目前 double buffering 的优化设计。

关于 PG shared buffers 的配置策略,在网上一直有很多不同的声音。PG 的官方手册上建议 shared buffers 设置为 1/4 的物理内存,不用设置太大,让更多的内存可以被 OS CACHE 使用,通过 double buffering 机制来优化你的数据库 IO,设置过大的 shared buffers 可能反而会导致数据库性能下降。不过我翻阅了medium、serverfault 等IT论坛,以及一些 PG 大佬的博客,也有一些人说,他们的系统甚至使用了物理内存的 80% 作为 shared buffers ,获得了很好的效果,数据库的总体性能得到了极大的提升。

为什么会出现如此截然不同的观点呢?难道其中一种观点是错误的吗?以前我也写过一篇关于 PG double buffering的文章,不过我感觉那时候仅仅从 PG 的shared buffers 和 OS CACHE 出发,并没有考虑 PG 与 ORACLE 截然不同的文件存储结构。因此并没有抓住问题最关键的那一点,通过这段时间的实践,我们终于把这两个概念联系到一起了,这两个知识点一串联,问题就清晰了,因此今天把这个问题拿出来再来讨论讨论。

首先无论是 ORACLE的 DB CACHE 还是 PG 的 shared buffers ,其目的不外乎几个。第一,减少数据库服务器的物理IO,从而提升数据库的整体效率,减少物理IO的工作大部分可以由 shared buffers 来做;第二,缓冲数据,削峰填谷,和系统中所有环节上的 CACHE 一样,OS CACHE 也可以削平 OS 上的大流量 IO ,从而让系统处于较为平稳的运行状态。

和数据库的缓冲区不同的是,OS CACHE 是一种管理更为简单的,更为灵活的缓冲系统,不过文件缓冲对于数据块 IO 的减少,并不够专业。OS CACHE 并不从数据库的角度去缓冲数据,而是通过预读等机制来优化文件系统的IO性能。而shared buffers 是和数据库的一致性访问紧密相关的缓冲区,里面存储的都是数据库使用的数据块,对 shared buffers 的访问都必须通过 lwlock 来进行并发控制,其访问成本相对较高。因此,通过 shared buffers 和 OS CACHE 之间的访问量的平衡,可以更好的控制数据库访问数据的性能。

引用一张网络上的图片,当 Backend 要读取数据的时候,首先要通过 lwlock 去锁定相关的内存结构,然后对 shared buffers 进行一致性读取,如果找到了,那么就产生了一个逻辑读,这种情况我们在后面称之为方式1;如果没有找到,那么就会产生一个“逻辑物理读”操作,去操作系统中读取这个数据。这里我用了一个“逻辑物理读”这个十分别扭的词汇,是因为这个物理读有可能并没有产生真正的物理读,而只是从 OS CACHE 中读取到了这部分数据。这种情况我们在后面称之为方式2;如果在OS CACHE里还是没有找到,那么就要产生实际的物理读了,从数据文件中把数据块读入 OS CACHE 和 shared buffers ,这是方式3。

方式3是成本最高的,其次是方式2,访问成本最低的是方式1。因此无论是什么样的数据块系统,最好都通过方式1来获取数据。把 shared buffers 的命中率提高到最高,就能让绝大多数的访问都是使用方式1的。对于Oracle数据库来说,因为 ORACLE 使用了表空间这种文件存储结构,文件空间的地址相对固定,因此只要DB CACHE 的大小足够大,那么一个数据块被读取到 DB CACHE 中之后,会被更长时间的保存在 DB CACHE 里,被多次访问。表中新写入的数据或者 UPDATE 一条数据,也是尽可能先写入已经放在 DB CACHE 的块中,或者直接在 DB CACHE 中修改。

PG 数据库的物理存储架构与 ORACLE 大不相同,表数据是存储在独立文件中的,建立一张新的表,就会创建一个新的文件,修改一条数据,也不会在原来的记录上修改,而是会新产生一条记录,这条记录很可能会写在一个不同的块中。因此一个数据块存储到 shared buffers 中之后,很可能多次被访问的几率没有 ORACLE高,这样 shared buffers 的效率就没有 ORACLE DB CACHE 这么高了。因此,针对某些数据重用率不高的应用,shared buffers 设置的太大,反而会挤占 OS CACHE的容量,从而让数据库的整体性能变得更差。而实际上,如果我们的应用是能够很好的多次访问相同的数据的情况下,比如写入操作比较少,读操作比例比较高的数据库中,设置一个较大的 shared buffers,让逻辑读的比例提高,肯定是能够获得较好的性能的。

如果你的数据库中已经安装了 pg_buffercache 插件,那么我们可以通过下面的语句来分析shared buffers中缓冲的效率。


SELECT
 c.relname, count(*) AS buffers,usagecount
FROM pg_class c
 INNER JOIN pg_buffercache b  ON b.relfilenode = c.relfilenode
 INNER JOIN pg_database d  ON (b.reldatabase = d.oid AND d.datname = current_database())
GROUP BY c.relname,usagecount
ORDER BY usagecount,c.relname;


这个图的第二个列是某个关系在 shared buffers 中缓冲的块的数量,第三列是访问的计数器。从这个图来看,shared buffers 中的数据的重复访问率并不高,这套系统如果使用较大的 shared buffers 可能效果不一定很好。

如果系统中存在一些较为复杂的查询语句,会反复查询某些热数据,那么,如果能把一些热数据的表和索引长期缓冲在 shared buffers 中,那么对 PG 数据库的性能提升就十分明显了。通过今天我们对 double buffering 的再理解,终于真正搞清楚了,为什么会有不同的专家,对 shared buffers 的设置的建议会如此迥然不同了。

最后划一下重点,总结一下今天这篇文章的主要观点。

首先,PG 的 shared buffers 和 OS CACHE 共同发挥作用,可以让PG数据库有效的减少物理IO,提升数据库的总体性能;其次,Shared buffers 的设置并不是越大越好,shared buffers 的命中率也没有ORACLE DB CACHE命中率那么有指向性,没有命中的 PAGE 也可能不需要物理读,可以直接从 OS CACHE 中读取到;第三,通过对应用系统的访问特性的分析,根据数据页在 shared buffer 中可能被多次访问的比例,以及这部分数据的大小,可以大致确定 shared buffers 的合理配置,同时根据应用的访问特性,可以大致确定 OS CACHE 与 SHARED BUFFERS 的比例;第四,根据服务器的 IO 能力和数据库的 IO 访问流量,通过调整 OS 层面的脏页写入策略参数和 PG 数据库的 CHECKPOINT 相关参数,可以获得一个较匹配的配置,增加系统的 IO 总吞吐量,降低平均 IO 延时,这对于大系统上做 PG 优化十分重要。





以下是个人新增。


不管是传统数据库还是新兴的国产数据库或者分布式数据库,即使用 SSD 替换了机械盘,数据库的物理IO对性能依然有很大影响。观察数据库的物理 IO 是件很有趣的事情。


文章转载自数据库技术闲谈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论