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)
小结
数据库年龄达到autovacuum_freeze_max_age时,无论autovacuum功能是否打开均会进行full scan vaccum
autovaccum无论partscan还是fullscan能进行冻结处理的的最大事务号为freezeLimit,partscan可能会因为跳过某些存在未frozen的但visible的数据页导致无法冻结至freezeLimit
命令行执行带DISABLE_PAGE_SKIPPING的vacuum和autovaccum时年龄超过vacuum_freeze_table_age原理一致,结果一致。即均会冻结至freezeLimit
命令行执行带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;
}





