ITPub博客

首页 > 数据库 > 数据库开发技术 > PostgreSQL 17开发调试特性|驱逐缓冲

PostgreSQL 17开发调试特性|驱逐缓冲

原创 数据库开发技术 作者:象楚之行 时间:2024-07-08 07:08:00 0 删除 编辑

在Oracle数据库可以使用"alter system flush buffer cache"命令来驱逐buffer cache,而截至到PG 16并不支持类似的功能,除非重启数据库实例。

PG 17的pg_buffercache插件将增加驱逐buffer cache的功能,新增的pg_buffercache_evict函数可以在数据块级别从shared buffer cache中移除。

注意生产环境普通用户不应该使用这个功能,开发测试人员可以在一些调试场景使用。

本文将在测试环境演示buffer cache的性能以及double buffering的影响。

1.创建pg_buffercache扩展插件

pg_buffercache_evict函数是pg_buffercache扩展的部分功能之一,先创建pg_buffercache扩展

create extension pg_buffercache;

创建扩展后可以看到新增的pg_buffercache_evict函数

postgres=# \df pg_buffercache_evict
List of functions
-[ RECORD 1 ]-------+----------------
Schema | public
Name | pg_buffercache_evict
Result data type | boolean
Argument data types | integer
Type | func

pg_buffercache_evict函数只需要接收一个bufferid参数,即可将该数据块从PG内部的shared buffer cache中驱逐,但此时操作系统的page cache还是缓存数据块,这也是PG的double buffering问题。

2.创建测试表

创建一张包含1百万条记录的测试表

CREATE TABLE test AS SELECT * FROM generate_series(1, 1_000_000);

这张表不大,只有35MB,本地虚拟机环境使用的shared_buffers是128MB

postgres=# \dt+ test
List of relations
-[ RECORD 1 ]-+----------
Schema | public
Name | test
Type | table
Owner | postgres
Persistence | permanent
Access method | heap
Size | 35 MB
Description |

3.简单的顺序扫描测试

使用下面的语句来查询测试表的所有数据

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM test;

执行结果如下:

QUERY PLAN
------------------------------------
Seq Scan on test (cost=0.00..15904.00 rows=1142400 width=4) (actual time=0.222..74.092 rows=1000000 loops=1)
Buffers: shared hit=2048 read=2432 dirtied=2432 written=2400
I/O Timings: shared read=1.856 write=2.999
Planning:
Buffers: shared hit=4
Planning Time: 0.372 ms
Execution Time: 130.821 ms
(7 rows)

语句首次执行可以看到大约有一半的数据加载到shared buffers中

使用pg_prewarm插件对测试表进行完整预热,全部加载到buffer cache中:

postgres=# create extension pg_prewarm;
CREATE EXTENSION
postgres=# select pg_prewarm ( 'test', 'buffer', 'main', null, null );
pg_prewarm
------------
4480
(1 row)
postgres=# select pg_relation_filepath('test');
pg_relation_filepath
----------------------
base/5/16645
(1 row)

postgres=# select count(*) from pg_buffercache where relfilenode = 16645;
count
-------
4485
(1 row)

注意上面pg_prewarm插件加载的数据块是4480个,但pg_buffercache显示的数据块是4485个,因为数据表还有其它分支文件也进行了cache。

postgres=# select relforknumber from pg_buffercache where relfilenode = 16645 and relforknumber != 0;
relforknumber
---------------
1
1
1
1
2
(5 rows)

再次执行刚才的查询语句

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM test;

执行结果如下:

QUERY PLAN
------------------------------------
Seq Scan on test (cost=0.00..14480.00 rows=1000000 width=4) (actual time=0.411..96.530 rows=1000000 loops=1)
Buffers: shared hit=4480
Planning Time: 1.300 ms
Execution Time: 95.058 ms
(4 rows)

全部的数据都加载到了shared buffers。

现在使用新增的pg_buffercache_evict函数将测试表从shared buffers驱逐

SELECT DISTINCT pg_buffercache_evict(bufferid)
FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode('test');

执行完后可以检查测试表已经不在shared buffers中,可以再次运行EXPLAIN ANALYZE,可以看到

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM test;

执行结果如下:

QUERY PLAN
------------------------------------
Seq Scan on test (cost=0.00..14480.00 rows=1000000 width=4) (actual time=0.206..82.959 rows=1000000 loops=1)
Buffers: shared read=4480
I/O Timings: shared read=14.213
Planning Time: 0.118 ms
Execution Time: 133.511 ms
(5 rows)

此时可以观察到全部的数据块由之前的shared hit变为了shared read,也就是从磁盘或者操作系统的page cache读取的数据而不是从buffer cache读取。

此时执行的时间依然较快,并未发生性能变化,因为测试表的数据在操作系统内部的page cache里,独立于PG内核之外,pg_buffercache_evict函数驱逐buffer cache触达不到OS层。

可以使用fincore进行观测验证:

$ fincore /opt/pgdata1700/base/5/16645
RES PAGES SIZE FILE
35M 4864 35M /opt/pgdata1700/base/5/16645

测试环境可以使用dd命令来清理OS cache:

$ dd oflag=nocache conv=notrunc,fdatasync count=0 of=/opt/pgdata1700/base/5/16645

上面的命令告诉操作系统内核,这个文件不在需要了,请帮忙把OS的page cache清理掉。

再使用fincore工具进行确认:

$ fincore /opt/pgdata1700/base/5/16645
RES PAGES SIZE FILE
0B 0 35M /opt/pgdata1700/base/5/16645

可以看到文件大小还是35MB,但OS的page cache已经消失。

再次执行下面的语句:

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM test;

执行结果如下:

QUERY PLAN
------------------------------------
Seq Scan on test (cost=0.00..14480.00 rows=1000000 width=4) (actual time=2.931..109.191 rows=1000000 loops=1)
Buffers: shared hit=33 read=4447
I/O Timings: shared read=50.175
Planning:
Buffers: shared hit=20
Planning Time: 2.551 ms
Execution Time: 152.630 ms
(7 rows)

4.总结

验证buffer cache对性能的影响,以往需要使用pg_dropcache插件,或者手动重启数据库,同时为了清空OS的page cache,还需要使用其它工具进行清理。17版本pg_buffercache插件提供的pg_buffercache_evict函数可以非常方便调试。

推荐阅读

与我联系

  • 微信公众号:象楚之行

  • 墨天轮:https://www.modb.pro/u/15675

  • 微信:skypkmoon

勤耕细作,用心积微;静待花开,量变质成。

原文作者:象楚之行;如有侵权,请联系管理员删除。

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

注册时间:2018-06-12

  • 博文量
    102
  • 访问量
    50362