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

PostgreSQL如何清理Buffer Cache

前言

昨天看到了一个问题,PostgreSQL如何清理Buffer Cache?。让我们来探讨一下这个问题。在Oracle数据库中清理Buffer Cache和Shared Pool有alter system的命令,而MySQL也有innodb_max_dirty_pages_pct
这样的参数可以进行控制,但在PostgreSQL中,您会发现基本上没有这些选项。

如何清理Buffer Cache

以前编写参数优化时曾发布过一个图,PostgreSQL使用自己的缓冲区,以及Linux操作系统的内核缓冲OS Cache。

因此,在清理缓存时,我们不仅要清理shared_buffer
中的数据,还要清除所有OS Cache
。在O'REILLY的网站上可以看到《PostgreSQL9.6 High Performance》提到的方法,即停止数据库,然后使用操作系统命令清除os缓存。

我们来按照这个方法测一下。为了方便观察,我们需要安装插件pg_buffercache

postgres=# create extension pg_buffercache;postgres=# CREATE TABLE test(id integer, name text);postgres=# INSERT INTO test SELECT i, 'name ' || i FROM generate_series(1, 100000) i;SELECT     c.relname,     pg_size_pretty(count(*) * 8192as buffered,     round(100.0 * count(*) / (SELECT setting FROM pg_settings WHERE name='shared_buffers')::integer1AS buffer_percent,     round(100.0 * count(*) * 8192 / pg_table_size(c.oid), 1AS percent_of_relation 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.oid, c.relname ORDER BY 3 DESC LIMIT 10;               relname             |  buffered  | buffer_percent | percent_of_relation ---------------------------------+------------+----------------+--------------------- test                            | 4344 kB    |            3.3 |                99.5 pg_statistic                    | 112 kB     |            0.1 |                31.1 pg_operator                     | 112 kB     |            0.1 |                77.8 pg_constraint_conparentid_index | 8192 bytes |            0.0 |                50.0 pg_amop                         | 48 kB      |            0.0 |                60.0 pg_amproc                       | 32 kB      |            0.0 |                50.0 pg_am                           | 16 kB      |            0.0 |                40.0 pg_constraint                   | 8192 bytes |            0.0 |                16.7 pg_depend                       | 40 kB      |            0.0 |                 8.1 pg_cast                         | 16 kB      |            0.0 |                33.3(10 rows)

你可以看到这里的test表,当前在缓冲区中,占比3.3%。

我们按照书中的方法,关闭数据库,然后清除os缓存后。

pg_ctl stopsyncecho 3 > /proc/sys/vm/drop_cachespg_ctl start 

再次查看可以看到刚刚的test对象已经被清理出了缓冲区。

那么你可能会问,有没有什么别的办法,不用重启数据库也能清理Buffer Cache?

这是可以的,比如你可以下载pg_dropcache插件或者是pg_dropbuffers插件,这是两个不同的插件。但是实现原理都差不多,基本上利用了bufmgr.h
中的函数。但是这两个插件的缺点就是不支持13.2,直接编译就会报错,原因后面我会详细说明。这些插件也都基本3年没有更新了。

我先来说说这些插件实现的基本原理,清空全部内存利用了PG内置的DropDatabaseBuffers
函数。我们来看这个函数的定义。

DropDatabaseBuffers
函数将移除所有的buffer cache。但是要注意得一点是脏页只是被简单的丢弃了,没有先写入磁盘,所以这个动作的风险很大。

其次他还调用了一个函数DropRelFileNodeBuffers
按照表来清空内存。

这个函数的作用就是删除相关表所有在内存中缓冲的page,特别是firstDelBlock=0时,将删除所有页面。而这个操作也是很有风险的,也是直接丢弃脏页。

我们现在在看看它插件的代码。

这里的代码只有3个参数,而上面的有4个参数。所以编译就会失败。

extern void DropRelFileNodeBuffers(struct SMgrRelationData *smgr_reln, ForkNumber *forkNum,int nforks, BlockNumber *firstDelBlock);

PostgreSQL 13.2版本该函数的第一个入参是结构体SMgrRelationData
SMgrRelationData
这个结构体代表了单个表的文件结构。而之前PostgrSQL版本这个函数的输入参数是RelFileNodeBackend

extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,ForkNumber forkNum, BlockNumber firstDelBlock);

所以新版本编译不了。但是它的全库清理缓存是可以用的,那么我们就简单的把这个DropRelFileNodeBuffers
函数给删除,不使用单个清理的功能。所以这个代码就及其简单。

再次编译安装,这次没任何问题,这么简单的代码是不可能出问题的。

再次测试我编译的pg_dropcache,这次直接成功了。把一大堆的缓存全部清空出了buffer cache。

后记

针对这个数据库版本的问题,后续再研究一下,看看应该怎么把这个结构体传进去,自己开发一款高版本的插件。

参考链接

1.https://github.com/zilder/pg_dropcache/
2.https://maahl.net/pg_dropbuffers
3.https://www.oreilly.com/library/view/postgresql-96-high/9781784392970/b42a3be7-18cf-48e3-a51e-cb99cd075478.xhtml

文章转载自励志成为postgresql大神,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论