Skip to content

Commit 2cc22eb

Browse files
wangxuqireshke
authored andcommitted
Speed up AOCS index build by only scan needed columns. (#13012)
On first index creation (block directory is created) and only that time all columns are scanned. Later any index creation just scans needed columns. cherry-picked. Note influence of greenplum-db/gpdb-archive@8c92aa0
1 parent 8da79b3 commit 2cc22eb

File tree

4 files changed

+152
-24
lines changed

4 files changed

+152
-24
lines changed

src/backend/access/aocs/aocsam_handler.c

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -607,15 +607,13 @@ extractcolumns_from_node(Node *expr, bool *cols, AttrNumber natts)
607607
return ecCtx.found;
608608
}
609609

610-
static TableScanDesc
611-
aoco_beginscan_extractcolumns(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key,
612-
ParallelTableScanDesc parallel_scan,
613-
PlanState *ps, uint32 flags)
610+
AOCSScanDesc
611+
aoco_beginscan_extractcolumns_quals(Relation rel, Snapshot snapshot,
612+
List *targetlist, List *qual,
613+
ParallelTableScanDesc parallel_scan, uint32 flags)
614614
{
615615
AOCSScanDesc aoscan;
616616
AttrNumber natts = RelationGetNumberOfAttributes(rel);
617-
List *targetlist = ps->plan->targetlist;
618-
List *qual = ps->plan->qual;
619617
bool *cols;
620618
bool found = false;
621619

@@ -639,6 +637,19 @@ aoco_beginscan_extractcolumns(Relation rel, Snapshot snapshot, int nkeys, struct
639637
flags);
640638

641639
pfree(cols);
640+
return aoscan;
641+
}
642+
643+
static TableScanDesc
644+
aoco_beginscan_extractcolumns(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key,
645+
ParallelTableScanDesc parallel_scan,
646+
PlanState *ps, uint32 flags)
647+
{
648+
AOCSScanDesc aoscan;
649+
List *targetlist = ps->plan->targetlist;
650+
List *qual = ps->plan->qual;
651+
652+
aoscan = aoco_beginscan_extractcolumns_quals(rel, snapshot, targetlist, qual, parallel_scan, flags);
642653

643654
if (gp_enable_predicate_pushdown)
644655
ps->qual = aocs_predicate_pushdown_prepare(aoscan, qual, ps->qual, ps->ps_ExprContext, ps);
@@ -1687,14 +1698,13 @@ aoco_index_build_range_scan(Relation heapRelation,
16871698
int64 total_blockcount = 0;
16881699
BlockNumber lastBlock = start_blockno;
16891700
int64 blockcounts = 0;
1690-
#if 0
16911701
bool need_create_blk_directory = false;
16921702
List *tlist = NIL;
16931703
List *qual = indexInfo->ii_Predicate;
1694-
#endif
16951704
Oid blkdirrelid;
16961705
Oid blkidxrelid;
16971706
int64 previous_blkno = -1;
1707+
Relation blkdir;
16981708

16991709
/*
17001710
* sanity checks
@@ -1734,6 +1744,17 @@ aoco_index_build_range_scan(Relation heapRelation,
17341744
/* Set up execution state for predicate, if any. */
17351745
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
17361746

1747+
/*
1748+
* If block directory is empty, it must also be built along with the index.
1749+
*/
1750+
GetAppendOnlyEntryAuxOids(heapRelation, NULL,
1751+
&blkdirrelid, &blkidxrelid, NULL, NULL);
1752+
1753+
blkdir = relation_open(blkdirrelid, AccessShareLock);
1754+
1755+
need_create_blk_directory = RelationGetNumberOfBlocks(blkdir) == 0;
1756+
relation_close(blkdir, NoLock);
1757+
17371758
if (!scan)
17381759
{
17391760
/*
@@ -1744,12 +1765,50 @@ aoco_index_build_range_scan(Relation heapRelation,
17441765
*/
17451766
snapshot = SnapshotAny;
17461767

1747-
scan = table_beginscan_strat(heapRelation, /* relation */
1748-
snapshot, /* snapshot */
1749-
0, /* number of keys */
1750-
NULL, /* scan key */
1751-
true, /* buffer access strategy OK */
1752-
allow_sync); /* syncscan OK? */
1768+
/*
1769+
* Scan all columns if we need to create block directory.
1770+
*/
1771+
if (need_create_blk_directory)
1772+
{
1773+
scan = table_beginscan_strat(heapRelation, /* relation */
1774+
snapshot, /* snapshot */
1775+
0, /* number of keys */
1776+
NULL, /* scan key */
1777+
true, /* buffer access strategy OK */
1778+
allow_sync); /* syncscan OK? */
1779+
}
1780+
else
1781+
{
1782+
uint32 flags = SO_TYPE_SEQSCAN |
1783+
SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE;
1784+
/*
1785+
* if block directory has created, we can only scan needed column.
1786+
*/
1787+
for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
1788+
{
1789+
AttrNumber attrnum = indexInfo->ii_IndexAttrNumbers[i];
1790+
Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(heapRelation), attrnum - 1);
1791+
Var *var = makeVar(i,
1792+
attrnum,
1793+
attr->atttypid,
1794+
attr->atttypmod,
1795+
attr->attcollation,
1796+
0);
1797+
1798+
/* Build a target list from index info */
1799+
tlist = lappend(tlist,
1800+
makeTargetEntry((Expr *) var,
1801+
list_length(tlist) + 1,
1802+
NULL,
1803+
false));
1804+
}
1805+
1806+
/* Push down target list and qual to scan */
1807+
scan = (TableScanDesc)aoco_beginscan_extractcolumns_quals(heapRelation, /* relation */
1808+
snapshot, /* snapshot */
1809+
tlist, /* targetlist */
1810+
qual, NULL, flags); /* qual */
1811+
}
17531812
}
17541813
else
17551814
{
@@ -1767,11 +1826,6 @@ aoco_index_build_range_scan(Relation heapRelation,
17671826

17681827
aocoscan = (AOCSScanDesc) scan;
17691828

1770-
/*
1771-
* If block directory is empty, it must also be built along with the index.
1772-
*/
1773-
GetAppendOnlyEntryAuxOids(heapRelation, NULL,
1774-
&blkdirrelid, &blkidxrelid, NULL, NULL);
17751829
/*
17761830
* Note that block directory is created during creation of the first
17771831
* index. If it is found empty, it means the block directory was created
@@ -1781,8 +1835,7 @@ aoco_index_build_range_scan(Relation heapRelation,
17811835
* blocked. We can rest assured of exclusive access to the block
17821836
* directory relation.
17831837
*/
1784-
Relation blkdir = relation_open(blkdirrelid, AccessShareLock);
1785-
if (RelationGetNumberOfBlocks(blkdir) == 0)
1838+
if (need_create_blk_directory)
17861839
{
17871840
/*
17881841
* Allocate blockDirectory in scan descriptor to let the access method
@@ -1792,7 +1845,6 @@ aoco_index_build_range_scan(Relation heapRelation,
17921845
Assert(aocoscan->blockDirectory == NULL);
17931846
aocoscan->blockDirectory = palloc0(sizeof(AppendOnlyBlockDirectory));
17941847
}
1795-
relation_close(blkdir, NoLock);
17961848

17971849

17981850
/* Publish number of blocks to scan */

src/include/cdb/cdbaocsam.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ extern void aoco_dml_init(Relation relation, CmdType operation);
401401
extern void aoco_dml_finish(Relation relation, CmdType operation);
402402

403403
extern bool extractcolumns_from_node(Node *expr, bool *cols, AttrNumber natts);
404+
extern AOCSScanDesc aoco_beginscan_extractcolumns_quals(Relation rel, Snapshot snapshot,
405+
List *targetlist, List *qual,
406+
ParallelTableScanDesc parallel_scan, uint32 flags);
407+
404408
extern ExprState * aocs_predicate_pushdown_prepare(AOCSScanDesc scan,
405409
List *qual,
406410
ExprState *state,

src/test/regress/input/aocs.source

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ insert into aocs_index_cols values (1, 'foo', 'bar');
623623

624624
-- Create an index on the table. This is the first index on the table, so it
625625
-- creates the ao block directory, and scans all columns.
626-
create index on aocs_index_cols (id);
626+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
627627

628628
-- Create a partial index. This index needs to scan two columns; one is used
629629
-- as the index column, and the other in the WHERE clause.
@@ -639,6 +639,34 @@ select * from aocs_index_cols where a like 'f%';
639639
select * from aocs_index_cols where length(b) = 3;
640640
reset enable_seqscan;
641641

642+
-- Recreate aocs_index_cols_id_idx. This index needs to scan only one columns.
643+
drop index aocs_index_cols_id_idx;
644+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
645+
-- Check that the row is found using the new index.
646+
set enable_seqscan=off;
647+
select * from aocs_index_cols where id = 1;
648+
649+
-- Test for corner cases
650+
drop table aocs_index_cols;
651+
create table aocs_index_cols (id int4, a text, b text) with (appendonly=true, orientation=column);
652+
insert into aocs_index_cols values (1, 'foo', 'bar');
653+
-- Create index in a txn first, then rollback, then create index again.
654+
-- No blockdir will be created when rollback.
655+
begin;
656+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
657+
abort;
658+
select blkdirrelid=0 as blk_dir_not_exist from pg_appendonly where relid = 'aocs_index_cols'::regclass;
659+
660+
-- Create the first index and then drop it, blockdir existed.
661+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
662+
drop index aocs_index_cols_id_idx;
663+
select blkdirrelid=0 as blk_dir_not_exist from pg_appendonly where relid = 'aocs_index_cols'::regclass;
664+
665+
-- Create again. This index needs to scan only one columns.
666+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
667+
select * from aocs_index_cols where id = 1;
668+
669+
reset enable_seqscan;
642670

643671
-- Small content as well as bulk dense content headers need to be used
644672
-- appropriately if compression is found to be not useful. This test

src/test/regress/output/aocs.source

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1250,7 +1250,7 @@ create table aocs_index_cols (id int4, a text, b text) with (appendonly=true, or
12501250
insert into aocs_index_cols values (1, 'foo', 'bar');
12511251
-- Create an index on the table. This is the first index on the table, so it
12521252
-- creates the ao block directory, and scans all columns.
1253-
create index on aocs_index_cols (id);
1253+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
12541254
-- Create a partial index. This index needs to scan two columns; one is used
12551255
-- as the index column, and the other in the WHERE clause.
12561256
create index on aocs_index_cols (id) WHERE a like 'f%';
@@ -1276,6 +1276,50 @@ select * from aocs_index_cols where length(b) = 3;
12761276
1 | foo | bar
12771277
(1 row)
12781278

1279+
reset enable_seqscan;
1280+
-- Recreate aocs_index_cols_id_idx. This index needs to scan only one columns.
1281+
drop index aocs_index_cols_id_idx;
1282+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
1283+
-- Check that the row is found using the new index.
1284+
set enable_seqscan=off;
1285+
select * from aocs_index_cols where id = 1;
1286+
id | a | b
1287+
----+-----+-----
1288+
1 | foo | bar
1289+
(1 row)
1290+
1291+
-- Test for corner cases
1292+
drop table aocs_index_cols;
1293+
create table aocs_index_cols (id int4, a text, b text) with (appendonly=true, orientation=column);
1294+
insert into aocs_index_cols values (1, 'foo', 'bar');
1295+
-- Create index in a txn first, then rollback, then create index again.
1296+
-- No blockdir will be created when rollback.
1297+
begin;
1298+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
1299+
abort;
1300+
select blkdirrelid=0 as blk_dir_not_exist from pg_appendonly where relid = 'aocs_index_cols'::regclass;
1301+
blk_dir_not_exist
1302+
-------------------
1303+
t
1304+
(1 row)
1305+
1306+
-- Create the first index and then drop it, blockdir existed.
1307+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
1308+
drop index aocs_index_cols_id_idx;
1309+
select blkdirrelid=0 as blk_dir_not_exist from pg_appendonly where relid = 'aocs_index_cols'::regclass;
1310+
blk_dir_not_exist
1311+
-------------------
1312+
f
1313+
(1 row)
1314+
1315+
-- Create again. This index needs to scan only one columns.
1316+
create index aocs_index_cols_id_idx on aocs_index_cols (id);
1317+
select * from aocs_index_cols where id = 1;
1318+
id | a | b
1319+
----+-----+-----
1320+
1 | foo | bar
1321+
(1 row)
1322+
12791323
reset enable_seqscan;
12801324
-- Small content as well as bulk dense content headers need to be used
12811325
-- appropriately if compression is found to be not useful. This test

0 commit comments

Comments
 (0)