PostgreSQL缓冲区管理器管理着shared buffer和持久存储之间的数据传输,对数据库性能有着重要影响。本节重要介绍shared buffer重要参数、查看缓冲信息及预热方法。
1、shared buffer参数shared_buffers
PostgreSQL通过自己的buffer和操作系统内核缓冲来缓冲数据,即在内存中存储着两份数据,分别在shared buffer和内核buffer中。和其他数据库不同,PG不提供direct IO。而PG的buffer由shared_buffer参数控制,对数据库调优有重要作用。
这个参数意思是PG为了cache数据需要使用内存大小。默认值是128MB,最小值是128KB。如果没有指定单位,则为block个数,一个block为BLOCKSZ,即默认8KB。这个参数只能重启生效。
这个参数的推荐值是DRAM大小的1/4。也可根据环境进行调整。生产环境中,这个值越大性能越好。
可以通过执行show shared_buffers或从pg_settings视图中查询该参数配置信息:
postgres # show shared_buffers;shared_buffers
----------------128MB
(1 row)postgres # select *,current_setting(name) from pg_settings where name like ‘shared_buf%’;
----------------+-----------------------------------------------------------------
name | shared_buffers
setting | 16384
unit | 8kB
category | Resource Usage /Memory
shart_desc | Sets the number of shared memory buffers used by the server.
extra_desc |
context | postmaster
vartype | integer
source | configuration file
min_val | 16
max_val | 1073741823
enumvals |
boot_val | 1024
reset_val | 16384
sourcefile | /home/yzspm/data/postgresql.conf
sourceline | 121
pending_restart | f
current_setting | 128MB
2、查看shared buffer
2.1 pg_buffercache介绍
使用pg_buffercache可以查看PG数据库共享内存中内容。现在来部署pg_buffercache:
postgres=# CREATE EXTENSION pg_buffercache;
CREATE EXTENSION
pg_buffercache视图结构:
postgres=# \d pg_buffercache
View "public.pg_buffercache"
Column | Type | Collation | Nullable | Default
-----------------+----------+-----------+----------+---------
bufferid | integer | | |
relfilenode | oid | | |
reltablespace | oid | | |
reldatabase | oid | | |
relforknumber | smallint | | |
relblocknumber | bigint | | |
isdirty | boolean | | |
usagecount | smallint | | |
pinning_backends | integer | | |
对于shared buffer中每8b页,pg_buffercache都返回一行。例如:
该视图每列定义如下:
名称 | 描述 |
bufferid | 共享内存中块ID |
relfilenode | 表文件ID |
reltablespace | 关系的表空间ID |
reldatabase | 关系的数据库ID |
relforknumber | 关系的分叉树,比如表主文件为0,fsm、vm文件分别为1和2 |
relblocknumber | 关系的页数 |
isdirty | 是否变脏 |
usagecount | Clock-sweep访问计数 |
pinning_backends | 对这个缓冲区加pin的后端进程数量 |
这个视图通过创建的函数pg_buffercache_pages()进行构建:
CREATE VIEW pg_buffercache ASSELECT P.* FROM pg_buffercache_pages() AS P(bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2,pinning_backends int4);
使用这个插件查询时,需要注意,他会对每个缓冲页进行加锁,所以不建议频繁执行:
pg_buffercache_pages(PG_FUNCTION_ARGS)...for (i = 0; i < NBuffers; i++){//NBuffers为缓冲区块数bufHdr = GetBufferDescriptor(i);buf_state = LockBufHdr(bufHdr);fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;...UnlockBufHdr(bufHdr, buf_state);
}...
2.2 pg_buffercache应用
通过pg_buffercache可以分析缓冲区设置是否合理:
如果有大量内存块其usagecount值普遍处于4-5的值,说明shared_buffers不够用,可以适当调高;如果usagecount值普遍为0-1,则可以适当减少。至于怎样减少,还需结合缓存命中率情况。
计算缓存命中率的方法:
1)可以利用pg_stat_database视图计算,该视图有两个字段blks_read和blks_hit,分别表示未命中和命中页数,那么计算命中率的语句:
注意,这种方式统计的信息包含系统表的信息,所以如果统计某个表的命中率,需要通过方法2。
2)利用pg_stat_user_tables视图计算
该例子表t2的缓冲命中率为100%。如果命中率太低,比如小于60%,那么共享缓冲区太小,需要调高。如果计算索引的缓冲命中率,仅需把heap替换成idx即可。
另外一个第三方插件pgfincore提供了操作系统如何缓冲页面信息,该插件安装及使用方法如下:
同时会显示操作系统缓存中页数,即rel_os_pages字段值。
这个插件详细使用方法可参考:Projects · pgfincore · GitHub
需要注意,执行pgfincore会将数据页预取到操作系统缓存。
在分析一个新数据库时,可以使用下面语句快速查看每个表缓冲了多少数据页,以及其相对于表总大小的百分比:
表t1的索引t1_pkey占cache的37.8%,占表总大小的90.2%,缓存中读取了索引中90%多的数据。数据库将数据保存在内存中非常重要。
3、预热
PG自带插件pg_prewarm可以用于缓冲预热。该工具支持3种预读方式:
- prefetch:异步异读到操作系统page cache,调用的函数posix_fadvise
- read:读取到用户buffer,实际上也在OS cache
- buffer:读取到shared buffer
注意,这种预热工具没有对缓冲区进行保护,可能导致预热的数据页又被替换出去,导致预热失效。通常仅在启动时有用,并且大部分缓存为空。
这个工具支持2种方式:手动和自动。
3.1 手动
1)首先安装该插件:
postgres=# CREATE EXTENSION pg_prewarm;
CREATE EXTENSION
2)创建表并预制数据
postgres=# CREATE TABLE t1(id int)
CREATE TABLE
postgres=# INSERT INTO t1 SELECT * FROM generate_series(1, 1000000);
SELECT 1000000
3)关闭服务并重启
pg_ctl restart
4)预热
test=# SELECT * FROM pg_prewarm('public.t1');
pg_prewarm
------------
5425
(1 row)
这个例子中,调用pg_prewarm函数将表t1预热,有5425个页读取并加载到cache中。上面的方法是最简单的,还有其他方式。看下这个函数定义:
postgres=# \x
Expanded display is on.
test=# \df *pg_prewarm*
List of functions
-[ RECORD 1 ]
----------------------+---------------------------------------------Schema | publicName | pg_prewarmResult data type | bigintArgument data types | regclass, mode text DEFAULT 'buffer'::text,fork text DEFAULT 'main'::text,first_block bigint DEFAULT NULL::bigint,last_block bigint DEFAULT NULL::bigintType | func
参数除了传入想要预热的表名外,还可以通过第二个参数告诉加载哪种文件,是主文件还是fsm或vm文件。通常加载主文件即可。还可以预热独立指定块,即第3和4个参数,告诉加载哪些页。第一个参数告诉使用哪种预热方式,默认是buffer。这种方式比较灵活,但通常并不使用。
3.2 自动
启动时,自动预热才会更加完美。需要将pg_prewarm添加到shared_preload_libraries参数,然后重启服务。例如在postgresql.conf中配置:
shared_preload_libraries = ‘pg_stat_statements, pg_prewarm’
启动后,可以看到“autoprewarm master”进程,这个进程负责预热:
80259 ? Ss 0:00 /home/yzs/bin/postmaster -D /home/yzs/data/
80260 ? Ss 0:00 \_ postgres: logger
80262 ? Ss 0:00 \_ postgres: checkpointer
80263 ? Ss 0:00 \_ postgres: background writer
80264 ? Ss 0:00 \_ postgres: walwriter
80265 ? Ss 0:00 \_ postgres: autovacuum launcher
80266 ? Ss 0:00 \_ postgres: stats collector
80267 ? Ss 0:00 \_ postgres: autoprewarm master
80268 ? Ss 0:00 \_ postgres: logical replication launcher
默认情况下,pg_prewarm会将内存中的块链表存储到磁盘,重启时自动读取这个链表中页号,并把这些对于页读取到cache。