PostgreSQL - Fix gin index cost estimation 이슈 분석

이동욱

2022/12/12

Categories: 오픈소스 Tags: PostgreSQL

Ronan Dunklau -> PG Hackers

1. B+TREE와 비교해서, GIN, GIST, SP-GIST 인덱스들을 CPU 비용을 계산하지 않는다. 엔트리 트리를 내려가는 동안에... -> 이는 낮는 IO 비용을 가지고 있는 연산에서 문제가 될 수 있다.

2. 네스티드 루프에서 많은 반복에 의해서 IO 비용이 줄어들 떄 모든 것이 SHARED BUFFER 에있기 때문에 비용이 무료라고 간주한다.

3. 해당 이슈는 PG14 버전에서 발견되었으며, 해당 버전에서는 PG_TRGM, GIN 인덱스에서 '=' 연산자를 사용할 수 있다.

4. 이전에는 B+TREE에 대해서만, 고려를 하고 있었다. 따라서 BTREE_GIN 익스텐션 역시 위와 같은 문제를 일으킬 것이라고 생각한다.
create temp table t_gin_test_tbl(i int4[], j int4[]);
create index on t_gin_test_tbl using gin (i, j);
insert into t_gin_test_tbl
values
  (null,    null),
  ('{}',    null),
  ('{1}',   null),
  ('{1,2}', null),
  (null,    '{}'),
  (null,    '{10}'),
  ('{1,2}', '{10}'),
  ('{2}',   '{10}'),
  ('{1,3}', '{}'),
  ('{1,1}', '{10}');

set enable_seqscan = off;

explain (costs off)
select * from t_gin_test_tbl where array[0] <@ i;
select * from t_gin_test_tbl where array[0] <@ i;
select * from t_gin_test_tbl where array[0] <@ i and '{}'::int4[] <@ j;

Tom Lane -> Ronan Dunklau

CREATE EXTENSION btree_gist;
CREATE EXTENSION pg_trgm;
CREATE EXTENSION bloom;

drop table if exists t1,t2;

CREATE TABLE t1 (
          id text
        );

CREATE TABLE t2 (
          id text primary key,
            t1_id text
        );

insert into t1 (id)
select i::text FROM generate_series(1, 1500) as i;

insert into t2 (id, t1_id)
SELECT i::text, (i % 1500 + 1)::text FROM generate_series(1, 20000) i;

ANALYZE t1, t2;

SET random_page_cost = 0.00000001;
-- SET random_page_cost = 1.0;
SET enable_hashjoin = off;
SET enable_mergejoin = off;
SET enable_memoize = off;

CREATE INDEX t1_btree_index ON t1 USING btree (id);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_btree_index;

CREATE INDEX t1_gin_index ON t1 USING gin (id gin_trgm_ops);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_gin_index;

CREATE INDEX t1_gist_index ON t1 USING gist (id);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_gist_index;

CREATE INDEX t1_spgist_index ON t1 USING spgist (id);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_spgist_index;

CREATE INDEX t1_hash_index ON t1 USING hash (id);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_hash_index;

CREATE INDEX t1_brin_index ON t1 USING brin (id);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_brin_index;

CREATE INDEX t1_bloom_index ON t1 USING bloom (id);
explain (analyze, buffers) select * from t1 join t2 on t1.id = t2.t1_id ;
DROP INDEX t1_bloom_index;

* If there are ScalarArrayOpExprs, charge this once per SA scan.  The
* ones after the first one are not startup cost so far as the overall
* plan is concerned, so add them only to "total" cost.

참고 문헌

>> Home