6

私は常に更新される配列を含むpostgresqlのテーブルを持っています。私のアプリケーションでPostgreSQLのカウントクエリの最適化

私は特定のパラメータは、その配列の列に存在されていない行数を取得する必要があります。私のクエリは次のようになります。

select count(id) 
from table 
where not (ARRAY['parameter value'] <@ table.array_column) 

しかし、行の量とそのクエリの実行の量を増やしたときに(おそらく毎秒数回、数百または数千)の性能は、多くのことをデクリメントし、それはように私には思えますpostgresqlのカウントには線形の実行順序があるかもしれません(私は完全にはわかりません)。

基本的に私の質問は:

は、私は、このような状況に適用されるのを認識していないよ、既存のパターンがありますか?このための最良の方法は何でしょうか?

あなたは私を与えることができる任意の提案は本当にいただければ幸いです。

+0

わかりませんが、table.array_columnのGINインデックスはこれをスピードアップするのに役立ちます。 EXPLAINを実行して調べる必要があります。ここをクリックしてください:http://dba.stackexchange.com/a/27505/1822 –

+1

テーブルが大きくなると、これをポストグルで効率的にするのは難しいでしょう。ジンインデックスは、あなたの述語の「含まれていない」とは対照的に、「含む」のためにテストするときに役立ちます。カウントが100%正確であることが決定的に重要でない場合は、TTLを使ってアプリ層でキャッシングを試みることができます。テーブルの書き込み率が高すぎない場合、現在のカウントを含む別のテーブルを更新するためにトリガーを合理的に使用することができます。 – dbenhur

+0

あなたのバージョンと 'explain analyze'を見せてください。 http://stackoverflow.com/tags/postgresql-performance/infoを参照してください。 –

答えて

2

このパターンには、この の状況に該当することが分かりませんか?このための最良の方法は何でしょうか?

この状況では、スキーマを正規化することが最善の方法です。配列をテーブルに分割します。プロパティのテーブルにbツリーインデックスを追加するか、プライマリキーを注文して効率的に検索できるようにします(property_id)。

CREATE TABLE demo(id integer primary key); 
INSERT INTO demo (id) SELECT id FROM arrtable; 
CREATE TABLE properties (
    demo_id integer not null references demo(id), 
    property integer not null, 
    primary key (demo_id, property) 
); 
CREATE INDEX properties_property_idx ON properties(property); 

その後、プロパティを照会することができます

SELECT count(id) 
FROM demo 
WHERE NOT EXISTS (
    SELECT 1 FROM properties WHERE demo.id = properties.demo_id AND property = 1 
) 

私は、これははるかに高速、元のクエリよりなることが期待が、それは実際にはほとんど同じ同じサンプルデータを持つのです。元のクエリと同じ2〜3秒の範囲で実行されます。 ではなく、の検索では、の検索よりもはるかに遅いのと同じ問題です。です。プロパティを含む行を探している場合は、seqscanがdemoにならないようにして、一致するIDに直接propertiesをスキャンするだけです。

この場合も、アレイを含むテーブルのseqスキャンでも同様にジョブが実行されます。

+0

その詳細な説明に感謝しています。私の現在の状況では、逐次カウントを行う方が良いか、検索を高速化するために情報を格納する別の方法を考えるのが良いです、これは本当に便利です – jeruki

2

私はあなたが運の外にある現在のデータモデルを考えます。データベースがあなたのクエリに対して実行しなければならないアルゴリズムを考えてみてください。逐次データをスキャンすることなく動作する方法はありません。それはデータの逆を格納するように(クエリがselect count(id) from table where ARRAY[‘parameter value’] <@ table.array_columnになるように)

あなたは、列を手配することはできますか?このクエリは、gin/gistインデックスを使用します。

5

PostgreSQLは実際には配列の列に対するGINインデックスをサポートしています。残念ながら、NOT ARRAY[...] <@ indexed_colでは使用できないようで、GINのインデックスは頻繁に更新されるテーブルには不適切です。

デモ:

CREATE TABLE arrtable (id integer primary key, array_column integer[]); 

INSERT INTO arrtable(1, ARRAY[1,2,3,4]); 

CREATE INDEX arrtable_arraycolumn_gin_arr_idx 
ON arrtable USING GIN(array_column); 

-- Use the following *only* for testing whether Pg can use an index 
-- Do not use it in production. 
SET enable_seqscan = off; 

explain (buffers, analyze) select count(id) 
from arrtable 
where not (ARRAY[1] <@ arrtable.array_column); 

残念ながら、これは書かれたとして、我々は、インデックスを使用しないことを示しています。あなたが条件を否定していない場合は、検索して(NOTを取り除くことによって)検索要素を含んでいない行を数えることができますので、それを使用することができます。

doに目標値が含まれているエントリをカウントするためにインデックスを使用して、その結果をすべてのエントリの数から差し引くことができます。 countテーブル内のすべての行がPostgreSQL(9.1以前)でかなり遅く、順次スキャンが必要な場合、実際には現在のクエリより遅くなります。それはより悪い行うことが保証されます

SELECT (
    SELECT count(id) FROM arrtable 
) - (
    SELECT count(id) FROM arrtable 
    WHERE (ARRAY[1] <@ arrtable.array_column) 
); 

:あなたは、これは実際にOKかもしれない、その場合にはidにB-treeインデックスを、持っている場合は9.2に索引のみのスキャンが行をカウントするために使用することができている可能性があります元のバージョンのseqscanに加えて、もあります。には、GINインデックススキャンが必要です。私は今9.2でこれをテストしましたが、カウントにインデックスを使用するように見えるので、9.2を調べる価値があります。このようなGINインデックスは、LOTを更新を遅くし、最初の場所に作成することは非常に遅いということ

drop index arrtable_arraycolumn_gin_arr_idx ; 
truncate table arrtable; 
insert into arrtable (id, array_column) 
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s; 
CREATE INDEX arrtable_arraycolumn_gin_arr_idx 
ON arrtable USING GIN(array_column); 

注:いくつかのあまり些細なダミーデータ付き。あなたのテーブルのように、まったく更新されるテーブルには適していません。

さらに悪いことに、このインデックスを使用したクエリでは、元のクエリの最大2倍の時間がかかり、最高で同じデータセットのの半分になります。インデックスが非常に選択的でない場合は、元のクエリではARRAY[1] - 4sと2sのように最悪です。索引が非常に選択的である場合(すなわち、一致しない場合は、ARRAY[199]など)、それは約1.2秒で実行され、元の3秒と比較して実行されます。このインデックスは、単にこのクエリでは意味がありません。

ここのレッスンは?場合によっては、正しい答えは、順次スキャンを行うことです。それ以来

はあなたのヒット率のために行う、のいずれか@debenhurが示唆するように、トリガーを使用したマテリアライズド・ビューを維持する、またはエントリがないはそうあなたが持っているパラメータのリストであることを、配列を反転しようとはしません@maniekが示唆するようにGiSTインデックスを使用できます。

関連する問題