Alvaro Herrera escribió:

> Okay, here's a patch along these lines.  I haven't considered Jim's
> suggestion downthread about discounting dead tuples from relpages; maybe
> we can do that by subtracting the pages attributed to dead ones,
> estimating via tuple density (reltuples/relpages).

Patch attached.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/postmaster/autovacuum.c
--- b/src/backend/postmaster/autovacuum.c
***************
*** 167,173 **** typedef struct avw_dbase
  	PgStat_StatDBEntry *adw_entry;
  } avw_dbase;
  
! /* struct to keep track of tables to vacuum and/or analyze, in 1st pass */
  typedef struct av_relation
  {
  	Oid			ar_toastrelid;	/* hash key - must be first */
--- 167,173 ----
  	PgStat_StatDBEntry *adw_entry;
  } avw_dbase;
  
! /* struct to keep track of TOAST<->main relation mappings */
  typedef struct av_relation
  {
  	Oid			ar_toastrelid;	/* hash key - must be first */
***************
*** 177,182 **** typedef struct av_relation
--- 177,201 ----
  								 * reloptions, or NULL if none */
  } av_relation;
  
+ /*
+  * A tasklist is a set of tables to process, collected during a worker's first
+  * phase.  For each table we keep track of its Browne strength, so that we can
+  * process in priority order.
+  */
+ typedef struct avw_tltable
+ {
+ 	Oid			tt_reloid;
+ 	float4		tt_browne_strength;
+ } avw_tltable;
+ 
+ typedef struct avw_tasklist
+ {
+ 	int			tl_maxelts;
+ 	int			tl_nelts;
+ 	avw_tltable	**tl_elts;
+ } avw_tasklist;
+ 
+ 
  /* struct to keep track of tables to vacuum and/or analyze, after rechecking */
  typedef struct autovac_table
  {
***************
*** 299,305 **** static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
  static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry,
! 						  bool *dovacuum, bool *doanalyze, bool *wraparound);
  
  static void autovacuum_do_vac_analyze(autovac_table *tab,
  						  BufferAccessStrategy bstrategy);
--- 318,326 ----
  static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry,
! 						  bool *dovacuum, bool *doanalyze, bool *wraparound,
! 						  float4 *deadtuples, float4 *relpages,
! 						  uint32 *xidage);
  
  static void autovacuum_do_vac_analyze(autovac_table *tab,
  						  BufferAccessStrategy bstrategy);
***************
*** 1890,1895 **** get_database_list(void)
--- 1911,1979 ----
  	return dblist;
  }
  
