bgwriter(Background Writer)是PostgreSQL中在后台将脏页写出到磁盘的一个单独的进程,这一进程主要有两个目的:
数据库在进行查询处理时若发现要读取的数据不在缓冲区中要先从磁盘中读入该页,这时如果缓冲区已满,就需要先选择一些缓冲区中的页面替换出去。如果是简单的替换是很快的,但如果要被替换的页被修改了,则必需先将这页读出到磁盘中去后才能替换,这样数据库的查询处理就会被阻塞。通过使用bgwriter定期的写出缓冲区中的部分脏页,为缓冲区腾出空间,就可以防止这一情况。
PostgreSQL在定期作检查点时需要把所有脏页写出到磁盘,通过bgwriter预先写出一些脏页,可以减少作检查点时要进行的IO操作,使系统的IO负载趋向平稳。
但如果bgwriter过于积极的将脏页写出,则经常被更新的数据页很可能会被一次又一次的写出到磁盘上,总体上说增加了数据库的写IO量,导致系统性能下降。另一方面,若bgwriter太不积极了,则又不能超到上述的两个优化作用。因此确定bgwriter应已什么速率将脏页写出才能达到最佳效果并不是一件很容易的事。
bgwriter是在PostgreSQL 8.0中加入的,但在8.2及以前版本中,使用bgwriter都要求管理员进行很复杂的配置。在PostgreSQL 8.2中,bgwriter相关的配置总共有5个:bgwriter_delay, bgwriter_lru_percent, bgwriter_lru_maxpages, bgwriter_all_percent, bgwriter_all_maxpages。根据这5个配置,bgwriter的工作原理如下:
系统每隔bgwriter_delay指定的时间启动bgwriter,bgwriter先从后向前扫描缓冲区的LRU链表(即先扫描最长时间没有访问的数据页),最多扫描这一链表的bgwriter_lru_percent%的项(即如果bgwriter_lru_percent为1,就最多扫描1%的LRU链表),将遇到的脏页写出去,但最多不超过bgwriter_lru_maxpages个脏页。然后bgwriter循环扫描整个缓冲区,最多扫描缓冲区的bgwriter_all_percent项,写出遇到的脏页,但最多不超过bgwriter_all_maxpages个。循环扫描的意思是说下次扫描是从本次扫描的结束位置开始扫描。
虽然这个算法简单明了,但要怎么配置这5个参数实在是成问题。首先这些参数的最优值显然与系统负载与应用访问模式密切相关。并且即使管理员对系统的负载和应用都有所了解,也非常难估算出这些配置的最优值。使用PostgreSQL 8.2,我想管理员只能摸石头过河,不断试出个差不多的值,而且应用或负载一发生变化还得改。
因此8.3对bgwriter的算法进行了大幅修改,在PostgreSQL 8.3中,bgwriter相关的配置减少到3个:bgwriter_delay, bgwriter_lru_maxpages, bgwriter_lru_multiplier。根据这3个配置,8.3中bgwriter的工作原理如下:
系统每隔bgwriter_delay指定的时间启动bgwriter,bgwriter从后向前扫描缓冲区的LRU链表,写出至多bgwriter_lru_multiplier * N个脏页,并且不超过bgwriter_lru_maxpages个。其中N是最近一段时间在两次bgwriter运行期间系统新申请的缓冲页数。
为理解这个算法的功效,我们先假设bgwriter_lru_multiplier为1,这样,一次bgwriter运行写出的页数就是两次bgwriter运行期间系统新申请的缓冲页数。这样,在平均情况下,一次bgwriter释放出来的缓冲区空间在下次bgwriter运行时就会被刚好用完,不多不少。并且如果系统负载比较高,新申请的缓冲页数越多,bgwriter写出去的就越多,基本保证能在缓冲区中腾出足够的空间。否则若系统负载轻,bgwriter就写的很少,不会给系统带来额外开销。这样一来,bgwriter就能根据系统负载来决定写出策略,拥有了一定程度上的自调优能力,管理员的负担也大为减轻。
一般来说bgwriter_lru_multiplier的值应该大于1,这样才能更好的保证为正常事务处理腾出足够空间。默认值是2,估计对一般应用都不太需要修改了