diff options
author | Tomas Vondra <tomas.vondra@postgresql.org> | 2020-04-06 21:33:28 +0200 |
---|---|---|
committer | Tomas Vondra <tomas.vondra@postgresql.org> | 2020-04-06 21:35:10 +0200 |
commit | d2d8a229bc58a2014dce1c7a4fcdb6c5ab9fb8da (patch) | |
tree | 66e2560c49ee43d13c29bd9f5760731001312738 /src/include/nodes/execnodes.h | |
parent | 3c8553547b1493c4afdb80393f4a47dbfa019a79 (diff) | |
download | postgresql-d2d8a229bc58a2014dce1c7a4fcdb6c5ab9fb8da.tar.gz postgresql-d2d8a229bc58a2014dce1c7a4fcdb6c5ab9fb8da.zip |
Implement Incremental Sort
Incremental Sort is an optimized variant of multikey sort for cases when
the input is already sorted by a prefix of the requested sort keys. For
example when the relation is already sorted by (key1, key2) and we need
to sort it by (key1, key2, key3) we can simply split the input rows into
groups having equal values in (key1, key2), and only sort/compare the
remaining column key3.
This has a number of benefits:
- Reduced memory consumption, because only a single group (determined by
values in the sorted prefix) needs to be kept in memory. This may also
eliminate the need to spill to disk.
- Lower startup cost, because Incremental Sort produce results after each
prefix group, which is beneficial for plans where startup cost matters
(like for example queries with LIMIT clause).
We consider both Sort and Incremental Sort, and decide based on costing.
The implemented algorithm operates in two different modes:
- Fetching a minimum number of tuples without check of equality on the
prefix keys, and sorting on all columns when safe.
- Fetching all tuples for a single prefix group and then sorting by
comparing only the remaining (non-prefix) keys.
We always start in the first mode, and employ a heuristic to switch into
the second mode if we believe it's beneficial - the goal is to minimize
the number of unnecessary comparions while keeping memory consumption
below work_mem.
This is a very old patch series. The idea was originally proposed by
Alexander Korotkov back in 2013, and then revived in 2017. In 2018 the
patch was taken over by James Coleman, who wrote and rewrote most of the
current code.
There were many reviewers/contributors since 2013 - I've done my best to
pick the most active ones, and listed them in this commit message.
Author: James Coleman, Alexander Korotkov
Reviewed-by: Tomas Vondra, Andreas Karlsson, Marti Raudsepp, Peter Geoghegan, Robert Haas, Thomas Munro, Antonin Houska, Andres Freund, Alexander Kuzmenkov
Discussion: https://postgr.es/m/CAPpHfdscOX5an71nHd8WSUH6GNOCf=V7wgDaTXdDd9=goN-gfA@mail.gmail.com
Discussion: https://postgr.es/m/CAPpHfds1waRZ=NOmueYq0sx1ZSCnt+5QJvizT8ndT2=etZEeAQ@mail.gmail.com
Diffstat (limited to 'src/include/nodes/execnodes.h')
-rw-r--r-- | src/include/nodes/execnodes.h | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 0fb5d61a3f6..fb490b404ce 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1982,6 +1982,21 @@ typedef struct MaterialState Tuplestorestate *tuplestorestate; } MaterialState; + +/* ---------------- + * When performing sorting by multiple keys, it's possible that the input + * dataset is already sorted on a prefix of those keys. We call these + * "presorted keys". + * PresortedKeyData represents information about one such key. + * ---------------- + */ +typedef struct PresortedKeyData +{ + FmgrInfo flinfo; /* comparison function info */ + FunctionCallInfo fcinfo; /* comparison function call info */ + OffsetNumber attno; /* attribute number in tuple */ +} PresortedKeyData; + /* ---------------- * Shared memory container for per-worker sort information * ---------------- @@ -2010,6 +2025,71 @@ typedef struct SortState SharedSortInfo *shared_info; /* one entry per worker */ } SortState; +/* ---------------- + * Instrumentation information for IncrementalSort + * ---------------- + */ +typedef struct IncrementalSortGroupInfo +{ + int64 groupCount; + long maxDiskSpaceUsed; + long totalDiskSpaceUsed; + long maxMemorySpaceUsed; + long totalMemorySpaceUsed; + bits32 sortMethods; /* bitmask of TuplesortMethod */ +} IncrementalSortGroupInfo; + +typedef struct IncrementalSortInfo +{ + IncrementalSortGroupInfo fullsortGroupInfo; + IncrementalSortGroupInfo prefixsortGroupInfo; +} IncrementalSortInfo; + +/* ---------------- + * Shared memory container for per-worker incremental sort information + * ---------------- + */ +typedef struct SharedIncrementalSortInfo +{ + int num_workers; + IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER]; +} SharedIncrementalSortInfo; + +/* ---------------- + * IncrementalSortState information + * ---------------- + */ +typedef enum +{ + INCSORT_LOADFULLSORT, + INCSORT_LOADPREFIXSORT, + INCSORT_READFULLSORT, + INCSORT_READPREFIXSORT, +} IncrementalSortExecutionStatus; + +typedef struct IncrementalSortState +{ + ScanState ss; /* its first field is NodeTag */ + bool bounded; /* is the result set bounded? */ + int64 bound; /* if bounded, how many tuples are needed */ + bool outerNodeDone; /* finished fetching tuples from outer node */ + int64 bound_Done; /* value of bound we did the sort with */ + IncrementalSortExecutionStatus execution_status; + int64 n_fullsort_remaining; + Tuplesortstate *fullsort_state; /* private state of tuplesort.c */ + Tuplesortstate *prefixsort_state; /* private state of tuplesort.c */ + /* the keys by which the input path is already sorted */ + PresortedKeyData *presorted_keys; + + IncrementalSortInfo incsort_info; + + /* slot for pivot tuple defining values of presorted keys within group */ + TupleTableSlot *group_pivot; + TupleTableSlot *transfer_tuple; + bool am_worker; /* are we a worker? */ + SharedIncrementalSortInfo *shared_info; /* one entry per worker */ +} IncrementalSortState; + /* --------------------- * GroupState information * --------------------- |