Postgresql作为世界上最先进的HTAP数据库,以其超高在线事务处理及分析性能和强大的功能被广泛应用与各行各业中。但其实它也并不完美,说到postgres,不得不提那个让人一直头疼的问题,也是数据库使用者所诟病最多的地方:vacuum。那么为什么会有vacuum这个东西呢?它是做什么用的呢?
我们先来聊聊postgresql的mvcc机制,我们知道,postgresql是没有undo表空间的,它通过数据的多版本来实现mvcc,一条数据的delete并不会释放数据占用空间,同理update是通过delete insert的方式实现,通过这种方式减少了锁的使用,提高了并发性能。但是这种设计天然带来一个问题:旧数据的清理,如果清理不及时就会造成数据膨胀,这也是在频繁更新的oltp系统中数据膨胀问题的原因。
这里面其实还有一个问题,postgresql从设计之初事务号就是32位。但是32位的事务id最大只有49亿,49亿的事务号在如今的生产系统中几乎会很快耗尽,而事务号耗尽后从头开始循环使用,这里为了保证数据不丢失,需要对旧的事务号进行清理,这个清理过程会使得整个数据库无法处理新的请求,这就是网上经常所说pg的“冻结炸弹”。其实真正的清理并不是达到49亿就开始的,因为事务号是循环的,所以当达到事务号一半的时候数据库就会出现冻结炸弹。这个冻结的问题是pg永远绕不过去的痛。其实pg经过这么多年的社区发展,到今天pg12版本依然是32位的事务id,这个设计有人说是为了使得事务的回滚更快,能够更快的寻址,但是我个人觉得牺牲大于收益。
而我们今天所讲的主角就是解决上面两个问题的。Vacuum的第一个功能是清理旧的数据,第二是清理旧的txid。那么如何控制vacuum的频率、消耗io资源的大小等就成了运维好pg数据库非常关键的因素,下面我们来探讨和vacuum相关的一些参数。
autovacuum相关参数
①autovacuum
表示是否自动清理功能,默认值on。
②log_autovacuum_min_duration
表示当vacuum超过多少ms后,将本次vacuum过程记录到日志中,默认值-1,表示不进行记录,设置为0代表所有vacuum都进行记录,生产中按照实际需要开启或者关闭。
③autovacuum_max_workers
表示执行vacuum的进程数,默认值3,对于频繁更新的系统可以调大,5-10都是可以的。该参数重启生效。
④autovacuum_naptime
设置两次vacuum的时间间隔。默认值1min。
⑤autovacuum_vacuum_threshold
表示某个表更新或删除多少条记录后触发vacuum,默认值50。
⑥autovacuum_analyze_threshold
表示某个表更新或删除多少条记录后触发analzye,默认值50。
⑦autovacuum_work_mem
每个vacuum进程所能使用的最大内存,默认值-1,该参数就会使用maintenance_work_mem的值。
⑧autovacuum_vacuum_scale_factor
执行vacuum的阈值因子,当更新的元组数超过table_size*autovacuum_vacuum_scale_factor autovacuum_vacuum_threshold的值时,执行vacuum操作
⑨autovacuum_analyze_scale_factor
执行analyze的阈值因子,当更新的元组数超过table_size*autovacuum_anzlyze_scale_factor autovacuum_analyze_threshold的值时,执行analyze操作
⑩autovacuum_freeze_max_age
设置事务冻结清理的最大事务age,默认值是2亿,建议增大,因为freeze vacuum的代价太大,即使没达到冻结的年龄,系统也会自动调用autovacuum来进行清理。该参数重启生效。
⑪autovacuum_vacuum_cost_delay
Vacuum执行时如果达到io_limits时sleep的时间。默认值为20ms,这个值可以调到10ms,如果发现资源负载较高,可以继续上调autovacuum_vacuum_cost_limit值,因为这样系统会更加平稳,vacuum对系统的影响能够更平均地分配到时间轴上。如果设置为-1,会取vacuum_cost_delay值。
⑫autovacuum_vacuum_cost_limit
设置在自动VACUUM操作里使用的开销延迟数值。当vacuum使用的io资源超过该值限制时会sleep autovacuum_vacuum_cost_delay参数定义的时间,用来减轻vacuum对系统io的影响。默认值-1,表示不限制,建议进行设置,对于普通硬盘设置为1000,对于ssd可以设置为10000。
基于开销的延迟清理
这个话题其实上面已经介绍的差不多了,因为vacuum是一个比较消耗io资源的动作,但是有时候其实并不需要vacuum动作迅速完成,以减轻vacuum对数据库正常业务的影响,这时pg提供了一种基于开销的延迟清理,能够将vacuum的时间拉长,使得io负载更轻,同时更加平稳。
但是值得注意的是,有时候有些清理操作会持有关键的锁,这时候我们可能希望vacuum迅速结束并释放锁。在这种情况下,上面所说的基于延迟的清理可能不会起作用,因此执行vacuum过程中累计的cost可能远远大于指定的limit值。为了避免这种情况下的长延时,真实的delay会用下面的两者计算的较大值:
vacuum_cost_delay*accumulated_balance/vacuum_cost_limit
vacuum_cost_delay*4
基于开销的延迟清理主要由以下几个参数控制,当然如果autovacuum相关参数开启后,会以那个为准。
①vacuum_cost_delay
达到io_limits后sleep的时间
②vacuum_cost_page_hit
清理一个在共享缓存里找到的缓冲区的预计开销。它代表锁住缓冲池,查找共享的散列表以及扫描页面的内容的开销。缺省值是1,说明vacuum命中buffer cache的开销为1。
③vacuum_cost_page_miss
和上面的参数类似,此参数表示vacuum没有命中缓冲区而从磁盘上读取数据库并扫描的开销,默认值为10。
④vacuum_cost_page_dirty
修改一个原先是干净的块的预计vacuum开销。他代表把一个脏的磁盘块再次刷新到磁盘上的额外开销。默认值20。
⑤vacuum_cost_limit
Vacuum达到的io限制进行sleep。
Vacuum作为postgresql数据库非常独特也是很容易出现问题的地方,非常值得我们关注。32位事务id和数据膨胀问题一直是pg的主要痛点,也是永远绕不过去的话题,可能遭人诟病,但是在目前的版本里我们更应该关注怎样更好的管理好它,避免那些别人踩过的坑,让它更好的为我们服务。