sgs
作者sgs2022-07-01 15:49
数据库管理员, 某公司

PostgreSQL源码看vacuum

字数 3670阅读 1898评论 0赞 1
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;
  }

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

1

添加新评论0 条评论

Ctrl+Enter 发表

本文隶属于专栏

最佳实践
不同的领域,都有先行者,实践者,用他们的最佳实践来加速更多企业的建设项目落地。

相关文章

相关问题

相关资料

X社区推广