首页 > 数据库 > 数据库开发技术 > PostgreSQL 17开发调试特性|驱逐缓冲
在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的影响。
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问题。
创建一张包含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 |
使用下面的语句来查询测试表的所有数据
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)
验证buffer cache对性能的影响,以往需要使用pg_dropcache插件,或者手动重启数据库,同时为了清空OS的page cache,还需要使用其它工具进行清理。17版本pg_buffercache插件提供的pg_buffercache_evict函数可以非常方便调试。
微信公众号:象楚之行
墨天轮:https://www.modb.pro/u/15675
微信:skypkmoon
勤耕细作,用心积微;静待花开,量变质成。
原文作者:象楚之行;如有侵权,请联系管理员删除。