복합 인덱스에 대한 YugabyteDB 스킵 스캔 일명 느슨한 인덱스 스캔

인덱스 스캔이 두 번째 열의 범위에 액세스하기 위해 첫 번째 인덱스 열을 건너뛸 수 있는 방법을 테스트했습니다. 이를 위해서는 첫 번째 열을 범위별로 분할해야 했습니다.

여기서는 해시 샤딩을 사용하여 유사하지만 테스트하고 있습니다. 해시 열에 등호 술어가 있고 두 번째 술어는 건너뜁니다. 다음은 내가 실행한 결과이며 나중에 결과를 설명하겠습니다.

\! curl -s https://raw.githubusercontent.com/FranckPachot/ybdemo/main/docker/yb-lab/client/ybwr.sql | grep -v '\watch' > ybwr.sql
\i ybwr.sql
create table tbl ( A int, B int, C int );
insert into  tbl select 0,mod(m,10),m from generate_series(1,1000000) m;
create index i1 on tbl(A hash,B asc) INCLUDE (C);
create index i2 on tbl(A hash,B asc,C asc);
set yb_enable_expression_pushdown=on;
execute snap_table;
/*+ indexonlyscan(tbl i1) */ explain analyze select C from tbl where A =  0 and C = 900042;
execute snap_table;
/*+ indexonlyscan(tbl i2) */ explain analyze select C from tbl where A =  0 and C = 900042;
execute snap_table;
/*+ indexonlyscan(tbl i1) */ explain analyze select C from tbl where A =  0 and C > 900042;
execute snap_table;
/*+ indexonlyscan(tbl i2) */ explain analyze select C from tbl where A =  0 and C > 900042;
execute snap_table;


아이디어는 하나( i2 )에 모든 열이 인덱스 키로 있고 다른 하나( i1 )에 키의 일부가 아닌 마지막 열이 포함된 두 개의 포함 인덱스를 비교하는 것입니다. 해시된 첫 번째 열( A )에 등호 술어로 쿼리하고 세 번째 열( C )에 범위 술어를 사용하고 두 번째 열( B )에는 술어가 없습니다. Skip Scan이 가장 적합한 두 번째 열(B)에 대한 고유 값이 거의 없습니다.

포인트 쿼리



첫 번째 테스트는 A = 0 and C = 900042를 필터링합니다.

포함 색인 포함




yugabyte=# /*+ indexonlyscan(tbl i1) */ explain analyze select C from tbl where A =  0 and C = 900042;
                                                      QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Index Only Scan using i1 on tbl  (cost=0.00..15.75 rows=100 width=4) (actual time=3605.845..3605.848 rows=1 loops=1)
   Index Cond: (a = 0)
   Remote Filter: (c = 900042)
   Heap Fetches: 0
 Planning Time: 0.123 ms
 Execution Time: 3605.891 ms
 Peak Memory Usage: 8 kB
(7 rows)

yugabyte=# execute snap_table;
 rocksdb_seek | rocksdb_next | rocksdb_insert | dbname / relname / tserver / tabletid / leader
--------------+--------------+----------------+------------------------------------------------
            1 |              |                | yugabyte i1 10.0.0.61 9c0a2dd1193e... L
            1 |       999999 |                | yugabyte i1 10.0.0.62 359f44ea1136... L
            1 |              |                | yugabyte i1 10.0.0.63 61591ddbdd81... L
(3 rows)


여기에서 INCLUDE(C) 인덱스를 사용하면 (a = 0)에 대한 포인트 액세스가 없기 때문에 (c = 900042)에 대한 모든 행을 읽었습니다.

키 커버링 인덱스




yugabyte=# /*+ indexonlyscan(tbl i2) */ explain analyze select C from tbl where A =  0 and C = 900042;
                                                   QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 Index Only Scan using i2 on tbl  (cost=0.00..15.50 rows=100 width=4) (actual time=1.040..1.041 rows=1 loops=1)
   Index Cond: ((a = 0) AND (c = 900042))
   Heap Fetches: 0
 Planning Time: 0.108 ms
 Execution Time: 1.070 ms
 Peak Memory Usage: 8 kB
(6 rows)

yugabyte=# execute snap_table;
 rocksdb_seek | rocksdb_next | rocksdb_insert | dbname / relname / tserver / tabletid / leader
--------------+--------------+----------------+------------------------------------------------
           21 |           41 |                | yugabyte i2 10.0.0.63 135697ffc832... L
(1 row)


