KingbaseESV7 vacuum工作机制

Q: autovacuum工作机制

A:

AutoVacuum

1     AutoVacuum线程负责的工作

AutoVacuum线程主要负责对有过大量更新操作的表进行空间清理和更新统计信息,即包括Vacuum和Analyze两部分工作。Vacuum负责对表进行空间清理,将过期的元组清除,使其空间可以被其他数据使用;Analyze负责将表的统计信息进行更新,使得查询在生成查询计划时,可以根据比较准确的统计信息选择较优的查询计划。

2     相关的系统表

与AutoVacuum相关的系统表如下:

1.  sys_class:该表中的relpages、reltuples、relhasindex、relhaspkey、relfrozenxid等列在VACUUM操作过程中会被更新。

a)  relpages:说明该表在磁盘上的形式大小,以页为单位。它只是一个近似值。

b)  reltuples:表中行的数目。它是一个近似值。

c)  relhasindex:如果它是一个表而且至少有(或者最近有过)一个索引,则为真。它是由CREATE INDEX设置的,但DROP INDEX不会立即将它清除。如果VACUUM发现一个表没有索引,那么它将清理relhasindex。

d)  relhaspkey:如果该表有一个(或曾经有一个)主键,则为真。

e)  relfrozenxid:该表中所有在这个之前的事务ID已经被一个固定的事务ID替换(“FROZEN”)。这用于跟踪该表是否需要为了防止事务ID重叠或者允许收缩sys_clog而进行清理。如果该关系不是表则为零(InvalidTransactionId)。

2.  sys_database:该表中的datfrozenxid列在VACUUM操作过程中会被更新。

a)  datfrozenxid:该数据库中所有在这个之前的事务ID已经被一个固定的事务ID替换(“FROZEN”)。这用于跟踪该数据库是否需要为防止事务ID重叠或者允许收缩sys_clog而进行清理。它是针对每个表的sys_class.relfrozenxis中的最小值。

3.  sys_autovacuum:该表为AutoVacuum进程存储针对每个关系的配置参数,给出的参数将用于自动清理该表。如果没有提供条目,则使用相应系统参数值。

a)  vacrelid:表的oid。

b)  enabled:该表是否会被自动清理。

c)  vac_base_thresh:清理前修改的最少行数。

d)  vac_scale_factor:追加到vac_base_thresh上的行的倍数。

e)  anl_base_thresh:分析之前修改的最少行数。

f)  anl_scale_factor:追加到anl_base_thresh上的行的倍数。

g)  vac_cost_delay:自定义的vacuum_cost_delay参数。

h)  vac_cost_limit:自定义的vacuum_cost_limit参数。

i)  freeze_min_age:自定义的vacuum_freeze_min_age参数。

j)  freeze_max_age:自定义的autovacuum_freeze_max_age参数。

在pg8.4中,该表被合并到pg_class的reloptions列中,可以通过CREATE TABLE语句的WITH子句设置该表中各列的值,WITH子句的格式如下:

        WITH ( storage_parameter [= value] [, ... ] )

        其中storage_parameter可以是上面各列的列名。

        但在pg8.3以及Kingbase中,没有找到如何设置这些列值的方法,因此,该表一直是空的。

3   相关的数据结构

typedef struct VariableCacheData

{

    Oid         nextOid;            /* next OID to assign */

    uint32      oidCount;           /* OIDs available before must do XLOG work */

    TransactionId nextXid;          /* next XID to assign */

    uint32      sessionid;          /* bug#11847 : audit func */

    TransactionId oldestXid;        /* cluster-wide minimum datfrozenxid */

    TransactionId xidVacLimit;      /* start forcing autovacuums here */

    TransactionId xidWarnLimit;     /* start complaining here */

    TransactionId xidStopLimit;     /* refuse to advance nextXid beyond here */

    TransactionId xidWrapLimit;     /* where the world ends */

    NameData    limit_datname;      /* database that needs vacuumed first */

} VariableCacheData;

typedef VariableCacheData *VariableCache;

MT_LOCAL VariableCache ShmemVariableCache;

VariableCache位于共享内存中,用于各backend得到下一个可用的OID和XID。xidWrapLimit和limit_datname只是在取到的事务id超过xidWarnLimit或xidStopLimit时生成有用的报错消息。

ShmemVariableCache中的几个Limit值只有在函数SetTransactionIdLimit中被设置,而SetTransactionIdLimit只被write_database_file和vac_truncate_clog调用,因此,在每次启动服务器做完日志恢复时,会通过调用write_database_file函数来初始化ShmemVariableCache中的各Limit值,之后每次在修改database或vacuum操作时,会更新各Limit值。

4     SetTransactionIdLimit函数的实现