+ static avw_tasklist *
+ tasklist_initialize(void)
+ {
+ 	avw_tasklist   *tasklist;
+ 
+ 	tasklist = palloc(sizeof(avw_tasklist));
+ 	tasklist->tl_maxelts = 32;
+ 	tasklist->tl_nelts = 0;
+ 	tasklist->tl_elts = palloc(tasklist->tl_maxelts * sizeof(avw_tltable *));
+ 
+ 	return tasklist;
+ }
+ 
+ /*
+  * Add a table to the tasklisk.
+  */
+ static void
+ tasklist_add_table(avw_tasklist *tasklist, Oid relid, bool dovacuum,
+ 				   bool doanalyze, bool wraparound, float4 deadtuples,
+ 				   float4 relpages, uint32 xidage)
+ {
+ 	avw_tltable *tab;
+ 
+ 	/* enlarge the array if necessary */
+ 	if (tasklist->tl_nelts >= tasklist->tl_maxelts)
+ 	{
+ 		tasklist->tl_maxelts *= 2;
+ 		tasklist->tl_elts = repalloc(tasklist->tl_elts, tasklist->tl_maxelts *
+ 									 sizeof(avw_tltable *));
+ 	}
+ 
+ 	tab = palloc0(sizeof(avw_tltable));
+ 
+ 	tab->tt_reloid = relid;
+ 	if (dovacuum)
+ 	{
+ 		tab->tt_browne_strength = deadtuples / relpages +
+ 			exp(xidage * logf(relpages) / UINT_MAX);
+ 	}
+ 
+ 	tasklist->tl_elts[tasklist->tl_nelts++] = tab;
+ }
+ 
+ /*
+  * qsort comparator: sorts avw_tltable elements by value of Browne strength,
+  * descending
+  */
+ static int
+ avw_tt_compar(const void *a, const void *b)
+ {
+ 	const avw_tltable *taba = *(avw_tltable *const *) a;
+ 	const avw_tltable *tabb = *(avw_tltable *const *) b;
+ 
+ 	return tabb->tt_browne_strength - taba->tt_browne_strength;
+ }
+ 
+ static void
+ tasklist_sort(avw_tasklist *tasklist)
+ {
+ 	qsort(tasklist->tl_elts, tasklist->tl_nelts, sizeof(avw_tltable *),
+ 		  avw_tt_compar);
+ }
+ 
  /*
   * Process a database table-by-table
   *
***************
*** 1903,1917 **** do_autovacuum(void)
  	HeapTuple	tuple;
  	HeapScanDesc relScan;
  	Form_pg_database dbForm;
- 	List	   *table_oids = NIL;
  	HASHCTL		ctl;
  	HTAB	   *table_toast_map;
- 	ListCell   *volatile cell;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
  	BufferAccessStrategy bstrategy;
  	ScanKeyData key;
  	TupleDesc	pg_class_desc;
  
  	/*
  	 * StartTransactionCommand and CommitTransactionCommand will automatically
--- 1987,2001 ----
  	HeapTuple	tuple;
  	HeapScanDesc relScan;
  	Form_pg_database dbForm;
  	HASHCTL		ctl;
  	HTAB	   *table_toast_map;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
  	BufferAccessStrategy bstrategy;
  	ScanKeyData key;
  	TupleDesc	pg_class_desc;
+ 	avw_tasklist *tasklist;
+ 	int			i;
  
  	/*
  	 * StartTransactionCommand and CommitTransactionCommand will automatically
***************
*** 1986,1991 **** do_autovacuum(void)
--- 2070,2078 ----
  								  &ctl,
  								  HASH_ELEM | HASH_FUNCTION);
  
+ 	/* initialize our tasklist */
+ 	tasklist = tasklist_initialize();
+ 
  	/*
  	 * Scan pg_class to determine which tables to vacuum.
  	 *
***************
*** 2020,2025 **** do_autovacuum(void)
--- 2107,2115 ----
  		bool		dovacuum;
  		bool		doanalyze;
  		bool		wraparound;
+ 		float4		deadtuples;
+ 		float4		relpages;
+ 		uint32		xidage;
  
  		relid = HeapTupleGetOid(tuple);
  
***************
*** 2030,2036 **** do_autovacuum(void)
  
  		/* Check if it needs vacuum or analyze */
  		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! 								  &dovacuum, &doanalyze, &wraparound);
  
  		/*
  		 * Check if it is a temp table (presumably, of some other backend's).
--- 2120,2127 ----
  
  		/* Check if it needs vacuum or analyze */
  		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! 								  &dovacuum, &doanalyze, &wraparound,
! 								  &deadtuples, &relpages, &xidage);
  
  		/*
  		 * Check if it is a temp table (presumably, of some other backend's).
***************
*** 2077,2085 **** do_autovacuum(void)
  		}
  		else
  		{
! 			/* relations that need work are added to table_oids */
  			if (dovacuum || doanalyze)
! 				table_oids = lappend_oid(table_oids, relid);
  
  			/*
  			 * Remember the association for the second pass.  Note: we must do
--- 2168,2177 ----
  		}
  		else
  		{
! 			/* relations that need work are added to our tasklist */
  			if (dovacuum || doanalyze)
! 				tasklist_add_table(tasklist, relid, dovacuum, doanalyze,
! 								   wraparound, deadtuples, relpages, xidage);
  
  			/*
  			 * Remember the association for the second pass.  Note: we must do
***************
*** 2129,2134 **** do_autovacuum(void)
--- 2221,2229 ----
  		bool		dovacuum;
  		bool		doanalyze;
  		bool		wraparound;
+ 		float4		deadtuples;
+ 		float4		relpages;
+ 		uint32		xidage;
  
  		/*
  		 * We cannot safely process other backends' temp tables, so skip 'em.
***************
*** 2158,2168 **** do_autovacuum(void)
  											 shared, dbentry);
  
  		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! 								  &dovacuum, &doanalyze, &wraparound);
  
  		/* ignore analyze for toast tables */
  		if (dovacuum)
! 			table_oids = lappend_oid(table_oids, relid);
  	}
  
  	heap_endscan(relScan);
--- 2253,2265 ----
  											 shared, dbentry);
  
  		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! 								  &dovacuum, &doanalyze, &wraparound,
! 								  &deadtuples, &relpages, &xidage);
  
  		/* ignore analyze for toast tables */
  		if (dovacuum)
! 			tasklist_add_table(tasklist, relid, dovacuum, doanalyze,
! 							   wraparound, deadtuples, relpages, xidage);
  	}
  
  	heap_endscan(relScan);
***************
*** 2185,2202 **** do_autovacuum(void)
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MAXSIZE);
  
  	/*
  	 * Perform operations on collected tables.
  	 */
