diff -cNr a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c *** a/src/backend/commands/vacuumlazy.c 2014-03-18 16:34:33.630514689 +1100 --- b/src/backend/commands/vacuumlazy.c 2014-03-19 11:27:30.048419316 +1100 *************** *** 95,100 **** --- 95,107 ---- */ #define SKIP_PAGES_THRESHOLD ((BlockNumber) 32) + /* + * The following two values are used for identifying whether any table contains + * large number of unused rows and are eligible for 'VACUUM FULL'. + */ + #define RELPAGES_VALUES_THRESHOLD 1000 + #define FREESPACE_PERCENTAGE_THRESHOLD 0.5 + typedef struct LVRelStats { /* hasindex = true means two-pass strategy; false means one-pass */ *************** *** 189,194 **** --- 196,202 ---- double new_live_tuples; TransactionId new_frozen_xid; MultiXactId new_min_multi; + Size total_freespace; /* measure elapsed time iff autovacuum logging requires it */ if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0) *************** *** 268,274 **** lazy_truncate_heap(onerel, vacrelstats); /* Vacuum the Free Space Map */ ! FreeSpaceMapVacuum(onerel); /* * Update statistics in pg_class. --- 276,282 ---- lazy_truncate_heap(onerel, vacrelstats); /* Vacuum the Free Space Map */ ! total_freespace = FreeSpaceMapVacuum(onerel); /* * Update statistics in pg_class. *************** *** 343,348 **** --- 351,357 ---- ereport(LOG, (errmsg("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n" "pages: %d removed, %d remain\n" + "approximate free space: %zu\n" "tuples: %.0f removed, %.0f remain, %.0f are dead but not yet removable\n" "buffer usage: %d hits, %d misses, %d dirtied\n" "avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n" *************** *** 353,358 **** --- 362,368 ---- vacrelstats->num_index_scans, vacrelstats->pages_removed, vacrelstats->rel_pages, + total_freespace, vacrelstats->tuples_deleted, vacrelstats->new_rel_tuples, vacrelstats->new_dead_tuples, *************** *** 363,368 **** --- 373,390 ---- pg_rusage_show(&ru0)))); } } + + /* + * According the percentage of the free space in the table to check + * whether a table contains large numbers of unused row and are eligible for + * 'VACUUM FULL' + */ + if (vacrelstats->rel_pages > RELPAGES_VALUES_THRESHOLD && + (total_freespace > vacrelstats->rel_pages * BLCKSZ * FREESPACE_PERCENTAGE_THRESHOLD)) + ereport(LOG, + (errmsg("Table \"%s\" contains large numbers of unused row, suggest using VACUUM FULL on it!", + RelationGetRelationName(onerel)))); + } /* diff -cNr a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c *** a/src/backend/storage/freespace/freespace.c 2014-03-18 16:34:33.690514688 +1100 --- b/src/backend/storage/freespace/freespace.c 2014-03-19 11:48:05.319417472 +1100 *************** *** 108,114 **** static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue); static BlockNumber fsm_search(Relation rel, uint8 min_cat); ! static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof); /******** Public API ********/ --- 108,114 ---- static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue); static BlockNumber fsm_search(Relation rel, uint8 min_cat); ! static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof, Size *total_freespace); /******** Public API ********/ *************** *** 315,330 **** /* * FreeSpaceMapVacuum - scan and fix any inconsistencies in the FSM */ ! void FreeSpaceMapVacuum(Relation rel) { bool dummy; /* * Traverse the tree in depth-first order. The tree is stored physically * in depth-first order, so this should be pretty I/O efficient. */ ! fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, &dummy); } /******** Internal routines ********/ --- 315,332 ---- /* * FreeSpaceMapVacuum - scan and fix any inconsistencies in the FSM */ ! Size FreeSpaceMapVacuum(Relation rel) { bool dummy; + Size total_freespace = 0; /* * Traverse the tree in depth-first order. The tree is stored physically * in depth-first order, so this should be pretty I/O efficient. */ ! fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, &dummy, &total_freespace); ! return total_freespace; } /******** Internal routines ********/ *************** *** 725,735 **** * Recursive guts of FreeSpaceMapVacuum */ static uint8 ! fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof_p) { Buffer buf; Page page; uint8 max_avail; /* Read the page if it exists, or return EOF */ buf = fsm_readbuf(rel, addr, false); --- 727,738 ---- * Recursive guts of FreeSpaceMapVacuum */ static uint8 ! fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof_p, Size *total_freespace) { Buffer buf; Page page; uint8 max_avail; + int slot; /* Read the page if it exists, or return EOF */ buf = fsm_readbuf(rel, addr, false); *************** *** 749,755 **** */ if (addr.level > FSM_BOTTOM_LEVEL) { - int slot; bool eof = false; for (slot = 0; slot < SlotsPerFSMPage; slot++) --- 752,757 ---- *************** *** 760,766 **** /* After we hit end-of-file, just clear the rest of the slots */ if (!eof) ! child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot), &eof); else child_avail = 0; --- 762,768 ---- /* After we hit end-of-file, just clear the rest of the slots */ if (!eof) ! child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot), &eof, total_freespace); else child_avail = 0; *************** *** 774,779 **** --- 776,788 ---- } } } + else if (addr.level == FSM_BOTTOM_LEVEL) + { + for (slot = 0; slot < SlotsPerFSMPage; slot++) + { + *total_freespace += fsm_space_cat_to_avail(fsm_get_avail(page,slot)); + } + } max_avail = fsm_get_max_avail(BufferGetPage(buf)); diff -cNr a/src/include/storage/freespace.h b/src/include/storage/freespace.h *** a/src/include/storage/freespace.h 2014-03-18 16:34:33.579514686 +1100 --- b/src/include/storage/freespace.h 2014-03-18 16:34:44.093514674 +1100 *************** *** 31,36 **** Size spaceAvail); extern void FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks); ! extern void FreeSpaceMapVacuum(Relation rel); #endif /* FREESPACE_H_ */ --- 31,36 ---- Size spaceAvail); extern void FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks); ! extern Size FreeSpaceMapVacuum(Relation rel); #endif /* FREESPACE_H_ */