PostgreSQL数据库为了定时清理因为MVCC 引入的垃圾数据,实现了自动清理机制。其中涉及到了两种辅助进程:
autovacuum launcher
autovacuum worker
其中,autovacuum launcher 主要负责调度autovacuum worker,autovacuum worker进程进行具体的自动清理工作。本文主要是对autovacuum worker进行分析。
除了参数track_counts,autovacuum,autovacuum_max_workers,autovacuum_naptime,autovacuum_vacuum_cost_limit,autovacuum_vacuum_cost_delay,autovacuum_freeze_max_age,autovacuum_multixact_freeze_max_age之外,autovacuum worker进程还涉及到以下参数:
log_autovacuum_min_duration:所有运行超过此时间或者因为锁冲突而退出的autovacuum 会被打印在日志中,该参数每个表可以单独设置。
autovacuum_vacuum_threshold :与下文的autovacuum_vacuum_scale_factor配合使用,该参数每个表可以单独设置。
autovacuum_analyze_threshold:与下文的autovacuum_analyze_scale_factor配合使用,该参数每个表可以单独设置。
autovacuum_vacuum_scale_factor :当表更新或者删除的元组数超过autovacuum_vacuum_threshold autovacuum_vacuum_scale_factor* table_size会触发VACUUM,该参数每个表可以单独设置。
autovacuum_analyze_scale_factor:当表插入,更新或者删除的元组数超过autovacuum_analyze_threshold autovacuum_analyze_scale_factor* table_size会触发ANALYZE,该参数每个表可以单独设置。
vacuum_cost_page_hit:清理一个在共享缓存中找到的缓冲区的估计代价。它表示锁住缓冲池、查找共享哈希表和扫描页内容的代价。默认值为1。
vacuum_cost_page_miss:清理一个必须从磁盘上读取的缓冲区的代价。它表示锁住缓冲池、查找共享哈希表、从磁盘读取需要的块以及扫描其内容的代价。默认值为10。
vacuum_cost_page_dirty:当清理修改一个之前干净的块时需要花费的估计代价。它表示再次把脏块刷出到磁盘所需要的额外I/O。默认值为20。
其中,autovacuum_vacuum_threshold和autovacuum_vacuum_scale_factor参数配置会决定VACUUM 的频繁程度。因为autovacuum会消耗一定的资源,设置的不合适,有可能会影响用户的其他正常的查询。对PostgreSQL使用者来说,一般有2种方案:
调大触发阈值,在业务低峰期,主动去做VACUUM。在VACUUM过程中,性能可能会出现抖动。
调小触发阈值,将清理工作分摊到一段时间内。但是参数如果设置不合理,会使得正常查询性能都会下降。
为了降低对并发正常查询的影响,autovacuum引入了vacuum_cost_delay,vacuum_cost_page_hit,vacuum_cost_page_miss,vacuum_cost_page_dirty,vacuum_cost_limit参数。在VACUUM和ANALYZE命令的执行过程中,系统维护着一个内部计数器来跟踪各种被执行的I/O操作的估算开销。当累计的代价达到一个阈值(vacuum_cost_limit),执行这些操作的进程将按照vacuum_cost_delay所指定的休眠一小段时间。然后它将重置计数器并继续执行,这样就大大降低了这些命令对并发的数据库活动产生I/O影响
autovacuum worker 的启动
autovacuum launcher 在选取合适的database 之后会向Postmaster 守护进程发送PMSIGNAL_START_AUTOVAC_WORKER信号。Postmaster 接受信号会调用StartAutovacuumWorker函数:
1、调用StartAutoVacWorker 启动worker
2、调用成功,则释放后台进程slot进行善后处理,否则向autovacuum launcher发送信息,标记其为失败的autovacuum worker
StartAutoVacWorker 函数调用AutoVacWorkerMain 函数启动worker 进程:
- 注册信号处理函数
- 更新GUC参数配置:
- zero_damaged_pages 参数强制设置为off,这个参数会忽略掉坏页,在自动清理的过程中,这样设置太危险
- statement_timeout,lock_timeout,idle_in_transaction_session_timeout 为0,防止这些配置阻碍清理任务
- default_transaction_isolation 设置为read committed,相对于设置为serializable,没增加死锁的风险,同时也不会阻塞其他的事务
- synchronous_commit 设置为local,这样就允许我们不受备库的影响能够进行正常的清理任务
3. 读取共享内存中的AutoVacuumShmem 结构中的av_startingWorker 并更新需要清理的databaseoid 和wi_proc,放在运行中的autovacuum worker进程列表里面,并更新av_startingWorker 为NULL唤醒autovacuum launcher 4. 更新统计信息中autovacuum 的开始时间 5. 连接数据库,并读取最新的xid 和multixactid 6. 调用do_autovacuum 函数清理数据