! 	foreach(cell, table_oids)
  	{
- 		Oid			relid = lfirst_oid(cell);
  		autovac_table *tab;
  		bool		skipit;
  		int			stdVacuumCostDelay;
  		int			stdVacuumCostLimit;
  		dlist_iter	iter;
  
  		CHECK_FOR_INTERRUPTS();
  
  		/*
--- 2282,2304 ----
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MAXSIZE);
  
+ 	/* sort our task list */
+ 	tasklist_sort(tasklist);
+ 
  	/*
  	 * Perform operations on collected tables.
  	 */
! 	for (i = 0; i < tasklist->tl_nelts; i++)
  	{
  		autovac_table *tab;
+ 		Oid			relid;
  		bool		skipit;
  		int			stdVacuumCostDelay;
  		int			stdVacuumCostLimit;
  		dlist_iter	iter;
  
+ 		relid = tasklist->tl_elts[i]->tt_reloid;
+ 
  		CHECK_FOR_INTERRUPTS();
  
  		/*
***************
*** 2499,2505 **** table_recheck_autovac(Oid relid, HTAB *table_toast_map,
  										 shared, dbentry);
  
  	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
! 							  &dovacuum, &doanalyze, &wraparound);
  
  	/* ignore ANALYZE for toast tables */
  	if (classForm->relkind == RELKIND_TOASTVALUE)
--- 2601,2608 ----
  										 shared, dbentry);
  
  	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
! 							  &dovacuum, &doanalyze, &wraparound,
! 							  NULL, NULL, NULL);
  
  	/* ignore ANALYZE for toast tables */
  	if (classForm->relkind == RELKIND_TOASTVALUE)
***************
*** 2565,2573 **** table_recheck_autovac(Oid relid, HTAB *table_toast_map,
  /*
   * relation_needs_vacanalyze
   *
!  * Check whether a relation needs to be vacuumed or analyzed; return each into
!  * "dovacuum" and "doanalyze", respectively.  Also return whether the vacuum is
!  * being forced because of Xid wraparound.
   *
   * relopts is a pointer to the AutoVacOpts options (either for itself in the
   * case of a plain table, or for either itself or its parent table in the case
--- 2668,2682 ----
  /*
   * relation_needs_vacanalyze
   *
!  * Check whether a relation needs to be vacuumed or analyzed, and return each
!  * into "dovacuum" and "doanalyze", respectively.
!  *
!  * Output parameters:
!  *	wraparound: whether the vacuum is being forced because of Xid wraparound
!  *	deadtuples: number of dead tuples in the table (from the pgstat tabentry;
!  *		zero if table is not present in pgstat)
!  *	relpages: number of pages in the table (from pg_class)
!  *	xidage: age of relfrozenxid, compared to recentXid
   *
   * relopts is a pointer to the AutoVacOpts options (either for itself in the
   * case of a plain table, or for either itself or its parent table in the case
***************
*** 2606,2612 **** relation_needs_vacanalyze(Oid relid,
   /* output params below */
  						  bool *dovacuum,
  						  bool *doanalyze,
! 						  bool *wraparound)
  {
  	bool		force_vacuum;
  	bool		av_enabled;
--- 2715,2724 ----
   /* output params below */
  						  bool *dovacuum,
  						  bool *doanalyze,
! 						  bool *wraparound,
! 						  float4 *deadtuples,
! 						  float4 *relpages,
! 						  uint32 *xidage)
  {
  	bool		force_vacuum;
  	bool		av_enabled;
***************
*** 2715,2724 **** relation_needs_vacanalyze(Oid relid,
  		/*
  		 * Skip a table not found in stat hash, unless we have to force vacuum
  		 * for anti-wrap purposes.	If it's not acted upon, there's no need to
! 		 * vacuum it.
  		 */
  		*dovacuum = force_vacuum;
  		*doanalyze = false;
  	}
  
  	/* ANALYZE refuses to work with pg_statistics */
--- 2827,2848 ----
  		/*
  		 * Skip a table not found in stat hash, unless we have to force vacuum
  		 * for anti-wrap purposes.	If it's not acted upon, there's no need to
! 		 * vacuum it.  It seems okay to say that there are no tuples to remove,
! 		 * either.
  		 */
  		*dovacuum = force_vacuum;
  		*doanalyze = false;
+ 		vactuples = 0;
+ 	}
+ 
+ 	if (*dovacuum)
+ 	{
+ 		if (deadtuples)
+ 			*deadtuples = vactuples;
+ 		if (relpages)
+ 			*relpages = classForm->relpages;
+ 		if (xidage)
+ 			*xidage = recentXid - classForm->relfrozenxid;
  	}
  
  	/* ANALYZE refuses to work with pg_statistics */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to