该函数负责更新和设置防止事务id重叠的几个Limit值,具体实现如下:

根据当前所有数据库中最老的datfrozenxid(下面以oldest_datfrozenxid来代表)来设置事务id的wrap limit值,包括oldestXid、wrap limit、stop limit、warn limit和vacuum limit,这些值保存在MT_LOCAL的全局变量shmemVariableCache中,这几个值如下进行设置:

ShmemVariableCache->oldestXid = oldest_datfrozenxid;

ShmemVariableCache-> xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);

ShmemVariableCache-> xidStopLimit = ShmemVariableCache->xidWrapLimit - 1000000;

ShmemVariableCache-> xidWarnLimit = ShmemVariableCache-> xidStopLimit - 10000000;

ShmemVariableCache-> xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;

其中MaxTransactionId = 0xFFFFFFFF,autovacuum_freeze_max_age = 2亿。

当ShmemVariableCache-> xidVacLimit比当前的事务id老时,就会发送信号启动AutoVacuum线程。如果ShmemVariableCache-> xidWarnLimit比当前事务老时,将会给出如下警告:

WARNING: database "XXX" must be vacuumed within %u transactions

HINT: To avoid a database shutdown, execute a full-database VACUUM in "XXX".

5     相关的命令

VACUUM

元组被删除或更新后,其占用的空间不会被释放,用户必须利用VACUUM命令回收这些空间。

命令格式如下:

VACUUM [ FULL ] [ VERBOSE ] [ Tablename ]

VACUUM [ FULL ] ANALYZE [ VERBOSE ] [ Tablename [(ColumnName [, …] ) ] ]

VACUUM主要进行三部分工作:

1.  对指定表的每个页面进行检查,回收这些被删除元组以及指向它们的索引项占用的空间。回收完成后,这些空间可以继续被本表(索引)使用。

2.  对所有页面进行检查和回收完成后,对象(表和索引)中可能会出现大量全空的页面。为提高空间利用率,如果回收过程中发现表中存在大量全空的区(页面数超过1000,或超过该表总页面数的1/16),VACUUM操作还会将这些页面从表中删除,这样别的数据库对象也可以使用这部分空间。注意:VACUUM不会删除索引页面中的全空页面。

3.  VACUUM处理过程中,还会收集被处理对象的各种统计信息,操作最后,VACUUM会将这些统计信息记录到相应的系统表中。这些统计信息包括:sys_class中的reltuples、relpages、relhasindex、relhaspkey、relfrozenxid列;如果指定了ANALYZE选项,则还包括sys_statisitc表;如果没有指定TableName,则还包括sys_database表的datfrozenxid列。

ANALYZE

该命令用于更新数据库中的统计信息。

命令格式如下:

ANALYZE [ TableName [ ( ColumnName [, …] ) ] ]

使用说明:

1.  对于经常更新的表,应该定期做ANALYZE,已获得准确的统计信息。

2.  对于经常更新的数据库或表,应该定期做VACUUM和ANALYZE。

3.  ANALYZE只对可分析的列进行分析,如果表中没有可分析的列,则ANALYZE后,reltuples不变。ANALYZE不分析的列包括:(1)已删除的列;(2)用户已指定不做分析的列;(3)无法使用’=’操作符的列。

6   相关的GUC参数

1.  autovacuum_naptime参数:系统启动两次自动空间回收之间的时间间隔。单位是秒,默认值是1分钟(60秒)。

2.  autovacuum_freeze_max_age参数:指定从某张表对应的sys_class.relfrozenxid开始算起,到强制通过VACUUM自动对其进行重置之前,可以运行的最大事务个数。默认值是200000000。

3.  autovacuum_vacuum_threshold参数:对表触发VACUUM操作前,该表中被更新或删除的最少元组数。默认值是50。

4.  autovacuum_analyze_threshold参数:对表触发ANALYZE操作前,该表中被插入、删除、或更新的最少元组数。默认值是50。

5.  autovacuum_vacuum_scale_factor参数:决定是否对表触发VACUUM操作时,加到autovacuum_vacuum_threshold上的表大小的比例。默认值是0.2(20%)。

6.  autovacuum_analyze_scale_factor参数:决定是否对表触发ANALYZE操作时,加到autovacuum_analyze_threshold上的表大小的比例。默认值是0.1(10%)。

7.  autovacuum_vacuum_cost_limit参数:AutoVacuum中断前要达到的代价限制,。默认值是-1,如果该值为-1,则将其自动设置为vacuum_cost_limit。

8.  autovacuum_vacuum_cost_delay参数:达到autovacuum_vacuum_cost_limit后,AutoVacuum过程中断的时间。单位是毫秒,默认值是20。如果该值为-1,则将其自动设置为vacuum_cost_delay。

