新年的第一篇博客~
一、 Buffer Cache简介
1. 主要用途
调和内存(ns级)与磁盘(ms级)间的速度差异。
pg不仅用自己的buffer cache,也用os cache,所以它使用了“双缓存”,这也是很多文档推荐shared_buffer参数只设为内存25%(通常不超过16G)的原因。
2. os参数
-
shmall # 单个共享内存段的最大大小(字节为单位) shmmax # 服务器上所有进程可以使用的共享内存的总页数
推荐设置:将shmall设置为总内存的一半,太小会导致PG服务启动报错
设置脚本 vi shmsetup.sh
#!/bin/bash
page_size=`getconf PAGE_SIZE`
phys_pages=`getconf _PHYS_PAGES`
shmall=`expr $phys_pages / 2`
shmmax=`expr $shmall \* $page_size`
echo kernel.shmmax = $shmmax
echo kernel.shmall = $shmall
执行脚本
./shmsetup >> /etc/sysctl.conf
sysctl -p
3. db参数
估计共享内存消耗时,最需要关注的是shared_buffers的取值(调整后需重启生效)。
在pg中,buffer cache miss不意味着一定有物理IO,它还可以用到os cache,当然os cache不会按照db buffer cache那套重置和剔除策略,是用os自己的。
通常建议是将shared_buffers初始值设置为os内存的1/4,之后根据压测和运行情况调整。
- 查看shared_buffers
show shared_buffers;
select name,setting,unit,current_setting(name) from pg_settings where name='shared_buffers';
current_setting()函数和show 命令一样,用来显示配置的基本信息,也可以在查询中使用。
所有操作系统都给予应用一种强制从高速缓存写入磁盘的方法
- wal_sync_method参数决定PG如何请求内核强制将WAL更新到磁盘
二、 Buffer Cache设计
1. Buffer Cache结构
Buffer Cache位于共享内存中,所有进程均可访问
header包含以下信息:
- 页的物理位置(文件id、fork、fork中的块号)
- 该页是否为脏页
- buffer使用计数
- pin count (or reference count)
2. pg_buffercache插件
pg_buffercache插件可以实时检查共享缓冲区
CREATE EXTENSION pg_buffercache;
- 共享的系统目录被显示为属于数据库0
- 缓冲区是所有数据库共享,通常会有不属于当前数据库的关系的页面
- 可以与pg_class关联,将连接限制于reldatabase等于当前数据库 OID 或零的行
注意:经常读取这个视图还是会对数据库性能产生一些影响
可以通过查看系统shared_buffers的大小来确认该模块是否工作正常(db刚启动时除外):
select setting, unit from pg_settings where name = 'shared_buffers';
select count(*) from pg_buffercache;
创建一个简单函数帮助查看
CREATE FUNCTION buffercache(rel regclass)
RETURNS TABLE(
bufferid integer, relfork text, relblk bigint,
isdirty boolean, usagecount smallint, pins integer
) AS $$
SELECT bufferid,
CASE relforknumber
WHEN 0 THEN 'main'
WHEN 1 THEN 'fsm'
WHEN 2 THEN 'vm'
END,
relblocknumber,
isdirty,
usagecount,
pinning_backends
FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode(rel)
ORDER BY relforknumber, relblocknumber;
$$ LANGUAGE sql;
3. 测试案例
CREATE TABLE cacheme(id integer) WITH (autovacuum_enabled = off);
INSERT INTO cacheme VALUES (1);
SELECT * FROM buffercache('cacheme');
SELECT usagecount, count(*)
FROM pg_buffercache
GROUP BY usagecount
ORDER BY usagecount;
usagecount为空代表free buffer
三、 cache命中与未命中
pg通过哈希表定位cache中的页,hash key由文件id、fork、fork中的块号(上面提到的header内容)组成。
每当使用到cache中的页,usagecount会增加
EXPLAIN (analyze, buffers, costs off, timing off, summary off) SELECT * FROM cacheme;
SELECT * FROM buffercache('cacheme');
使用游标时,则会用到cache pin
BEGIN;
DECLARE c CURSOR FOR SELECT * FROM cacheme;
FETCH c;
SELECT * FROM buffercache('cacheme');
如果无法pin到该页,通常pg会跳过并选择下一个页。
vacuum操作中有时可以看到,另开一个会话
VACUUM VERBOSE cacheme;
由于游标还未关闭,这些页不能从pinned buffer中移除,因此会被跳过。
但如果是必须将这些页从pinned buffer中移除的操作(例如vacuum freeze),则会申请获得排他闩锁,被阻塞直到将这些页成功移除。
提交游标会话,可以看到vacuum freeze随即执行成功,并且pins全变为0。
另外,插入数据并不会影响pin
重启pg,再次执行上面查询
read表示从磁盘中读取,dirtied表示页已变脏
查看命中情况
SELECT heap_blks_read, heap_blks_hit FROM pg_statio_all_tables WHERE relname = 'cacheme';