新年的第一篇博客~

一、 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.confsysctl -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';

四、 buffer查找与驱逐

为一个page选择buffer有两种可能场景:

  • 服务刚启动,所有buffer都是空的
  • 一段时间后,没有了free buffer,此时pg就要选择一些页,将它们驱逐出buffer

每当buffer被访问,其usage count会+1,每当buffer manager尝试驱逐页时,会将buffer的usage count - 1,最先被减到0的说明最不常使用,会被驱逐。

当然,如果所有buffer的usage count都较大,buffer manager不可能一直循环去减,pg给它设了上限,最多循环5次。

选择了要驱逐的页后,如果不是脏页,则删除该页与hash table的引用关系;如果是脏页,还需将数据写回disk。然后,buffer manager将新页读入找到的buffer。

五、 Bulk Eviction and buffer ring 大规模驱逐与环形缓冲区

1. 为什么需要buffer ring

试想按照前面的方法,如果是突然读入一个大表,可能会将buffer中的绝大多数页都驱逐走,而如果大表只是简单查询一次,这样做会得不偿失。在oracle 11g中引入了direct path read避免这种问题,而在pg中,使用的则是环形缓冲区(buffer ring)。

buffer ring由特定大小的buffer数组组成,顾名思义它是环形的,就算进行buffer驱逐也只会在这个环内进行。

2. 3种驱逐策略:

  • Bulk reads strategy:用于大表(size超过1/4 buffer cache的表)的顺序扫描,使用256KB(32个标准页)的 buffer ring。这种策略不支持将脏页写入磁盘以释放buffer,而是将ring中的buffer替换为另一个,这样不需要等IO完成,速度会更快。
  • Bulk writes strategy:用于COPY FROM,CTAS,CREATE MATERIALIZED VIEW等大量写入的命令,以及会造成表rewrite的ALTER TABLE语句。pg默认为这些操作分配16MB的buffer ring(2048个标准页),但不会超过buffer cache的1/8。
  • Vacuuming strategy:用于需要进行全表扫描的vacuum操作,使用256KB(32个标准页)的 buffer ring。

3. 案例

shared_buffers大小默认是128MB(16384 pages),按照规则1,超过1/4即4096 pages的表在全表扫描时会采用buffer ring。

CREATE TABLE big(id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY,s char(1000)
) WITH (fillfactor = 10);INSERT INTO big(s) SELECT 'FOO' FROM generate_series(1,4096+1);ANALYZE big;SELECT relname, relfilenode, relpages FROM pg_class WHERE relname IN ('big', 'big_pkey');

重启一下pg,执行全表扫描

EXPLAIN (analyze, costs off, timing off, summary off)
SELECT * FROM big;SELECT count(*)
FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode('big'::regclass);

可以看到buffer ring刚好占用32个page,256KB

再重启一下,可以看到如果单纯走索引,则不会用到buffer ring

EXPLAIN (analyze, costs off, timing off, summary off)
SELECT id FROM big;SELECT count(*)
FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode('big'::regclass);

查询各表的缓存情况

SELECT c.relname,
count(*) blocks,
round( 100.0 * 8192 * count(*) /
pg_table_size(c.oid) ) AS "% of rel",
round( 100.0 * 8192 * count(*) FILTER (WHERE b.usagecount > 1) /
pg_table_size(c.oid) ) AS "% hot"
FROM pg_buffercache b
JOIN pg_class c ON pg_relation_filenode(c.oid) = b.relfilenode
WHERE b.reldatabase IN (
0, -- cluster-wide objects
(SELECT oid FROM pg_database WHERE datname = current_database()))
AND b.usagecount IS NOT NULL
GROUP BY c.relname, c.oid
ORDER BY 2 DESC
LIMIT 10;

六、 Local Cache本地缓存

由于临时表的数据只会被单个进程看到,因此它并不使用前面提到的buffer机制,而使用local cache。其最大值是由temp_buffers参数决定的。

如果你在执行计划中看到了local,就说明它使用了local cache。

CREATE TEMPORARY TABLE tmp AS SELECT 1;
EXPLAIN (analyze, buffers, costs off, timing off, summary off) SELECT * FROM tmp;

七、 缓存预热 Cache Warming

预热功能使用pg_prewarm模块,将数据缓存至os或pg缓冲区

1. 安装与简介

CREATE EXTENSION pg_prewarm;
-- 注意先show查看下有没设置别的插件
ALTER SYSTEM SET shared_preload_libraries = 'pg_prewarm';
-- 重启db生效
show shared_preload_libraries;

如果没有修改过pg_prewarm.autoprewarm设置,在pg启动时会自动启动一个auto-prewarm leader。如果设置了pg_prewarm.autoprewarm_interval,该进程会间隔指定秒数将cache的page刷入磁盘。

如果没有设置,默认不会预热表

SELECT count(*)
FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode('big'::regclass);

2. pg_prewarm函数

pg_prewarm(regclass, mode text default 'buffer', fork text default 'main',first_block int8 default null,last_block int8 default null) RETURNS int8
  • 参数1:要预热的表
  • 参数2:要使用预热的方法(3种)
  • 对操作系统发出异步prefetch请求
  • 读取块的请求范围,但可能会较慢
  • 缓冲区将请求的块范围(执行的查询)读入数据库缓冲区缓存中。
  • 参数3:relation fork被预热
  • 参数4:预热的第一个块号
  • 参数5:预热的最后一个块号
  • 返回值:prewarm块的数量

