暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL源码看vacuum

棓棓科技 2022-08-02
826
  • PostgreSQL事务号TransactionId

事务ID使用 unsigned int(0~4294967295)类型进行存储,当事务号耗尽则重新循环使用,从3开始

typedef uint32 TransactionId;

保留0、1、2作为特殊事务号,其中2为冻结事务号比所有事务号老,即对所有事务可见

#define InvalidTransactionId((TransactionId) 0)
#define BootstrapTransactionId((TransactionId) 1)
#define FrozenTransactionId((TransactionId) 2)
#define FirstNormalTransactionId((Trujuku ansactionId) 3)
#define MaxTransactionId((TransactionId) 0xFFFFFFFF)

 

  • PostgreSQL数据库age

数据库函数age(xid)获取年龄的c实现,即当前xid减去参数xid

Datum
xid_age(PG_FUNCTION_ARGS)
{
TransactionId xid = PG_GETARG_TRANSACTIONID(0);
TransactionId now = GetStableLatestTransactionId();

/* Permanent XIDs are always infinitely old */
if (!TransactionIdIsNormal(xid))
PG_RETURN_INT32(INT_MAX);

PG_RETURN_INT32((int32) (now - xid));
}


  • PostgreSQL事务回卷

  • xidWrapLimit为发生回卷的临界XID,例如当前最老的冻结号为3,那么回卷临界点即为2147483650=3+(4294967295>>1)

  • xidStopLimit为停止交易临界XID,值为xidWrapLimit减去100w,当事务号到达xidStopLimit,无法申请新的事务号,需进入单用户模式进行手工冻结

  • xidWarnLimit为告警提示临界XID,值为xidStopLimit减去1000w,当事务号到达xidWarnLimit,会进行警告性提示

  • xidVacLimit为强制vacuum临界XID,值为当前最老的冻结号+数据库参数(autovacuum_freeze_max_age),当事务号达到xidVacLimit时表示年龄已达到强制冻结年龄,即为autovacuum_freeze_max_age指定的值

xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
xidStopLimit = xidWrapLimit - 1000000;
xidWarnLimit = xidStopLimit - 10000000;
xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;


  • PostgreSQL autovacuum机制

  • autovacuum相关参数

  • autovacuum:自动vaccum/analyze开关


  • autovacuum_freeze_max_age:强制执行autovacuum的数据库年龄,即年龄大于此参数必须进行vaccum(无论autovacuum是否打开)


  • autovacuum_vacuum_scale_factor:update/delete记录数占表记录数的百分比


  • autovacuum_vacuum_threshold:最小update/delete记录数


  • vacuum_freeze_min_age:最小需要进行表记录冻结的年龄,最大值为autovacuum_freeze_max_age的一半

  • vacuum_freeze_table_age:需进行全表扫描进行冻结的年龄,最大值autovacuum_freeze_max_age*0.95

  • autovacuum触发条件

  • autovacuum打开:(update/delete的记录数>=autovacuum_vacuum_scale_factor*表记录总数+autovacuum_vacuum_threshold)or (表的年龄>=autovacuum_freeze_max_age)

    vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
    *dovacuum = force_vacuum || (vactuples > vacthresh);
    ..................................

    autovacuum关闭:(表的年龄>=autovacuum_freeze_max_age)and (当前事务号 % 65536 == 0 )

    if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit))
    {
    ............................
    if (IsUnderPostmaster && (xid % 65536) == 0)
    SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
    ....................................
  • autovacuum模式

  • partscan/惰性模式:基于可见性映射表扫描非all_visible的数据页进行垃圾回收或事务冻结,每32个连续数据页判断一次是否可以跳过,存在一个不能跳过的页则全不跳过

    /*
    * Before we consider skipping a page that's marked as clean in
    * visibility map, we must've seen at least this many clean pages.
    */
    #define SKIP_PAGES_THRESHOLD((BlockNumber) 32)

    fullscan/积极模式:基于可见性映射表扫描全表进行垃圾回收或事务冻结,但跳过all_frozen的数据页

    aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,xidFullScanLimit);
    if (options & VACOPT_DISABLE_PAGE_SKIPPING)
    aggressive = true;
  • 阻碍vacuum的因素

    小于freezeLimit标识的事务号均为可冻结的

    oldestXmin:根据长事务中最老XID、slot_xmin、slot_catalog_xmi、vacuum_defer_cleanup_age、old_snapshot_threshold等计算得出数据库需要保留的最小XID,即不能进行冻结处理的事务号。


  • freezeLimit:vaccum能进行冻结处理的最大XID,当(oldestXmin-vacuum_freeze_min_age)<(当前XID - autovacuum_freeze_max_age)时,值为(oldestXmin),反之则为(oldestXmin-vacuum_freeze_min_age)


  • 小结

  1. 数据库年龄达到autovacuum_freeze_max_age时,无论autovacuum功能是否打开均会进行full scan vaccum

  2. autovaccum无论partscan还是fullscan能进行冻结处理的的最大事务号为freezeLimit,partscan可能会因为跳过某些存在未frozen的但visible的数据页导致无法冻结至freezeLimit

  3. 命令行执行带DISABLE_PAGE_SKIPPING的vacuum和autovaccum时年龄超过vacuum_freeze_table_age原理一致,结果一致。即均会冻结至freezeLimit

  4. 命令行执行带freeze的vacuum原理实际上是将vacuum_freeze_table_age和vacuum_freeze_min_age在执行器内修改为0,执行结果仍是将表冻结至freezeLimit

if (vacstmt->options & VACOPT_FREEZE)
{
params.freeze_min_age = 0;
params.freeze_table_age = 0;
}



文章转载自棓棓科技,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论