9.  vacuum_freeze_min_age参数:指定VACUUM时需要被freeze的事务范围——所有事务号比VACUUM操作开始时的最早活动事务早超过vacuum_freeze_min_age个事务号的事务都应该被freeze。该参数的默认值是100000000。

10. vacuum_cost_delay参数:每次VACUUM操作的开销达到vacuum_cost_limit后,该操作中断的时间。单位是毫秒,默认值是0。

11. vacuum_cost_limit参数:中断VACUUM操作前要达到的代价限制。默认值是200。

12. vacuum_cost_page_hit参数:VACUUM一个在缓冲区中页面的代价(如锁、查找等)估计。默认值为1。

13. vacuum_cost_page_miss参数:VACUUM一个不在缓冲区中页面的代价(如锁、查找、度页面等)估计。默认值是10。

14. vacuum_cost_page_dirty参数:VACUUM操作会将页面变脏时(导致额外的写外存操作)的代价。默认值是20。

7   AutoVacuum线程的启动

启动AutoVacuum线程有以下三种情况:

1.  当从上次做AutoVacuum的时间,到现在超过了GUC参数autovacuum_naptime设定的时间,则启动AutoVacuum线程;

2.  当为新事务获取事务id时(GetNewTransactionId),如果新事务id满足如下条件时,启动AutoVacuum线程:

当事务id达到了Vacuum的极限值(ShmemVariableCache->xidVacLimit),且事务id是65536的整数倍,则会发送信号,来启动AutoVacuum线程;

3.  在AutoVacuum结束,更新数据库的datfrozenxid时,会设置事务id的Limit值,如果当前事务id达到了Vacuum的极限值(ShmemVariableCache->xidVacLimit),则会发送信号,来启动AutoVacuum线程。

对于第2、3中情况,如果接收到启动AutoVacuum线程的信号时的时间距离上次做AutoVacuum的时间间隔小于GUC参数autovacuum_naptime,则AutoVacuum线程不会被启动。

导致AutoVacuum线程创建失败有如下几个原因:

1.  内存空间不足;

2.  当前存活的线程数多于运行的最大线程数;

3.  创建线程函数调用失败;

4.  共享线程id数组(threadArray)被毁坏。这种情况会产生FATAL错误。

8      AutoVacuum线程的工作流程

AutoVacuum线程并不是每次把所有的数据库中的所有表都进行Vacuum和Analyze的,而是选择满足某些条件的库及其库中满足某些条件的表做Vacuum或Analyze的。

8.1 如何选择一个目标库进行Vacuum

AutoVacuum线程首先要选择一个数据库来进行对其中的表进行Vacuum和Analyze,当满足如下条件之一时,就会选择该库作为AutoVacuum的目标库:

1. 如果有数据库老到将要产生事务id的wraparound(即该数据库的datfrozenxid落后于当前事务id的距离超过了2亿),则选择最老的数据库来做AutoVacuum;通过下面的命令可以分别查看所有数据库的datfrozenxid值,以及下一个新事务的事务id:

SELECT datname, datfrozenxid FROM sys_database; /*查看所有数据库的datfrozenxid值 */

SELECT transaction_id(); /* 查看下一个新事务的事务id */

2.    如果不存在上述的数据库,在选择上次做AutoVacuum的时间离现在时间最长的数据库(目前还没有提供查看数据库最后一次做AutoVacuum的时间的功能);因此,在没有数据库满足第1个条件时,各数据库是轮流被AutoVacuum线程选中来做AutoVacuum的。

对于没有统计信息的数据库,即从来没有被连接过的库,是不被考虑在AutoVacuum的范围之内的。

8.2 如何选择库中的哪些表进行Vacuum或Analyze

对于目标库中的表,也并不是所有的表都会被AutoVacuum的,而是将要进行如下筛选来确定哪个表需要进行AutoVacuum:

1.  这些表应该为普通表、toast表或大对象表;

2.  如果表的relfrozenxid落后于当前事务id的距离超过了2亿,则该表达到了做vacuum的条件;通过下面的查询可以查看当前数据库中各表的relfrozenxid:

SELECT relname, relfrozenxid FROM sys_class;

3.  如果表的统计信息满足如下条件,则会被做Vacuum或Analyze:

a)  该表dead的元组数(update的元组数 + delete的元组总数) > vacthresh,则该表将会被Vacuum。

Vacthresh = vac_base_thresh + vac_scale_factor * reltuple,其中vac_base_thresh和vac_scale_factor分别是GUC参数autovacuum_vac_thresh和autovacuum_vac_scale的取值或表sys_autovacuum中该表对应行中列vac_base_thresh和vac_scale_factor的取值。

b)  该表中live的元组数 + dead的元组数 – 上次analyze的元组数 > anlthresh,则该表将会被analyze。