最简单的用法

SELECT pg_prewarm('big');

这些数据会被dump到$PGDATA/autoprewarm.blocks文件中,可以等待autoprewarm leader进程完成,也可以手动dump。

SELECT autoprewarm_dump_now();

这个数字大于4097是因为所有用到的buffer都dump了,文件是文本格式,可以查看,包含了database,tablespace,file,segment ID

重启,再查看big表预热情况

预热数据也没有对缓存驱逐的特殊保护,其他系统活动可能会在读取后不久将预热块驱逐出去;反之,预热也可能从buffer中驱逐其他数据。因此,预热通常在启动时(当缓存大部分为空时)最有用。

3. os缓存插件pgfincore

pgfincore用于查看os cache中的对象信息,以及将db中的表或索引直接缓存到os cache中,具体可以参考:https://github.com/klando/pgfincore

参考

PGCE课程《高速缓存》

postgresql_internals-14 学习笔记(五)Buffer Cache相关推荐

  1. Polyworks脚本开发学习笔记(五)-变量使用基本语法

    Polyworks脚本开发学习笔记(五)-变量使用基本语法 定义变量及赋值 定义各种类型的变量 定义变量时,只需要使用DECLARE 关键字即可定义,为了区别变量和脚本中的其它字符,建议都以小写v开头 ...

  2. 【K210】K210学习笔记五——串口通信

    [K210]K210学习笔记五--串口通信 前言 K210如何进行串口通信 K210串口配置 K210串口发送相关定义 K210串口接收相关定义 K210串口发送接收测试 完整源码 前言 本人大四学生 ...

  3. Halcon 学习笔记五:几何定位+仿射变换+测量

    Halcon 学习笔记五:几何定位+仿射变换+测量 定位流程 例子一 例子二(药片的定位) 例子三(充电宝定位) 例子四(车牌定位) 其他 定位流程 blob分析 模板匹配 (仿射变换成标准的形状) ...

  4. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  5. Ethernet/IP 学习笔记五

    Ethernet/IP 学习笔记五 Accessing data within a device using a non-time critical message (an explicit mess ...

  6. StackExchange.Redis学习笔记(五) 发布和订阅

    StackExchange.Redis学习笔记(五) 发布和订阅 原文:StackExchange.Redis学习笔记(五) 发布和订阅 Redis命令中的Pub/Sub Redis在 2.0之后的版 ...

  7. 吴恩达《机器学习》学习笔记五——逻辑回归

    吴恩达<机器学习>学习笔记五--逻辑回归 一. 分类(classification) 1.定义 2.阈值 二. 逻辑(logistic)回归假设函数 1.假设的表达式 2.假设表达式的意义 ...

  8. 好程序员教程分析Vue学习笔记五

    好程序员教程分析Vue学习笔记五,上次我们学习了Vue的组件,这次我们来学习一下路由的使用.在Vue中,所谓的路由其实跟其他的框架中的路由的概念差不多,即指跳转的路径. 注意:在Vue中,要使用路由, ...

  9. 【AngularJs学习笔记五】AngularJS从构建项目开始

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...

  10. ROS学习笔记五:理解ROS topics

    ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...

最新文章

  1. Windows基础题目
  2. [转] 初识Firebug(HTML查看和编辑、Javascript控制台、网络状况监视器)
  3. Language-Directed Hardware Design for Network Performance Monitoring——Marple
  4. Spring Bean 后置处理器
  5. 第三章用sql语句操作数据
  6. java序列化写法_java-spark的各种常用算子的写法
  7. 听歌也能倍速了!网易云音乐PM怎么想的?
  8. 微信小程序 全局共享数据
  9. crontab 不能执行git命令问题备忘
  10. java alt是什么意思_java中这都是什么意思? - 执着的笨蛋 - BlogJava
  11. sql select distinct常见错误_这8种常见的SQL错误用法,80%的程序员还在犯
  12. office设置保护色
  13. 华为安装gsm框架_华为nova5怎么下载安装谷歌服务助手,安装GMS框架教程
  14. Google推出即时通讯软件Hello
  15. unity与3DMax的单位问题
  16. 开源项目material-dialogs使用
  17. 在今日头条有粉丝17万月入万把块,究竟今日头条要怎么挣钱?
  18. 冬日丹崖风貌--承德碧霞山
  19. Android将webp格式图片转换为png格式方法
  20. 线性代数(2)行列式6种运算性质

热门文章

  1. aspire鹦鹉螺_【电子烟教程】aspire鹦鹉螺雾化器使用技巧 - 从入门到精通玩转电子烟...
  2. Photoshop初学者教程:解析色彩平衡原理
  3. 直方图、曲线与色彩平衡
  4. js-双重for循环-输入行列打印爱心
  5. cmd命令行返回上一级目录、切换到其他的盘、看一下该目录都有什么文件、打开该目录的某文件、改变命令颜色、快速进入某文件目录的方法
  6. 调试Bluetooth时的一些札记
  7. 关于DataStage Designer
  8. JDK7-hashmap源码
  9. vector的resize函数和reserve函数
  10. 2020年总结,世界不太平的一年