暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL缓存的脏数据处理

叶同学专栏 2022-07-29
1664

    在PostgreSQL数据库中 ,数据更改是先写入缓冲池中(shared buffers),缓冲池里面的这些数据更改,在事务提交时,是无需同步写入到磁盘的,因为在事务提交时,会先写入WAL日志,有了WAL日志,就可以在异常情况下将数据恢复,保障数据安全,因此数据本身是否在提交时写入磁盘就没那么重要了。数据库是只是在需要的时候,例如脏页较多时、或一定时间间隔后,才将数据写回磁盘 。

PostgreSQL的bgwriter、checkpointer和backend都可能把脏数据回写到存储上。

  • checkpointer ,是以特定的时间间隔刷新所有脏页,并创建一个用于数据库恢复用的检查点 。

  • bgwriter ,是在检查点之间刷新一些脏页面,以便始终有足够多的干净页面可以使用 。

  • backend ,是在申请 shared buffer时找到了脏块,要先把脏块写回磁盘,再清掉脏块,往shared buffer写入新的数据,这操作开销是很高的。

bgwriter的作用

  • 提高了缓存的替换速度,提高了数据查询性能。因为数据库在进行查询处理时,若发现要读取的数据不在缓冲区要先从磁盘中读入该页,这时如果缓冲区已满,就需要先选择一些缓冲区中的页面替换出去。如果要被替换的页被修改了,则必须先将这个页面读出到磁盘才能替换,这样数据库的查询就会被阻塞在这里。通过bgwriter定期的写出缓冲区的部分页面,就可以为缓冲区腾出空间。就可以防止这一情况。

  • 同时,因为bgwriter预先写出了一些脏页面,可以减少checkpoint时要进行的io操作,使系统的IO负载趋于平稳。

但如果bgwriter设置得不合理的话,会导致更新的数据经常被刷到磁盘,导致不必要的IO负载。

bgwriter配置参数

名称含义默认值
bgwriter_delay每次执行bgwriter的间隔时间200ms
bgwriter_lru_maxpages每次bgwriter任务写buffer的最大page数,一旦达到这个数量,bgwriter就结束任务开始休息,也就是说bgwriter休眠200毫秒,然后写入几十毫秒就又开始了休息100
bgwriter_lru_multiplier用于评估下一次写任务的数量,其依据是这段时间内新申请的buffer数量的倍数,如果这个参数乘以新增加buffer分配的数量后小于bgwriter_lru_maxpages,那么有可能下一个写任务的数量会小于预期的写入量。将这个参数调大,会增加bgwriter回写脏块的数量,不过会增加写IO压力,将这个参数调小,可以降低写IO压力,不过可能导致backend写脏块的比例增加。不同负载的系统中,这个参数的值应该是需要做调整的,而不是只是用缺省值2。如果你发现你的系统的写IO压力还不大,但是 bgwriter写比例偏低,backend写比例偏高,那么尝试加大这个参数还是有效的。2
bgwriter_flush_after当局部sync请求达到的最大数,会触发处理512KB

bgwriter监控

pg_stat_bgwriter视图提供了一组共享缓冲区写入方面性能数据

postgres=# select * from pg_stat_bgwriter;
-[ RECORD 1 ]---------+------------------------------
checkpoints_timed     | 53418
checkpoints_req       | 34
checkpoint_write_time | 2661874250
checkpoint_sync_time | 384635
buffers_checkpoint   | 607926853
buffers_clean         | 31275113
maxwritten_clean     | 287343
buffers_backend       | 1489980490
buffers_backend_fsync | 0
buffers_alloc         | 8503586752
stats_reset           | 2022-01-18 09:11:16.021864+08

字段说明

类型描述
checkpoints_timed
bigint
计划检查点的发生次数,这种检查点是checkpoint_timeout参数规定的超时达到后系统启动的checkpoint;
checkpoints_req
bigint
非计划检查点的次数,包含手动的检查点、xlog检查点(比如WAL已经超出了max_wal_size或者checkpoint_segments触发的ckpt)
checkpoint_write_time
double precision
检查点写入的总时长,以毫秒为单位。
checkpoint_sync_time
double precision
检查点同步文件的总时长,以毫秒为单位。
buffers_checkpoint
bigint
检查点清理的脏块
buffers_clean
bigint
bgwriter清理的脏块数量
maxwritten_clean
bigint
bgwriter清理脏块的时候达到bgwriter_lru_maxpages后终止写入批处理的次数,为了防止一次批量写入太大影响数据块IO性能,bgwriter每次都有写入的限制。不过这个参数的缺省值100太小,对于负载较高的数据库,需要加大;
buffers_backend
bigint
backend清理的脏块数量
buffers_backend_fsync
bigint
backend被迫自己调用fsync来同步数据的计数,如果这个计数器不为零,说明当时的fsync队列已经满了,存储子系统肯定出现了性能问题;
buffers_alloc
bigint
buffer分配的次数
stats_reset
timestamp with time zone
上一次RESET这些统计值的时间

重置pg_stat_bgwriter数据

pg_stat_reset_shared('bgwriter')

