⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 autovacuum.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
	if (!HeapTupleIsValid(tup))		elog(ERROR, "could not find tuple for database %u", MyDatabaseId);	dbForm = (Form_pg_database) GETSTRUCT(tup);	if (!dbForm->datallowconn || dbForm->datistemplate)		freeze = true;	else		freeze = false;	systable_endscan(scan);	heap_close(dbRel, AccessShareLock);	elog(DEBUG2, "autovacuum: VACUUM%s whole database",		 (freeze) ? " FREEZE" : "");	autovacuum_do_vac_analyze(NIL, true, false, freeze);	/* Finally close out the last transaction. */	CommitTransactionCommand();}/* * Process a database table-by-table * * dbentry must be a valid pointer to the database entry in the stats * databases' hash table, and it will be used to determine whether vacuum or * analyze is needed on a per-table basis. * * Note that CHECK_FOR_INTERRUPTS is supposed to be used in certain spots in * order not to ignore shutdown commands for too long. */static voiddo_autovacuum(PgStat_StatDBEntry *dbentry){	Relation	classRel,				avRel;	HeapTuple	tuple;	HeapScanDesc relScan;	List	   *vacuum_tables = NIL;	List	   *toast_table_ids = NIL;	ListCell   *cell;	PgStat_StatDBEntry *shared;	/* Start a transaction so our commands have one to play into. */	StartTransactionCommand();	 /* functions in indexes may want a snapshot set */	ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());	/*	 * Clean up any dead statistics collector entries for this DB.	 * We always want to do this exactly once per DB-processing cycle,	 * even if we find nothing worth vacuuming in the database.	 */	pgstat_vacuum_tabstat();	/*	 * StartTransactionCommand and CommitTransactionCommand will automatically	 * switch to other contexts.  We need this one to keep the list of	 * relations to vacuum/analyze across transactions.	 */	MemoryContextSwitchTo(AutovacMemCxt);	/* The database hash where pgstat keeps shared relations */	shared = pgstat_fetch_stat_dbentry(InvalidOid);	classRel = heap_open(RelationRelationId, AccessShareLock);	avRel = heap_open(AutovacuumRelationId, AccessShareLock);	/*	 * Scan pg_class and determine which tables to vacuum.	 *	 * The stats subsystem collects stats for toast tables independently of	 * the stats for their parent tables.  We need to check those stats since	 * in cases with short, wide tables there might be proportionally much	 * more activity in the toast table than in its parent.	 *	 * Since we can only issue VACUUM against the parent table, we need to	 * transpose a decision to vacuum a toast table into a decision to vacuum	 * its parent.	There's no point in considering ANALYZE on a toast table,	 * either.	To support this, we keep a list of OIDs of toast tables that	 * need vacuuming alongside the list of regular tables.  Regular tables	 * will be entered into the table list even if they appear not to need	 * vacuuming; we go back and re-mark them after finding all the vacuumable	 * toast tables.	 */	relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL);	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)	{		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);		Form_pg_autovacuum avForm = NULL;		PgStat_StatTabEntry *tabentry;		SysScanDesc avScan;		HeapTuple	avTup;		ScanKeyData entry[1];		Oid			relid;		/* Consider only regular and toast tables. */		if (classForm->relkind != RELKIND_RELATION &&			classForm->relkind != RELKIND_TOASTVALUE)			continue;		/*		 * Skip temp tables (i.e. those in temp namespaces).  We cannot safely		 * process other backends' temp tables.		 */		if (isAnyTempNamespace(classForm->relnamespace))			continue;		relid = HeapTupleGetOid(tuple);		/* See if we have a pg_autovacuum entry for this relation. */		ScanKeyInit(&entry[0],					Anum_pg_autovacuum_vacrelid,					BTEqualStrategyNumber, F_OIDEQ,					ObjectIdGetDatum(relid));		avScan = systable_beginscan(avRel, AutovacuumRelidIndexId, true,									SnapshotNow, 1, entry);		avTup = systable_getnext(avScan);		if (HeapTupleIsValid(avTup))			avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);		if (classForm->relisshared && PointerIsValid(shared))			tabentry = hash_search(shared->tables, &relid,								   HASH_FIND, NULL);		else			tabentry = hash_search(dbentry->tables, &relid,								   HASH_FIND, NULL);		test_rel_for_autovac(relid, tabentry, classForm, avForm,							 &vacuum_tables, &toast_table_ids);		systable_endscan(avScan);	}	heap_endscan(relScan);	heap_close(avRel, AccessShareLock);	heap_close(classRel, AccessShareLock);	/*	 * Perform operations on collected tables.	 */	foreach(cell, vacuum_tables)	{		autovac_table *tab = lfirst(cell);		CHECK_FOR_INTERRUPTS();		/*		 * Check to see if we need to force vacuuming of this table because		 * its toast table needs it.		 */		if (OidIsValid(tab->toastrelid) && !tab->dovacuum &&			list_member_oid(toast_table_ids, tab->toastrelid))		{			tab->dovacuum = true;			elog(DEBUG2, "autovac: VACUUM %u because of TOAST table",				 tab->relid);		}		/* Otherwise, ignore table if it needs no work */		if (!tab->dovacuum && !tab->doanalyze)			continue;		/* Set the vacuum cost parameters for this table */		VacuumCostDelay = tab->vacuum_cost_delay;		VacuumCostLimit = tab->vacuum_cost_limit;		autovacuum_do_vac_analyze(list_make1_oid(tab->relid),								  tab->dovacuum,								  tab->doanalyze,								  false);	}	/* Finally close out the last transaction. */	CommitTransactionCommand();}/* * test_rel_for_autovac * * Check whether a table needs to be vacuumed or analyzed.	Add it to the * appropriate output list if so. * * A table needs to be vacuumed if the number of dead tuples exceeds a * threshold.  This threshold is calculated as * * threshold = vac_base_thresh + vac_scale_factor * reltuples * * For analyze, the analysis done is that the number of tuples inserted, * deleted and updated since the last analyze exceeds a threshold calculated * in the same fashion as above.  Note that the collector actually stores * the number of tuples (both live and dead) that there were as of the last * analyze.  This is asymmetric to the VACUUM case. * * A table whose pg_autovacuum.enabled value is false, is automatically * skipped.  Thus autovacuum can be disabled for specific tables.  Also, * when the stats collector does not have data about a table, it will be * skipped. * * A table whose vac_base_thresh value is <0 takes the base value from the * autovacuum_vacuum_threshold GUC variable.  Similarly, a vac_scale_factor * value <0 is substituted with the value of * autovacuum_vacuum_scale_factor GUC variable.  Ditto for analyze. */static voidtest_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,					 Form_pg_class classForm,					 Form_pg_autovacuum avForm,					 List **vacuum_tables,					 List **toast_table_ids){	Relation	rel;	float4		reltuples;		/* pg_class.reltuples */	/* constants from pg_autovacuum or GUC variables */	int			vac_base_thresh,				anl_base_thresh;	float4		vac_scale_factor,				anl_scale_factor;	/* thresholds calculated from above constants */	float4		vacthresh,				anlthresh;	/* number of vacuum (resp. analyze) tuples at this time */	float4		vactuples,				anltuples;	/* cost-based vacuum delay parameters */	int			vac_cost_limit;	int			vac_cost_delay;	bool		dovacuum;	bool		doanalyze;	/* User disabled it in pg_autovacuum? */	if (avForm && !avForm->enabled)		return;	/*	 * Skip a table not found in stat hash.  If it's not acted upon, there's	 * no need to vacuum it.  (Note that database-level check will take care	 * of Xid wraparound.)	 */	if (!PointerIsValid(tabentry))		return;	rel = RelationIdGetRelation(relid);	/* The table was recently dropped? */	if (!PointerIsValid(rel))		return;	reltuples = rel->rd_rel->reltuples;	vactuples = tabentry->n_dead_tuples;	anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -		tabentry->last_anl_tuples;	/*	 * If there is a tuple in pg_autovacuum, use it; else, use the GUC	 * defaults.  Note that the fields may contain "-1" (or indeed any	 * negative value), which means use the GUC defaults for each setting.	 */	if (avForm != NULL)	{		vac_scale_factor = (avForm->vac_scale_factor >= 0) ?			avForm->vac_scale_factor : autovacuum_vac_scale;		vac_base_thresh = (avForm->vac_base_thresh >= 0) ?			avForm->vac_base_thresh : autovacuum_vac_thresh;		anl_scale_factor = (avForm->anl_scale_factor >= 0) ?			avForm->anl_scale_factor : autovacuum_anl_scale;		anl_base_thresh = (avForm->anl_base_thresh >= 0) ?			avForm->anl_base_thresh : autovacuum_anl_thresh;		vac_cost_limit = (avForm->vac_cost_limit >= 0) ?			avForm->vac_cost_limit :			((autovacuum_vac_cost_limit >= 0) ?			 autovacuum_vac_cost_limit : VacuumCostLimit);		vac_cost_delay = (avForm->vac_cost_delay >= 0) ?			avForm->vac_cost_delay :			((autovacuum_vac_cost_delay >= 0) ?			 autovacuum_vac_cost_delay : VacuumCostDelay);	}	else	{		vac_scale_factor = autovacuum_vac_scale;		vac_base_thresh = autovacuum_vac_thresh;		anl_scale_factor = autovacuum_anl_scale;		anl_base_thresh = autovacuum_anl_thresh;		vac_cost_limit = (autovacuum_vac_cost_limit >= 0) ?			autovacuum_vac_cost_limit : VacuumCostLimit;		vac_cost_delay = (autovacuum_vac_cost_delay >= 0) ?			autovacuum_vac_cost_delay : VacuumCostDelay;	}	vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;	anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;	/*	 * Note that we don't need to take special consideration for stat reset,	 * because if that happens, the last vacuum and analyze counts will be	 * reset too.	 */	elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",		 RelationGetRelationName(rel),		 vactuples, vacthresh, anltuples, anlthresh);	/* Determine if this table needs vacuum or analyze. */	dovacuum = (vactuples > vacthresh);	doanalyze = (anltuples > anlthresh);	/* ANALYZE refuses to work with pg_statistics */	if (relid == StatisticRelationId)		doanalyze = false;	Assert(CurrentMemoryContext == AutovacMemCxt);	if (classForm->relkind == RELKIND_RELATION)	{		if (dovacuum || doanalyze)			elog(DEBUG2, "autovac: will%s%s %s",				 (dovacuum ? " VACUUM" : ""),				 (doanalyze ? " ANALYZE" : ""),				 RelationGetRelationName(rel));		/*		 * we must record tables that have a toast table, even if we currently		 * don't think they need vacuuming.		 */		if (dovacuum || doanalyze || OidIsValid(classForm->reltoastrelid))		{			autovac_table *tab;			tab = (autovac_table *) palloc(sizeof(autovac_table));			tab->relid = relid;			tab->toastrelid = classForm->reltoastrelid;			tab->dovacuum = dovacuum;			tab->doanalyze = doanalyze;			tab->vacuum_cost_limit = vac_cost_limit;			tab->vacuum_cost_delay = vac_cost_delay;			*vacuum_tables = lappend(*vacuum_tables, tab);		}	}	else	{		Assert(classForm->relkind == RELKIND_TOASTVALUE);		if (dovacuum)			*toast_table_ids = lappend_oid(*toast_table_ids, relid);	}	RelationClose(rel);}/* * autovacuum_do_vac_analyze *		Vacuum and/or analyze a list of tables; or all tables if relids = NIL */static voidautovacuum_do_vac_analyze(List *relids, bool dovacuum, bool doanalyze,						  bool freeze){	VacuumStmt *vacstmt;	MemoryContext old_cxt;	/*	 * The node must survive transaction boundaries, so make sure we create it	 * in a long-lived context	 */	old_cxt = MemoryContextSwitchTo(AutovacMemCxt);	vacstmt = makeNode(VacuumStmt);	/*	 * Point QueryContext to the autovac memory context to fake out the	 * PreventTransactionChain check inside vacuum().  Note that this is also	 * why we palloc vacstmt instead of just using a local variable.	 */	QueryContext = CurrentMemoryContext;	/* Set up command parameters */	vacstmt->vacuum = dovacuum;	vacstmt->full = false;	vacstmt->analyze = doanalyze;	vacstmt->freeze = freeze;	vacstmt->verbose = false;	vacstmt->relation = NULL;	/* all tables, or not used if relids != NIL */	vacstmt->va_cols = NIL;	/* Let pgstat know what we're doing */	autovac_report_activity(vacstmt, relids);	vacuum(vacstmt, relids);	pfree(vacstmt);	MemoryContextSwitchTo(old_cxt);}/* * autovac_report_activity * 		Report to pgstat what autovacuum is doing * * We send a SQL string corresponding to what the user would see if the * equivalent command was to be issued manually. * * Note we assume that we are going to report the next command as soon as we're * done with the current one, and exiting right after the last one, so we don't * bother to report "<IDLE>" or some such. */#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32)static voidautovac_report_activity(VacuumStmt *vacstmt, List *relids){	char		activity[MAX_AUTOVAC_ACTIV_LEN];	/*	 * This case is not currently exercised by the autovac code.  Fill it in	 * if needed.	 */	if (list_length(relids) > 1)		elog(WARNING, "vacuuming >1 rel unsupported");	/* Report the command and possible options */	if (vacstmt->vacuum)		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,					   "VACUUM%s%s%s",					   vacstmt->full ? " FULL" : "",					   vacstmt->freeze ? " FREEZE" : "",					   vacstmt->analyze ? " ANALYZE" : "");	else if (vacstmt->analyze)		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,					   "ANALYZE");	/* Report the qualified name of the first relation, if any */	if (list_length(relids) > 0)	{		Oid			relid = linitial_oid(relids);		Relation	rel;		rel = RelationIdGetRelation(relid);		if (rel == NULL)			elog(WARNING, "cache lookup failed for relation %u", relid);		else		{			char   *nspname = get_namespace_name(RelationGetNamespace(rel));			int		len = strlen(activity);			snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,					 " %s.%s", nspname, RelationGetRelationName(rel));			pfree(nspname);			RelationClose(rel);		}	}	pgstat_report_activity(activity);}/* * AutoVacuumingActive *		Check GUC vars and report whether the autovacuum process should be *		running. */boolAutoVacuumingActive(void){	if (!autovacuum_start_daemon || !pgstat_collect_startcollector ||		!pgstat_collect_tuplelevel)		return false;	return true;}/* * autovac_init *		This is called at postmaster initialization. * * Annoy the user if he got it wrong. */voidautovac_init(void){	if (!autovacuum_start_daemon)		return;	if (!pgstat_collect_startcollector || !pgstat_collect_tuplelevel)	{		ereport(WARNING,				(errmsg("autovacuum not started because of misconfiguration"),				 errhint("Enable options \"stats_start_collector\" and \"stats_row_level\".")));		/*		 * Set the GUC var so we don't fork autovacuum uselessly, and also to		 * help debugging.		 */		autovacuum_start_daemon = false;	}}/* * IsAutoVacuumProcess *		Return whether this process is an autovacuum process. */boolIsAutoVacuumProcess(void){	return am_autovacuum;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -