Commit c6fc50cb authored by Stephen Frost's avatar Stephen Frost

Use pre-fetching for ANALYZE

When we have posix_fadvise() available, we can improve the performance
of an ANALYZE by quite a bit by using it to inform the kernel of the
blocks that we're going to be asking for.  Similar to bitmap index
scans, the number of buffers pre-fetched is based off of the
maintenance_io_concurrency setting (for the particular tablespace or,
if not set, globally, via get_tablespace_maintenance_io_concurrency()).

Reviewed-By: Heikki Linnakangas, Tomas Vondra
Discussion: https://www.postgresql.org/message-id/VI1PR0701MB69603A433348EDCF783C6ECBF6EF0%40VI1PR0701MB6960.eurprd07.prod.outlook.com
parent 94d13d47
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
#include "utils/pg_rusage.h" #include "utils/pg_rusage.h"
#include "utils/sampling.h" #include "utils/sampling.h"
#include "utils/sortsupport.h" #include "utils/sortsupport.h"
#include "utils/spccache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
...@@ -1127,6 +1128,7 @@ acquire_sample_rows(Relation onerel, int elevel, ...@@ -1127,6 +1128,7 @@ acquire_sample_rows(Relation onerel, int elevel,
double liverows = 0; /* # live rows seen */ double liverows = 0; /* # live rows seen */
double deadrows = 0; /* # dead rows seen */ double deadrows = 0; /* # dead rows seen */
double rowstoskip = -1; /* -1 means not set yet */ double rowstoskip = -1; /* -1 means not set yet */
long randseed; /* Seed for block sampler(s) */
BlockNumber totalblocks; BlockNumber totalblocks;
TransactionId OldestXmin; TransactionId OldestXmin;
BlockSamplerData bs; BlockSamplerData bs;
...@@ -1135,6 +1137,10 @@ acquire_sample_rows(Relation onerel, int elevel, ...@@ -1135,6 +1137,10 @@ acquire_sample_rows(Relation onerel, int elevel,
TableScanDesc scan; TableScanDesc scan;
BlockNumber nblocks; BlockNumber nblocks;
BlockNumber blksdone = 0; BlockNumber blksdone = 0;
#ifdef USE_PREFETCH
int prefetch_maximum = 0; /* blocks to prefetch if enabled */
BlockSamplerData prefetch_bs;
#endif
Assert(targrows > 0); Assert(targrows > 0);
...@@ -1144,7 +1150,15 @@ acquire_sample_rows(Relation onerel, int elevel, ...@@ -1144,7 +1150,15 @@ acquire_sample_rows(Relation onerel, int elevel,
OldestXmin = GetOldestNonRemovableTransactionId(onerel); OldestXmin = GetOldestNonRemovableTransactionId(onerel);
/* Prepare for sampling block numbers */ /* Prepare for sampling block numbers */
nblocks = BlockSampler_Init(&bs, totalblocks, targrows, random()); randseed = random();
nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
#ifdef USE_PREFETCH
prefetch_maximum = get_tablespace_io_concurrency(onerel->rd_rel->reltablespace);
/* Create another BlockSampler, using the same seed, for prefetching */
if (prefetch_maximum)
(void) BlockSampler_Init(&prefetch_bs, totalblocks, targrows, randseed);
#endif
/* Report sampling block numbers */ /* Report sampling block numbers */
pgstat_progress_update_param(PROGRESS_ANALYZE_BLOCKS_TOTAL, pgstat_progress_update_param(PROGRESS_ANALYZE_BLOCKS_TOTAL,
...@@ -1156,14 +1170,69 @@ acquire_sample_rows(Relation onerel, int elevel, ...@@ -1156,14 +1170,69 @@ acquire_sample_rows(Relation onerel, int elevel,
scan = table_beginscan_analyze(onerel); scan = table_beginscan_analyze(onerel);
slot = table_slot_create(onerel, NULL); slot = table_slot_create(onerel, NULL);
#ifdef USE_PREFETCH
/*
* If we are doing prefetching, then go ahead and tell the kernel about
* the first set of pages we are going to want. This also moves our
* iterator out ahead of the main one being used, where we will keep it so
* that we're always pre-fetching out prefetch_maximum number of blocks
* ahead.
*/
if (prefetch_maximum)
{
for (int i = 0; i < prefetch_maximum; i++)
{
BlockNumber prefetch_block;
if (!BlockSampler_HasMore(&prefetch_bs))
break;
prefetch_block = BlockSampler_Next(&prefetch_bs);
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, prefetch_block);
}
}
#endif
/* Outer loop over blocks to sample */ /* Outer loop over blocks to sample */
while (BlockSampler_HasMore(&bs)) while (BlockSampler_HasMore(&bs))
{ {
bool block_accepted;
BlockNumber targblock = BlockSampler_Next(&bs); BlockNumber targblock = BlockSampler_Next(&bs);
#ifdef USE_PREFETCH
BlockNumber prefetch_targblock = InvalidBlockNumber;
/*
* Make sure that every time the main BlockSampler is moved forward
* that our prefetch BlockSampler also gets moved forward, so that we
* always stay out ahead.
*/
if (prefetch_maximum && BlockSampler_HasMore(&prefetch_bs))
prefetch_targblock = BlockSampler_Next(&prefetch_bs);
#endif
vacuum_delay_point(); vacuum_delay_point();
if (!table_scan_analyze_next_block(scan, targblock, vac_strategy)) block_accepted = table_scan_analyze_next_block(scan, targblock, vac_strategy);
#ifdef USE_PREFETCH
/*
* When pre-fetching, after we get a block, tell the kernel about the
* next one we will want, if there's any left.
*
* We want to do this even if the table_scan_analyze_next_block() call
* above decides against analyzing the block it picked.
*/
if (prefetch_maximum && prefetch_targblock != InvalidBlockNumber)
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, prefetch_targblock);
#endif
/*
* Don't analyze if table_scan_analyze_next_block() indicated this
* block is unsuitable for analyzing.
*/
if (!block_accepted)
continue; continue;
while (table_scan_analyze_next_tuple(scan, OldestXmin, &liverows, &deadrows, slot)) while (table_scan_analyze_next_tuple(scan, OldestXmin, &liverows, &deadrows, slot))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment