0

postgresqlで(3列の)複合主キーを持つテーブルを作成しました。先頭の列を含まないクエリでサブセットを使用すると、デフォルトのインデックスが使用されない。インデックスを明示的に作成する場合はそうではありません (インデックスはサブセットに使用されます)。

デフォルトでは、postgres は主キーにインデックスを作成します。しかし、postgreドキュメントが言うように

A multicolumn B-tree index can be used with query conditions that involve any subset of the index's columns, but the index is most efficient when there are constraints on the leading (leftmost) columns.

クエリに先頭の列が含まれていない場合は、インデックスも使用されます (インデックスを明示的に作成した場合)。

以下は、サブセットで機能しないスキーマとクエリです。

# \d client_data
              Table "public.client_data"
       Column       |         Type          | Modifiers 
--------------------+-----------------------+-----------
 macaddr            | character varying(64) | not null
 ts                 | bigint                | not null
 interval           | smallint              | not null
 snr                | smallint              | not null
 rx_rate            | bigint                | 
 tx_rate            | bigint                | 
 rx_data            | bigint                | 
 tx_data            | bigint                | 

Indexes:
    "client_data_pkey" PRIMARY KEY, btree (macaddr, ts, interval)

すべての主キー列を指定すると、クエリ プランナーはインデックス作成を使用します

# explain analyze select count(*) from client_data where macaddr='a:b:c' and ts=346783556 and interval=5;
                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=8.60..8.61 rows=1 width=0) (actual time=0.040..0.041 rows=1 loops=1)
   ->  Index Scan using client_data_pkey on client_data  (cost=0.00..8.59 rows=1 width=0) (actual time=0.037..0.037 rows=0 loops=1)
         Index Cond: (((macaddr)::text = 'a:b:c'::text) AND (ts = 346783556) AND ("interval" = 5))
 Total runtime: 0.096 ms
(4 rows)

ただし、サブセットを指定すると、クエリ プランナーはインデックス作成を使用しません。

# explain analyze select count(*) from client_data where ts=346783556;
                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=16176.01..16176.02 rows=1 width=0) (actual time=78.937..78.938 rows=1 loops=1)
   ->  Seq Scan on client_data  (cost=0.00..16175.92 rows=36 width=0) (actual time=78.932..78.932 rows=0 loops=1)
         Filter: (ts = 346783556)
 Total runtime: 78.975 ms
(4 rows)


# explain analyze select count(*) from client_data where ts=346783556 and interval=5;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=17639.11..17639.12 rows=1 width=0) (actual time=78.815..78.815 rows=1 loops=1)
   ->  Seq Scan on client_data  (cost=0.00..17639.11 rows=1 width=0) (actual time=78.810..78.810 rows=0 loops=1)
         Filter: ((ts = 346783556) AND ("interval" = 5))
 Total runtime: 78.853 ms
(4 rows)

しかし、ts または interval のいずれかで先行列 (macaddr) を使用すると、索引付けが使用されます。

# explain analyze select count(*) from client_data where macaddr='a' and ts=346783556;
                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=8.59..8.60 rows=1 width=0) (actual time=0.055..0.056 rows=1 loops=1)
   ->  Index Scan using client_data_pkey on client_data  (cost=0.00..8.59 rows=1 width=0) (actual time=0.051..0.051 rows=0 loops=1)
         Index Cond: (((macaddr)::text = 'a'::text) AND (ts = 346783556))
 Total runtime: 0.103 ms
(4 rows)


# explain analyze select count(*) from client_data where macaddr='a' and interval=56;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=56.15..56.16 rows=1 width=0) (actual time=0.021..0.022 rows=1 loops=1)
   ->  Index Scan using client_data_pkey on client_data  (cost=0.00..56.15 rows=1 width=0) (actual time=0.017..0.017 rows=0 loops=1)
         Index Cond: (((macaddr)::text = 'a'::text) AND ("interval" = 56))
 Total runtime: 0.055 ms
(4 rows)
4

1 に答える 1

2

引用した後に残りのテキストを読む必要があります。

PostgreSQL は、一番左の列を含む検索に対してのみ B ツリー インデックスを効率的に使用できます。を検索するクエリ、または と の両方を(a,b)検索するクエリにはインデックスを使用できますが、だけを検索するクエリは使用できません。これは、複数列の B ツリー インデックスが構造化されているためです。インデックスの大部分はいずれにせよスキャンする必要があるため、PostgreSQL が完全なテーブル スキャンを実行する方が効率的であることがよくあります。aabb

それらを個別の列として扱う必要があり、 で大量の検索/高速検索を行う必要がある場合はb、 で別のインデックスも作成しbます。

SET enable_seqscan = off(これをテスト目的でのみ使用すると) PostgreSQL はインデックスを左端以外の列に使用することに気付くかもしれませんが、おそらく seqscan よりも遅くなるでしょう。そうでない場合は、設定が実際random_page_costseq_page_cost一致しているかどうかを確認する必要があります。

于 2013-11-11T06:42:27.097 に答える