postgresql 提供了4个重置功能,看名字都很容易理解了。

  • pg_stat_reset()

    把用于当前数据库的所有统计计数器重置为零(默认要求超级用户权限,但这个函数的 EXECUTE 可以被授予给其他人)。

  • pg_stat_reset_shared(text)

    把某些集簇范围的统计计数器重置为零,具体哪些取决于参数(默认要求超级用户权限,但这个函数的 EXECUTE 可以被授予给其他人)。

    调用pg_stat_reset_shared('bgwriter')把pg_stat_bgwriter 视图中显示的所有计数器清零。

    调用pg_stat_reset_shared('archiver') 将会把pg_stat_archiver视图中展示的所有计数器清零。

  • pg_stat_reset_single_table_counters(oid)

    把当前数据库中用于单个表或索引的统计数据重置为零(默认要求超级用户权限,但这个函数的 EXECUTE 可以被授予给其他人)

  • pg_stat_reset_single_function_counters(oid)

    把当前数据库中用于单个函数的统计信息重置为零(默认要求超级用户权限,但这个函数的 EXECUTE 可以被授予给其他人)

其它相关参数

checkpoint

参数名说明默认值
checkpoint_timeout系统自动执行checkpoint之间的最大时间间隔5分钟
checkpoint_completion_target表示checkpoint的完成时间占两次checkpoint时间间隔的比例,系统默认值是0.5,也就是说每个checkpoint需要在checkpoints间隔时间的50%内完成0.5
checkpoint_flush_after在执行检查点时,只要写入的数据量超过此值,就会强制刷盘。这样做将限制os page cache的脏数据量,从而减少在检查点最后执行fsync或操作系统在后台大批量写回数据时出现io性能的问题。256KB
max_wal_sizeWAL日志超过max_wal_size就触发检查点开始工作。这是个软限制,PostgreSQL尽量不超过此值,但会在比如高负载和wal_keep_segments值较大的时候超过。1GB
min_wal_sizeWAL磁盘使用率低于这个设置,旧的WAL文件就会被回收,而不是删除,确保预留足够的WAL空间处理WAL使用中的峰值80MB

shared_buffers

数据库的共享内存缓冲区量,通常设置成内存的25%,shared_buffers更大的设置通常要求对max_wal_size也

做相应增加。

测试验证

测试过程

  1. 生成测试数据

pgbench -i -s 100 temp
  1. 调整参数

alter system set xxx=xxx;
select pg_reload_conf();
  1. 对压测表vacuum

vacuum analyze pgbench_accounts;
vacuum analyze pgbench_branches;
vacuum analyze pgbench_history;
vacuum analyze pgbench_tellers;
checkpoint;
select pg_stat_reset_shared('bgwriter');
  1. 执行压测,并统计tps

pgbench -c 5 -j 4 -n -T 310 temp
  1. 查看checkpoint、bgwriter、backend刷脏的百分比

select  round(buffers_checkpoint*100/buffer_total::numeric)||'%' checkpoint_percent
      ,round(buffers_clean*100/buffer_total::numeric)||'%' bgwriter_percent
      ,round(buffers_backend*100/buffer_total::numeric)||'%' backend_percent
from pg_stat_bgwriter a
,(select buffers_checkpoint+ buffers_clean+buffers_backend as buffer_total from pg_stat_bgwriter) b;
  1. 测试结果

默认配置如下

bgwriter_delay:200
bgwriter_flush_after:64
bgwriter_lru_maxpages:100
bgwriter_lru_multiplier:2
checkpoint_completion_target:0.5
checkpoint_flush_after:32
checkpoint_timeout:60
max_wal_size:4096
min_wal_size:80
shared_buffers:512MB

重复2-5步骤,共进行5项测试结果如下:

测试项参数调整TPSckpt刷脏占比bgwriter刷脏占比backend刷脏占比
1默认配置198320%30%50%
2bgwriter_delay=10;bgwriter_lru_maxpages=1000;181722%76%2%
3bgwriter_delay=10;bgwriter_lru_maxpages=1000;checkpoint_timeout='5min';18701%98%1%
4bgwriter_delay=2000;bgwriter_lru_maxpages=100;checkpoint_timeout='5min';19544%4%92%
5bgwriter_delay=2000;bgwriter_lru_maxpages=100;checkpoint_timeout='5min';shared_buffers='4GB';237494%0%6%






  • 测试项1作为对比参考

  • 测试项2和3调整了bgwriter刷脏频率较高,checkpoint刷脏其实和bgwriter一样,这样backend刷脏占比就会较低,所以测试结果的tps差异不大。

  • 测试项4调整bgwriter刷脏频率较低,并且checkpoint为5分钟,在测试时间(310秒)内checkpoint占比自然是会低,导致backend刷脏高,因为使用pgbench压测的是tpch性能,磁盘io没有到瓶颈,所以tps会较高。

  • 测试项5调整了shared_buffers='4GB',压测的表只是1GB多,因为调整了bgwriter频率较低,所以压测中都在shared_buffers中进行,直到5分钟了进行一次checkpoint导致占比高,但整体的tps提升上来了

上面测试,其中参数调整只作为刷脏占比的验证,不作最终配置优化的调整,实际优化中,需要根据实际的数据反映来进行调整。

参考文档

http://mysql.taobao.org/monthly/2015/09/06/
https://blog.csdn.net/qq_35462323/article/details/116268934
https://mp.weixin.qq.com/s/c6rjvlokbCe-8agc5_QAiQ
文章转载自叶同学专栏,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论