9

私は PostgreSQL 9.2 を使用しており、IP 範囲の表があります。SQLは次のとおりです。

CREATE TABLE ips (
  id serial NOT NULL,
  begin_ip_num bigint,
  end_ip_num bigint,
  country_name character varying(255),
  CONSTRAINT ips_pkey PRIMARY KEY (id )
)

begin_ip_numと の両方に単純な B ツリー インデックスを追加しましたend_ip_num

CREATE INDEX index_ips_on_begin_ip_num ON ips (begin_ip_num);
CREATE INDEX index_ips_on_end_ip_num ON ips (end_ip_num );

使用されているクエリは次のとおりです。

SELECT ips.* FROM ips
WHERE 3065106743 BETWEEN begin_ip_num AND end_ip_num;

問題は、BETWEENクエリが のインデックスのみを使用していることbegin_ip_numです。インデックスを使用した後、 を使用して結果をフィルタリングしend_ip_numます。EXPLAIN ANALYZE結果は次のとおりです。

Index Scan using index_ips_on_begin_ip_num on ips  (cost=0.00..2173.83 rows=27136 width=76) (actual time=16.349..16.350 rows=1 loops=1)
Index Cond: (3065106743::bigint >= begin_ip_num)
Filter: (3065106743::bigint <= end_ip_num)
Rows Removed by Filter: 47596
Total runtime: 16.425 ms

begin_ip_numと の両方に複合インデックスを追加するなど、インデックスのさまざまな組み合わせを既に試しましたend_ip_num

4

4 に答える 4

28

multicolumn indexを試してください。ただし、2 番目の列の順序が逆になっています。

CREATE INDEX index_ips_begin_end_ip_num ON ips (begin_ip_num, end_ip_num DESC);

単一列のインデックスは逆方向にほぼ同じ速度でスキャンできるため、順序付けはほとんど重要ではありません。ただし、複数列のインデックスでは重要です。

私が提案するインデックスを使用すると、Postgres は最初の列をスキャンして、残りのインデックスが最初の条件を満たすアドレスを見つけることができます。次に、最初の列の各値について、最初の条件が失敗するまで、2 番目の条件を満たすすべての行を返すことができます。次に、最初の列の次の値にジャンプします。
これはまだあまり効率的ではなく、最初のインデックス列をスキャンして 2 番目の列をフィルタリングするだけで、Postgres の方が高速になる可能性があります。データ分布に大きく依存します。

いずれにせよ、CLUSTER上記の複数列インデックスを使用すると、パフォーマンスが向上します。

CLUSTER ips USING index_ips_begin_end_ip_num

このようにして、最初の条件を満たす候補が同じまたは隣接するデータ ページにパックされます。最初の列の値ごとに多くの行がある場合、パフォーマンスを大幅に向上させることができます。そうでなければほとんど効果がありません。
(この目的のためのノンブロッキング外部ツールもあります: pg_repackまたはpg_squeeze。)

また、自動バキュームANALYZEが実行され、適切に構成されていますか、またはテーブルで実行されていますか? Postgres が適切なクエリ プランを選択するには、現在の統計が必要です。

ここで本当に役立つのは、PostgreSQL 9.2 以降で利用できる列のGiST インデックスです。int8range

参考文献:

組み込みのネットワーク タイプinetまたはcidrのいずれかで IP 範囲をカバーできる場合bigintは、2 つの列を置き換えることを検討してください。または、さらに良いことに、 Andrew Gierth による追加モジュールip4rを参照してください (標準ディストリビューションには含まれていません。それに応じて、インデックス作成戦略が変更されます。

それを除けば、部分インデックスを使用した洗練された体制を使用して、dba.SEでこの関連する回答を確認できます。高度なものですが、優れたパフォーマンスを提供します。

于 2013-01-18T21:21:18.850 に答える
6

maxmind.com の無料の geop テーブルからのほぼ同一のデータセットで、まったく同じ問題が発生しました。範囲型と GiST インデックスに関する Erwin のヒントを使用して解決しました。GiST インデックスが重要でした。それがなければ、毎秒せいぜい約 3 行のクエリを実行していました。それを使用して、10 秒以内に 500000 行近くをクエリしました。アーウィンはこれを行う方法に関する詳細な手順を投稿していないので、ここに追加すると思いました...

まず、範囲型を持つ新しい列を追加する必要があります。bigint 型には int8range が必要であることに注意してください。次に、その値を適切に設定します。'[]' パラメーターは、下限と上限 (rtfm) を含む範囲にすることを示していることに注意してください。最後にインデックスを追加します。GiST インデックスがすべてのパフォーマンスの利点の源であることに注意してください。

alter table ips add column iprange int8range;
update ips set iprange=int8range(begin_ip_num, end_ip_num, '[]');
create index index_ips_on_iprange on ips using gist (iprange);

基礎を築いたので、「<@」contained-by 演算子を使用して、テーブルに対して特定のアドレスを検索できるようになりました。http://www.postgresql.org/docs/9.2/static/functions-range.htmlを参照してください

SELECT "ips".* FROM "ips" WHERE (3065106743::bigint <@ iprange);
于 2014-03-26T20:18:11.790 に答える
0

私はあなたのクエリが次のように見えると信じていWHERE [constant] BETWEEN begin_ip_num AND end_ipnumます

私の知る限り、Postgresには「AND-EQUAL」アクセスプランがないため、Erwin Brandstetterによって提案されているように、2つの列に複合インデックスを追加する必要があります。

于 2013-01-18T21:32:46.330 に答える