열이 인덱스 키에 있으면 LSM-Tree 구조에서 바로 건너뛸 수 있습니다. 인덱스( B )인 경우 두 번째 열에 여러 값이 있으므로 건너뛰기 작업이 여러 개 있습니다. 이는 건너뛴 열에 고유 값이 너무 많지 않은 경우에 효율적입니다. 기본적으로 인덱스 액세스는 한 번의 작업으로 여러 지점에서 읽습니다.

범위 쿼리



두 번째 테스트는 A = 0 and C > 900042를 필터링합니다.

커버링 인덱스 포함




yugabyte=# /*+ indexonlyscan(tbl i1) */ explain analyze select C from tbl where A =  0 and C > 900042;
execute snap_table;
                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 Index Only Scan using i1 on tbl  (cost=0.00..15.75 rows=100 width=4) (actual time=39.337..3799.021 rows=99958 loops=1)
   Index Cond: (a = 0)
   Remote Filter: (c > 900042)
   Heap Fetches: 0
 Planning Time: 0.106 ms
 Execution Time: 3808.669 ms
 Peak Memory Usage: 8 kB
(7 rows)

yugabyte=# execute snap_table;
 rocksdb_seek | rocksdb_next | rocksdb_insert | dbname / relname / tserver / tabletid / leader
--------------+--------------+----------------+------------------------------------------------
            1 |              |                | yugabyte i1 10.0.0.61 9c0a2dd1193e... L
           98 |      1000096 |                | yugabyte i1 10.0.0.62 359f44ea1136... L
            1 |              |                | yugabyte i1 10.0.0.63 61591ddbdd81... L
(3 rows)

C에 대한 조건은 건너뛰기 작업으로 최적화할 수 없으므로 위와 동일합니다. 색인 항목. Remote Filter는 YugabyteDB가 스토리지에 필터링을 푸시하지만 여전히 모든 인덱스 항목을 읽어야 하는 최적화입니다.

키 커버링 인덱스




yugabyte=# /*+ indexonlyscan(tbl i2) */ explain analyze select C from tbl where A =  0 and C > 900042;
                                                      QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Index Only Scan using i2 on tbl  (cost=0.00..15.50 rows=100 width=4) (actual time=6.563..595.860 rows=99958 loops=1)
   Index Cond: ((a = 0) AND (c > 900042))
   Heap Fetches: 0
 Planning Time: 0.109 ms
 Execution Time: 599.628 ms
 Peak Memory Usage: 8 kB
(6 rows)

yugabyte=# execute snap_table;
 rocksdb_seek | rocksdb_next | rocksdb_insert | dbname / relname / tserver / tabletid / leader
--------------+--------------+----------------+------------------------------------------------
          108 |       100074 |                | yugabyte i2 10.0.0.63 135697ffc832... L
(1 row)


술어가 사용하는 커버링 컬럼이 인덱스 키에 있을 때 필요한 것만 읽도록 최적화

결론



커버링 인덱스는 테이블로 이동할 필요가 없도록 인덱스 항목에 필요한 모든 열을 보유합니다. 이는 인덱스로 많은 행을 읽을 때, 특히 일부 필터링이 있는 열의 경우 테이블로 이동하기 전에 가능한 한 많이 삭제하는 데 효율적입니다.

그러면 적용되는 열이 인덱스 키에 있거나 INCLUDE로 추가될 수 있습니다.

INCLUDE는 해당 열이 고유 키의 일부가 아닌 고유 인덱스에 대해 필수입니다. LSM-Tree의 인덱스 항목 위치가 변경되지 않으므로 해당 열에 대한 업데이트가 있는 경우에도 선호될 수 있습니다.

그러나 해당 열에 특정 지점 또는 범위 조건이 있는 경우 올바른 지점에 도달하기 위해 LSM-트리 건너뛰기 작업의 이점을 얻으려면 인덱스 키에 있어야 합니다. 그리고 이것은 사이에 추가 열이 있더라도 YugabyteDB가 건너뛸 수 있기 때문에 내림차순으로 오름차순으로 표시됩니다.

이는 여러 쿼리를 제공할 수 있는 인덱스를 정의할 때 유연성을 제공합니다. 일반적으로 가장 선택적인 열을 먼저 배치하는 것으로 간주되지만 고유한 값이 적은 열이 있고 ASC 또는 DESC가 있는 경우 여러 쿼리를 처리할 수 있는 인덱스를 가질 수 있도록 선택적 열 앞에 배치하는 것이 좋습니다. . 이 예에서 ((a = 0) AND (c > 900042))를 효율적으로 제공하는 것 외에도 내 인덱스는 쿼리where A=0 and B=0도 제공할 수 있습니다. 이 쿼리는 (A HASH, C ASC)에 대한 인덱스로만 효율적이지 않습니다.

좋은 웹페이지 즐겨찾기