postgresql AutoVacuum系统自动清理进程
2018-04-25 11:43 @学无止境 阅读(3448) 评论(0) 编辑 收藏 举报来源:https://blog.csdn.net/wzyzzu/article/details/50426692
《PostgreSQL数据库内核分析》第2章PostgreSQL的体系结构,本章从宏观上对PostgreSQL的控制和处理流程进行了简要介绍,说明了各个模块之间是如何协同工作,以使得整个数据库系统能够稳定、正确地处理用户的各种操作和请求的。至于每个模块是如何各司其职,其内部具体是如何运作的,将会在后续的章节进行专门的介绍。本节为大家介绍AutoVacuum系统自动清理进程。
2.5.5 AutoVacuum系统自动清理进程
在PostgreSQL数据库中,对表元组的UPDATE或DELETE操作并未立即删除旧版本的数据,表中的旧元组只是被标识为删除状态,并未立即释放空间。这种处理对于获取多版本并发控制是必要的,如果一个元组的版本仍有可能被其他事务看到,那么就不能删除元组的该版本。当事务提交后,过期元组版本将对事务不再有效,因而其占据的空间必须回收以供其他新元组使用,以避免对磁盘空间增长的无休止的需求,此时对数据库的清理工作通过运行VACUUM来实现。从PostgreSQL 8.1开始,PostgreSQL数据库引入一个额外的可选辅助进程AutoVacuum(系统自动清理进程),自动执行VACUUM和ANALYZE命令,回收被标识为删除状态记录的空间,更新表的统计信息。
在PostgreSQL数据库系统配置文件中,与系统自动清理相关的主要相关参数如下:
autovacuum:是否启动系统自动清理功能,默认值为on。
autovacuum_max_workers:设置系统自动清理工作进程的最大数量。
autovacuum_naptime:设置两次系统自动清理操作之间的间隔时间。
autovacuum_vacuum_threshold和autovacuum_analyze_threshold:设置当表上被更新的元组数的阈值超过这些阈值时分别需要执行vacuum和analyze。
autovacuum_vacuum_scale_factor和autovacuum_analyze_scale_factor:设置表大小的缩放系数。
autovacuum_freeze_max_age:设置需要强制对数据库进行清理的XID上限值。
AutoVacuum系统自动清理进程中包含两种不同的处理进程:AutoVacuum Launcher和AutoVacuum Worker。AutoVacuum Launcher进程为监控进程,用于收集数据库运行信息,根据数据库选择规则选中一个数据库,并调度一个AutoVacuum Worker进程执行清理操作。在AutoVacuum Launcher进程中,选择数据库的规则如下:首先由于数据库事务XID是32位整数且递增分配,当超过最大值时会从头开始计数使用,而事务XID的大小表示事务开始的时间,事务XID重新计数使用会使数据库中部分事务数据丢失,因此当XID超过配置的autovacuum_freeze_max_age时,强制对该数据库进行清理并更新事务XID;其次,若无强制清理操作,则选择数据库列表中最早未执行过自动清理操作的数据库。Launcher进程会定时(或者被信号驱动)选择数据库并调度Worker进程去执行清理工作。
AutoVacuum中的AutoVacuum Worker进程执行实际的清理任务,Launcher进程中维护有Worker进程列表。Worker进程列表由三种不同状态的进程列表构成,即空闲的Worker进程列表、正在启动的Worker进程、运行中的Worker进程列表。Launcher进程在不同状态之间的切换实现了Worker进程的调度工作。首先,在初始化阶段创建的运行内存上下文中,创建长度为autovacuum_max_workers的空闲Worker进程描述信息列表,而正在启动Worker进程和运行中的Worker进程的列表被置为空。如果Launcher进程需要一个Worker进程,空闲Worker进程列表不为空且当前没有正在启动中的Worker进程,则开始一个启动Worker进程的操作,即向Postmaster进程发送启动消息,从空闲Worker进程列表中取出一个进程描述信息,设置为启动中状态。Launcher进程中只允许存在一个启动中状态的Worker进程,启动中的Worker进程如果超时(超时时间由autovacuum_naptime设置)将被取消并重新开始启动Worker进程的循环。如果Worker启动成功,将启动成功的Worker进程信息添加到运行中的Worker进程列表中。运行中的Worker进程即连接上根据规则选中的数据库。
在启动成功的Worker进程连接数据库成功后,将遍历该数据库中的表,根据对表的清理规则选择要执行的表和在该表上执行的操作。对表的操作分为VACUUM和ANALYZE两种,对选中的表如果上次VACUUM之后的过期元组的数量超过了“清理阈值”(vacuum threshold),那么就清理该表,清理阈值是定义为:
清理阈值=清理基本阈值+清理缩放系数 *元组数
这里的清理基本阈值是autovacuum_vacuum_threshold,清理的缩放系数是autovacuum_vacuum_scale_factor,元组的数目可以从统计收集器里面获取。这是一个部分精确的计数,由每次UPDATE和DELETE操作更新。
如果表上次被执行ANALYZE操作之后,其中过期元组的数量超过了“分析阈值”(analyze threshold),那么就分析该表更新表统计信息,分析阈值的定义与清理阈值相似,定义如下:
分析阈值=分析基本阈值+分析缩放系数 *元组数目
缺省的阈值和伸缩系数都是从postgresql.conf里面取得的。不过,我们可以以每个表独立设置的方式覆盖它,方法就是在系统表pg_autovacuum里输入信息。pg_autovacuum表中一个元组可以用来记录一个需要自动清理的表及其清理设置,AutoVacuum进程将使用其中的清理设置来清理该表。如果没有特别设置该表的清理设置,AutoVacuum将使用全局设置。
1.AutoVacuum Launcher进程
通常,在入口函数StartAutoVacLauncher中执行fork操作创建Postmaster的子进程AutoVacuum Launcher,在新创建的子进程执行体中关闭从Postmaster进程中复制出的网络连接端口,同时进入进程AutoVacuum Launcher的执行体函数AutoVacLauncherMain,其处理流程如图2-12所示。
图2-12 AutoVacuum Launcher处理流程 |
下面对其中几个重要的步骤进行说明:
1)构建数据库列表:调用函数rebuild_database_list完成,其步骤如下:
①建立一个Hash表,其中每一个元素代表一个数据库,记录了该数据库的OID(adl_datid)、启动worker的时间戳(adl_next_worker)以及一个评分值(adl_score)。初始时该Hash表中没有元素。
②将pg_database平面文件(在PGDATA/global目录下)中的数据库构成一个链表,链表中的每一个节点代表一个数据库,其中包括数据库的OID、名称、该数据库的统计信息等。
③调用pgstat_fetch_stat_dbentry来填充每个节点的统计信息。
④对每一个统计信息不为空的数据库,在Hash表中搜索该数据库,如果没有找到则将该数据库加入到Hash表中,并且将该数据库的adl_score设置为该数据库被加入到Hash表中时的顺序号。
⑤将Hash表中的数据库按照adl_score值升序的顺序依次加入到全局变量DatabaseList所指向的链表中,并设置每一个数据库的adl_next_worker值。其中第一个数据库的adl_next_worker值设为当前时间,之后的每一个数据库的adl_next_worker的值都比前一个增加millis_increment。millis_increment的值由autovacuum_naptime参数值除以Hash表中数据库的个数来设定。
为什么使用平面文件?
在PostgreSQL的数据集簇中,提供了两个平面文件:pg_database和pg_auth。这两个文件分别记录了pg_database系统表和pg_authid系统表中的部分信息。如果有些还没有启动完毕的后台进程需要访问这两个系统表的内容,它们将会使用两个平面文件来进行信息的获取,这是由于进程还未完全启动时是无法连接到数据库并读取相关系统表内容的。
在“构建数据库列表”这一步骤由于并未连接到数据库,因此只能用平面文件来替代系统表pg_database。
2)设置进程休眠时间:根据空闲Worker和数据库列表来计算休眠的时间,当所有Worker进程都在运行时要设置一个较长的休眠时间。而当Worker进程退出时可以唤醒休眠,同时休眠也可以被其他信号中断。
3)信号处理分支中got_SIGUSR1信号通知有Worker进程退出或者Postmaster通知有Worker启动失败。若是Postmaster通知Worker启动失败,则给Postmaster重新发送启动Worker进程的消息。
4)启动Worker进程:如果当前有一个Worker正在启动中,则再休眠一会儿等待该Worker启动完成。如果可以开始启动一个新的Worker,则进行以下判断:
①如果数据库列表不为空,则检查DatabaseList尾部数据库的adl_next_worker参数,如果早于当前时间(表示该数据库早就应该被处理)则启动Worker进程。
②数据库列表为空时,立即启动Worker进程。
2.AutoVacuum Worker进程
AutoVacuum Worker进程的入口为launch_worker函数,在该入口处调用do_start_worker创建worker进程,并且返回连接数据库的OID。如果返回的OID为有效的数据库OID,则遍历数据库列表找到该OID对应的数据库在数据库列表中对应的节点,更新该节点的adl_next_worker域值,并将该节点移动到数据库列表的头部。如果遍历数据库列表没有对应于该OID的节点,则调用rebuild_database_list重建数据库列表。创建worker进程的函数体do_start_worker处理流程如图2-13所示。
图2-13 AutoVacuum Worker进程处理流程 |
AutoVacuum Worker进程的处理流程和AutoVacuum Launcher进程的处理流程基本类似,选择要进行清理的数据库的规则如前所述:选中数据库后遍历数据库中的表,根据表的统计信息计算清理阈值和分析阈值,来确定是否要对表执行相应的操作。
在系统进行自动清理的同时,用户可以使用安装目录bin文件夹下的vacuumdb或者vacuumlo工具对数据进行手动清理工作。vacuumdb工具清理数据库并对数据库执行分析操作,vacuumlo工具清理数据库中无效的大对象。