Anlthresh = anl_base_thresh + anl_scale_factor * reltuples,其中anl_base_thresh和anl_scale_factor分别是GUC参数autovacuum_anl_thresh和autovacuum_anl_scale的取值或表sys_autovacuum中该表对应行中列anl_base_thresh和anl_scale_factor的取值。

当条件2和条件3中的a)任意一条成立,该表都会被vacuum。

8.3 AutoVacuum执行Vacuum或Analyze

通过上面的判断,得到一些需要做Vacuum或Analyze的表的列表,遍历该列表,别对每个表执行Vacuum或Analyze。

首先,如果表满足Vacuum条件,则通过vacuum_rel函数对表进行Vacuum,这主要由函数lazy_vacuum_rel函数完成。Lazy_vacuum_rel函数在完成对一个表的Vacuum后,会通过函数vac_update_relstats将该表的统计信息进行更新,主要更新relpages、reltuples、relhasindex、relhaspkey以及relfrozenxid列,当该表原来的relfrozenxid落后于当前正在运行的最老的事务id(oldestXid)的距离超过了1亿,则将relfrozenxid设置为当前事务id减去1亿的值。

其次,如果表满足Analyze条件,则通过analyze_rel函数对表进行Analyze,在Analyze完成后,也会通过函数vac_update_relstats对表的统计信息进行更新,但是该更新不包括对relfrozenxid的更新。

如此循环,直到将所有满足Vacuum或Analyze条件的表进行了Vacuum或Analyze。

8.4 更新sys_database.datfrozenxid

在对所有满足条件的表进行了Vacuum或Analyze操作后,统一将目标库的sys_database.datfrozenxid进行更新,更新过程如下:

将目标库中所有表的relfrozenxid以及当前正在运行的最老的事务id(oldestXid)比较,选择其中最老的一个事务id(newFrozenXid)作为更新目标库的sys_database.datfrozenxid的标准,如果目标库的datfrozenxid比newFrozenXid老,则将目标库的datfrozenxid设置为newFrozenXid。

8.5 更新事务id的wrap limit值

在更新了目标库的datfrozenxid后,需要调用函数SetTransactionIdLimit,根据当前所有数据库中最老的datfrozenxid(下面以oldest_datfrozenxid来代表)来设置事务id的wrap limit值,包括oldestXid、wrap limit、stop limit、warn limit和vacuum limit。具体的更新操作请参见第四节。

到此,整个AutoVacuum的工作就完成了。

9   AutoVacuum与Vacuum的区别

AutoVacuum与Vacuum最大的区别在于:

AutoVacuum要根据数据库及其中的表是否满足某些条件,而有选择地对数据库及其中的某些表进行Vacuum或Analyze;而Vacuum在不指定其他参数的条件下,是对当前数据库中的所有表都进行Vacuum,而不需要某些条件来决定表是否需要做Vacuum。

此外,Vacuum是不会负责做Analyze的,除非在命令中指定 ANALYZE 关键字,如 VACUUM ANALYZE;

10  获取有关AutoVacuum或Vacuum的一些信息

1.      通过函数sys_stat_get_last_autovacuum_time(oid), sys_stat_get_last_vacuum_time(oid), sys_stat_get_last_autoanalyze_time(oid), 以及sys_stat_get_last_analyze_time(oid)可以得到oid对应的表最后一次进行autovacuum、vacuum、autoanalyze和analyze的时间。例:

SELECT relname,

sys_stat_get_last_autovacuum_time(oid) AUTOVACUUM_TIME,

sys_stat_get_last_autoanalyze_time(oid) AUTOANALYZE_TIME,

sys_stat_get_last_vacuum_time(oid) VACUUM_TIME,

sys_stat_get_last_analyze_time(oid) ANALYZE_TIME

FROM sys_class WHERE relkind in (‘r’, ‘t’, ‘l’);

2.  可以查看sys_class和sys_database分别得到各表和各数据库的frozenxid。例:

SELECT datname, datfrozenxid FROM sys_database;

SELECT relname, relfrozenxid FROM sys_class;

正常情况下,datfrozenxid应该与该数据库中所有表中最小的relfrozenxid相同。

11  可以改进的一些方面

1.    sys_stat_get_last_xxx_time函数只能用来查询当前数据库中的表最后一次做相应操作的时间,而没有查询数据库最后一次做相应操作的时间的函数,需要为数据库提供类似的查询函数,如sys_stat_get_db_last_xxx_time;

2.    提供一种方法来查询ShmemVariableCache中防止事务id重叠相关的各成员变量的值,如提供函数sys_stat_get_transaction_id_limits;

3.    在产生错误或警告时,错误提示信息中包含当前事务id以及ShmemVariableCache中各Limit值。

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值