mirror of
https://github.com/postgres/postgres.git
synced 2025-05-27 00:04:24 -04:00
Add optimizer and executor support for parallel index-only scans.
Commit 5262f7a4fc44f651241d2ff1fa688dd664a34874 added similar support for parallel index scans; this extends that work to index-only scans. As with parallel index scans, this requires support from the index AM, so currently parallel index-only scans will only be possible for btree indexes. Rafia Sabih, reviewed and tested by Rahila Syed, Tushar Ahuja, and Amit Kapila Discussion: http://postgr.es/m/CAOGQiiPEAs4C=TBp0XShxBvnWXuzGL2u++Hm1=qnCpd6_Mf8Fw@mail.gmail.com
This commit is contained in:
parent
16be2fd100
commit
0414b26bac
@ -29,6 +29,7 @@
|
|||||||
#include "executor/nodeForeignscan.h"
|
#include "executor/nodeForeignscan.h"
|
||||||
#include "executor/nodeSeqscan.h"
|
#include "executor/nodeSeqscan.h"
|
||||||
#include "executor/nodeIndexscan.h"
|
#include "executor/nodeIndexscan.h"
|
||||||
|
#include "executor/nodeIndexonlyscan.h"
|
||||||
#include "executor/tqueue.h"
|
#include "executor/tqueue.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
@ -202,6 +203,10 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
|
|||||||
ExecIndexScanEstimate((IndexScanState *) planstate,
|
ExecIndexScanEstimate((IndexScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
|
case T_IndexOnlyScanState:
|
||||||
|
ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
|
||||||
|
e->pcxt);
|
||||||
|
break;
|
||||||
case T_ForeignScanState:
|
case T_ForeignScanState:
|
||||||
ExecForeignScanEstimate((ForeignScanState *) planstate,
|
ExecForeignScanEstimate((ForeignScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
@ -258,6 +263,10 @@ ExecParallelInitializeDSM(PlanState *planstate,
|
|||||||
ExecIndexScanInitializeDSM((IndexScanState *) planstate,
|
ExecIndexScanInitializeDSM((IndexScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
|
case T_IndexOnlyScanState:
|
||||||
|
ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
|
||||||
|
d->pcxt);
|
||||||
|
break;
|
||||||
case T_ForeignScanState:
|
case T_ForeignScanState:
|
||||||
ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
|
ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
@ -737,6 +746,9 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc)
|
|||||||
case T_IndexScanState:
|
case T_IndexScanState:
|
||||||
ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc);
|
ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc);
|
||||||
break;
|
break;
|
||||||
|
case T_IndexOnlyScanState:
|
||||||
|
ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate, toc);
|
||||||
|
break;
|
||||||
case T_ForeignScanState:
|
case T_ForeignScanState:
|
||||||
ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
|
ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
|
||||||
toc);
|
toc);
|
||||||
|
@ -21,6 +21,11 @@
|
|||||||
* ExecEndIndexOnlyScan releases all storage.
|
* ExecEndIndexOnlyScan releases all storage.
|
||||||
* ExecIndexOnlyMarkPos marks scan position.
|
* ExecIndexOnlyMarkPos marks scan position.
|
||||||
* ExecIndexOnlyRestrPos restores scan position.
|
* ExecIndexOnlyRestrPos restores scan position.
|
||||||
|
* ExecIndexOnlyScanEstimate estimates DSM space needed for
|
||||||
|
* parallel index-only scan
|
||||||
|
* ExecIndexOnlyScanInitializeDSM initialize DSM for parallel
|
||||||
|
* index-only scan
|
||||||
|
* ExecIndexOnlyScanInitializeWorker attach to DSM info in parallel worker
|
||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
@ -277,6 +282,16 @@ ExecIndexOnlyScan(IndexOnlyScanState *node)
|
|||||||
void
|
void
|
||||||
ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
|
ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
|
||||||
{
|
{
|
||||||
|
bool reset_parallel_scan = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are here to just update the scan keys, then don't reset parallel
|
||||||
|
* scan. For detailed reason behind this look in the comments for
|
||||||
|
* ExecReScanIndexScan.
|
||||||
|
*/
|
||||||
|
if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
|
||||||
|
reset_parallel_scan = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are doing runtime key calculations (ie, any of the index key
|
* If we are doing runtime key calculations (ie, any of the index key
|
||||||
* values weren't simple Consts), compute the new key values. But first,
|
* values weren't simple Consts), compute the new key values. But first,
|
||||||
@ -296,10 +311,16 @@ ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
|
|||||||
node->ioss_RuntimeKeysReady = true;
|
node->ioss_RuntimeKeysReady = true;
|
||||||
|
|
||||||
/* reset index scan */
|
/* reset index scan */
|
||||||
index_rescan(node->ioss_ScanDesc,
|
if (node->ioss_ScanDesc)
|
||||||
node->ioss_ScanKeys, node->ioss_NumScanKeys,
|
{
|
||||||
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
|
|
||||||
|
|
||||||
|
index_rescan(node->ioss_ScanDesc,
|
||||||
|
node->ioss_ScanKeys, node->ioss_NumScanKeys,
|
||||||
|
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
|
||||||
|
|
||||||
|
if (reset_parallel_scan && node->ioss_ScanDesc->parallel_scan)
|
||||||
|
index_parallelrescan(node->ioss_ScanDesc);
|
||||||
|
}
|
||||||
ExecScanReScan(&node->ss);
|
ExecScanReScan(&node->ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,29 +557,124 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
|
|||||||
/*
|
/*
|
||||||
* Initialize scan descriptor.
|
* Initialize scan descriptor.
|
||||||
*/
|
*/
|
||||||
indexstate->ioss_ScanDesc = index_beginscan(currentRelation,
|
if (!node->scan.plan.parallel_aware)
|
||||||
indexstate->ioss_RelationDesc,
|
{
|
||||||
estate->es_snapshot,
|
indexstate->ioss_ScanDesc = index_beginscan(currentRelation,
|
||||||
|
indexstate->ioss_RelationDesc,
|
||||||
|
estate->es_snapshot,
|
||||||
indexstate->ioss_NumScanKeys,
|
indexstate->ioss_NumScanKeys,
|
||||||
indexstate->ioss_NumOrderByKeys);
|
indexstate->ioss_NumOrderByKeys);
|
||||||
|
|
||||||
/* Set it up for index-only scan */
|
|
||||||
indexstate->ioss_ScanDesc->xs_want_itup = true;
|
|
||||||
indexstate->ioss_VMBuffer = InvalidBuffer;
|
|
||||||
|
|
||||||
/*
|
/* Set it up for index-only scan */
|
||||||
* If no run-time keys to calculate, go ahead and pass the scankeys to the
|
indexstate->ioss_ScanDesc->xs_want_itup = true;
|
||||||
* index AM.
|
indexstate->ioss_VMBuffer = InvalidBuffer;
|
||||||
*/
|
|
||||||
if (indexstate->ioss_NumRuntimeKeys == 0)
|
/*
|
||||||
index_rescan(indexstate->ioss_ScanDesc,
|
* If no run-time keys to calculate, go ahead and pass the scankeys to
|
||||||
indexstate->ioss_ScanKeys,
|
* the index AM.
|
||||||
indexstate->ioss_NumScanKeys,
|
*/
|
||||||
indexstate->ioss_OrderByKeys,
|
if (indexstate->ioss_NumRuntimeKeys == 0)
|
||||||
indexstate->ioss_NumOrderByKeys);
|
index_rescan(indexstate->ioss_ScanDesc,
|
||||||
|
indexstate->ioss_ScanKeys,
|
||||||
|
indexstate->ioss_NumScanKeys,
|
||||||
|
indexstate->ioss_OrderByKeys,
|
||||||
|
indexstate->ioss_NumOrderByKeys);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* all done.
|
* all done.
|
||||||
*/
|
*/
|
||||||
return indexstate;
|
return indexstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* Parallel Index-only Scan Support
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecIndexOnlyScanEstimate
|
||||||
|
*
|
||||||
|
* estimates the space required to serialize index-only scan node.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
|
||||||
|
ParallelContext *pcxt)
|
||||||
|
{
|
||||||
|
EState *estate = node->ss.ps.state;
|
||||||
|
|
||||||
|
node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
|
||||||
|
estate->es_snapshot);
|
||||||
|
shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
|
||||||
|
shm_toc_estimate_keys(&pcxt->estimator, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecIndexOnlyScanInitializeDSM
|
||||||
|
*
|
||||||
|
* Set up a parallel index-only scan descriptor.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
|
||||||
|
ParallelContext *pcxt)
|
||||||
|
{
|
||||||
|
EState *estate = node->ss.ps.state;
|
||||||
|
ParallelIndexScanDesc piscan;
|
||||||
|
|
||||||
|
piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
|
||||||
|
index_parallelscan_initialize(node->ss.ss_currentRelation,
|
||||||
|
node->ioss_RelationDesc,
|
||||||
|
estate->es_snapshot,
|
||||||
|
piscan);
|
||||||
|
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
|
||||||
|
node->ioss_ScanDesc =
|
||||||
|
index_beginscan_parallel(node->ss.ss_currentRelation,
|
||||||
|
node->ioss_RelationDesc,
|
||||||
|
node->ioss_NumScanKeys,
|
||||||
|
node->ioss_NumOrderByKeys,
|
||||||
|
piscan);
|
||||||
|
node->ioss_ScanDesc->xs_want_itup = true;
|
||||||
|
node->ioss_VMBuffer = InvalidBuffer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no run-time keys to calculate, go ahead and pass the scankeys to
|
||||||
|
* the index AM.
|
||||||
|
*/
|
||||||
|
if (node->ioss_NumRuntimeKeys == 0)
|
||||||
|
index_rescan(node->ioss_ScanDesc,
|
||||||
|
node->ioss_ScanKeys, node->ioss_NumScanKeys,
|
||||||
|
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecIndexOnlyScanInitializeWorker
|
||||||
|
*
|
||||||
|
* Copy relevant information from TOC into planstate.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node, shm_toc *toc)
|
||||||
|
{
|
||||||
|
ParallelIndexScanDesc piscan;
|
||||||
|
|
||||||
|
piscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id);
|
||||||
|
node->ioss_ScanDesc =
|
||||||
|
index_beginscan_parallel(node->ss.ss_currentRelation,
|
||||||
|
node->ioss_RelationDesc,
|
||||||
|
node->ioss_NumScanKeys,
|
||||||
|
node->ioss_NumOrderByKeys,
|
||||||
|
piscan);
|
||||||
|
node->ioss_ScanDesc->xs_want_itup = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no run-time keys to calculate, go ahead and pass the scankeys to the
|
||||||
|
* index AM.
|
||||||
|
*/
|
||||||
|
if (node->ioss_NumRuntimeKeys == 0)
|
||||||
|
index_rescan(node->ioss_ScanDesc,
|
||||||
|
node->ioss_ScanKeys, node->ioss_NumScanKeys,
|
||||||
|
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
|
||||||
|
}
|
||||||
|
@ -1048,9 +1048,9 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If appropriate, consider parallel index scan. We don't allow
|
* If appropriate, consider parallel index scan. We don't allow
|
||||||
* parallel index scan for bitmap or index only scans.
|
* parallel index scan for bitmap index scans.
|
||||||
*/
|
*/
|
||||||
if (index->amcanparallel && !index_only_scan &&
|
if (index->amcanparallel &&
|
||||||
rel->consider_parallel && outer_relids == NULL &&
|
rel->consider_parallel && outer_relids == NULL &&
|
||||||
scantype != ST_BITMAPSCAN)
|
scantype != ST_BITMAPSCAN)
|
||||||
{
|
{
|
||||||
@ -1104,7 +1104,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
result = lappend(result, ipath);
|
result = lappend(result, ipath);
|
||||||
|
|
||||||
/* If appropriate, consider parallel index scan */
|
/* If appropriate, consider parallel index scan */
|
||||||
if (index->amcanparallel && !index_only_scan &&
|
if (index->amcanparallel &&
|
||||||
rel->consider_parallel && outer_relids == NULL &&
|
rel->consider_parallel && outer_relids == NULL &&
|
||||||
scantype != ST_BITMAPSCAN)
|
scantype != ST_BITMAPSCAN)
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#define NODEINDEXONLYSCAN_H
|
#define NODEINDEXONLYSCAN_H
|
||||||
|
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
|
#include "access/parallel.h"
|
||||||
|
|
||||||
extern IndexOnlyScanState *ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags);
|
extern IndexOnlyScanState *ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags);
|
||||||
extern TupleTableSlot *ExecIndexOnlyScan(IndexOnlyScanState *node);
|
extern TupleTableSlot *ExecIndexOnlyScan(IndexOnlyScanState *node);
|
||||||
@ -23,4 +24,12 @@ extern void ExecIndexOnlyMarkPos(IndexOnlyScanState *node);
|
|||||||
extern void ExecIndexOnlyRestrPos(IndexOnlyScanState *node);
|
extern void ExecIndexOnlyRestrPos(IndexOnlyScanState *node);
|
||||||
extern void ExecReScanIndexOnlyScan(IndexOnlyScanState *node);
|
extern void ExecReScanIndexOnlyScan(IndexOnlyScanState *node);
|
||||||
|
|
||||||
|
/* Support functions for parallel index-only scans */
|
||||||
|
extern void ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
|
||||||
|
ParallelContext *pcxt);
|
||||||
|
extern void ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
|
||||||
|
ParallelContext *pcxt);
|
||||||
|
extern void ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
|
||||||
|
shm_toc *toc);
|
||||||
|
|
||||||
#endif /* NODEINDEXONLYSCAN_H */
|
#endif /* NODEINDEXONLYSCAN_H */
|
||||||
|
@ -1409,6 +1409,7 @@ typedef struct IndexScanState
|
|||||||
* ScanDesc index scan descriptor
|
* ScanDesc index scan descriptor
|
||||||
* VMBuffer buffer in use for visibility map testing, if any
|
* VMBuffer buffer in use for visibility map testing, if any
|
||||||
* HeapFetches number of tuples we were forced to fetch from heap
|
* HeapFetches number of tuples we were forced to fetch from heap
|
||||||
|
* ioss_PscanLen Size of parallel index-only scan descriptor
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
typedef struct IndexOnlyScanState
|
typedef struct IndexOnlyScanState
|
||||||
@ -1427,6 +1428,7 @@ typedef struct IndexOnlyScanState
|
|||||||
IndexScanDesc ioss_ScanDesc;
|
IndexScanDesc ioss_ScanDesc;
|
||||||
Buffer ioss_VMBuffer;
|
Buffer ioss_VMBuffer;
|
||||||
long ioss_HeapFetches;
|
long ioss_HeapFetches;
|
||||||
|
Size ioss_PscanLen;
|
||||||
} IndexOnlyScanState;
|
} IndexOnlyScanState;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -92,12 +92,14 @@ explain (costs off)
|
|||||||
explain (costs off)
|
explain (costs off)
|
||||||
select sum(parallel_restricted(unique1)) from tenk1
|
select sum(parallel_restricted(unique1)) from tenk1
|
||||||
group by(parallel_restricted(unique1));
|
group by(parallel_restricted(unique1));
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
HashAggregate
|
HashAggregate
|
||||||
Group Key: parallel_restricted(unique1)
|
Group Key: parallel_restricted(unique1)
|
||||||
-> Index Only Scan using tenk1_unique1 on tenk1
|
-> Gather
|
||||||
(3 rows)
|
Workers Planned: 4
|
||||||
|
-> Parallel Index Only Scan using tenk1_unique1 on tenk1
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
-- test parallel plans for queries containing un-correlated subplans.
|
-- test parallel plans for queries containing un-correlated subplans.
|
||||||
alter table tenk2 set (parallel_workers = 0);
|
alter table tenk2 set (parallel_workers = 0);
|
||||||
@ -146,6 +148,25 @@ select count((unique1)) from tenk1 where hundred > 1;
|
|||||||
9800
|
9800
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- test parallel index-only scans.
|
||||||
|
explain (costs off)
|
||||||
|
select count(*) from tenk1 where thousand > 95;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Finalize Aggregate
|
||||||
|
-> Gather
|
||||||
|
Workers Planned: 4
|
||||||
|
-> Partial Aggregate
|
||||||
|
-> Parallel Index Only Scan using tenk1_thous_tenthous on tenk1
|
||||||
|
Index Cond: (thousand > 95)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
select count(*) from tenk1 where thousand > 95;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
9040
|
||||||
|
(1 row)
|
||||||
|
|
||||||
reset enable_seqscan;
|
reset enable_seqscan;
|
||||||
reset enable_bitmapscan;
|
reset enable_bitmapscan;
|
||||||
set force_parallel_mode=1;
|
set force_parallel_mode=1;
|
||||||
|
@ -56,6 +56,11 @@ explain (costs off)
|
|||||||
select count((unique1)) from tenk1 where hundred > 1;
|
select count((unique1)) from tenk1 where hundred > 1;
|
||||||
select count((unique1)) from tenk1 where hundred > 1;
|
select count((unique1)) from tenk1 where hundred > 1;
|
||||||
|
|
||||||
|
-- test parallel index-only scans.
|
||||||
|
explain (costs off)
|
||||||
|
select count(*) from tenk1 where thousand > 95;
|
||||||
|
select count(*) from tenk1 where thousand > 95;
|
||||||
|
|
||||||
reset enable_seqscan;
|
reset enable_seqscan;
|
||||||
reset enable_bitmapscan;
|
reset enable_bitmapscan;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user