webdevqa.jp.net

クエリ速度の向上:大きなpostgresテーブルでの単純なSELECT

PostgresデータベースでのSELECTクエリの速度に関して問題があります。

キーとして2つの整数列を持つテーブルがあります:(int1、int2)このテーブルには約7000万行があります。

この環境で2種類の単純なSELECTクエリを作成する必要があります。

SELECT * FROM table WHERE int1=X;
SELECT * FROM table WHERE int2=X;

これらの2つの選択は、これらの7000万行のうち約10.000行を返します。これを可能な限り高速に動作させるために、各列に1つずつ、2つのHASHインデックスを使用することを考えました。残念ながら、結果はそれほど良くありません:

                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on lec_sim  (cost=232.21..25054.38 rows=6565 width=36) (actual time=14.759..23339.545 rows=7871 loops=1)
   Recheck Cond: (lec2_id = 11782)
   ->  Bitmap Index Scan on lec_sim_lec2_hash_ind  (cost=0.00..230.56 rows=6565 width=0) (actual time=13.495..13.495 rows=7871 loops=1)
         Index Cond: (lec2_id = 11782)
 Total runtime: 23342.534 ms
(5 rows)

これは、これらのクエリの1つのEXPLAIN ANALYZEの例です。約23秒かかります。私の期待は、この情報を1秒以内に取得することです。

これらは、postgres db configのいくつかのパラメーターです。

work_mem = 128MB
shared_buffers = 2GB
maintenance_work_mem = 512MB
fsync = off
synchronous_commit = off
effective_cache_size = 4GB

どんな助け、コメントまたは考えでも本当に感謝されるでしょう。

前もって感謝します。

27
alexdemartos

コメントを回答に抽出します。ここでのインデックスルックアップは非常に高速で、実際の行の取得にすべての時間が費やされました。 23秒/ 7871行=行あたり2.9ミリ秒。これは、ディスクサブシステム全体に散らばっているデータを取得するのに妥当です。シークが遅い。 a)データセットをRAMに収める、b)SSDを購入する、c)シークを最小限に抑えるためにデータを事前に整理することができます。

PostgreSQL 9.2には index-only scans という機能があり、テーブルにアクセスせずに(通常)クエリに答えることができます。これを自動的に順序を維持するbtreeインデックスプロパティと組み合わせて、このクエリを高速化できます。 int1int2、および2つのフロート:

CREATE INDEX sometable_int1_floats_key ON sometable (int1, float1, float2);
CREATE INDEX sometable_int2_floats_key ON sometable (int2, float1, float2);

SELECT float1,float2 FROM sometable WHERE int1=<value>; -- uses int1 index
SELECT float1,float2 FROM sometable WHERE int2=<value>; -- uses int2 index

また、これはディスクのシークを魔法のように消去するのではなく、クエリ時間から挿入時間に移動するだけです。また、データを複製しているため、ストレージ容量がかかります。それでも、これはおそらくあなたが望むトレードオフです。

33
willglynn

ウィルグリンありがとうございます。お気づきのように、問題はHDを介したシークであり、インデックスを検索しないことでした。 RAMにデータセットをロードするか、SSD HDを購入するなど、多くのソリューションを提案しました。しかし、データベース自体の外部を管理するこれら2つを忘れて、2つのアイデアを提案しました。

  1. データを再編成して、データのシークを減らします。
  2. PostgreSQL 9.2の機能「インデックスのみのスキャン」を使用する

私はPostgreSQL 9.1サーバーの下にいるので、オプション「1」を取ることにしました。

テーブルのコピーを作成しました。そのため、同じテーブルに2つの同じデータがあります。各インデックスを作成しました。最初のインデックスは(int1)でインデックス付けされ、2番目のインデックスは(int2)でインデックス付けされています。次に、それぞれのインデックスを使用して、両方(CLUSTERテーブルUSING ind_intX)をクラスター化しました。

これらのクラスター化されたテーブルの1つで行われた同じクエリのEXPLAIN ANALYZEを投稿しています。

クエリプラン
 -------------------------------------- -------------------------------------------------- -------------------------------------------------- --- [。 :(lec2_id = 12300)合計ランタイム:
 1.822 ms(3行)

現在、シークは非常に高速です。 23秒から2ミリ秒に短縮されましたが、これは目覚しい改善です。私はこの問題が解決されたと思う、これは同じ問題を経験している他の人にも役立つかもしれないと思います。

ウィルグリンありがとうございます。

20
alexdemartos

超低速のクエリの場合、単純な1対多の結合(PG v9.1)が、3,300万行のテーブルと24億行の子テーブルの間で実行されました。子テーブルの外部キーインデックスに対してCLUSTERを実行しましたが、これはクエリタイムアウトの問題を解決するものではなく、最も単純なクエリでも解決できないことがわかりました。 ANALYZEを実行しても問題は解決しませんでした。

大きな違いをもたらしたのは、親テーブルと子テーブルの両方で手動のVACUUMを実行したことです。親テーブルがVACUUMプロセスを完了していても、10分のタイムアウトから1秒で結果が返されるようになりました。

私がこれから引き離しているのは、v9.1の場合でも、通常のVACUUM操作が依然として重要であることです。これを行った理由は、少なくとも2週間はautovacuumがどちらのテーブルでも実行されておらず、それ以降、多くのアップサートと挿入が発生したことに気づいたからです。今後この問題に対処するために自動バキュームトリガーを改善する必要があるかもしれませんが、数十億行の640GBテーブルはすべてがクリーンアップされればうまく機能するということです。良いパフォーマンスを得るためにテーブルを分割する必要はまだありません。

3
Robert Casey

非常にシンプルで効果的な1つのライナーの場合、postgresマシンに高速のソリッドステートストレージがある場合は、以下を設定してみてください。

random_page_cost=1.0

あなたのpostgresql.conf

デフォルトはrandom_page_cost=4.0これは、古い回転ディスクのようにシーク時間が長いストレージ向けに最適化されています。これにより、シークのコスト計算が変更され、メモリへの依存度が低くなります(最終的にはスワップになります)。

この設定だけで、数百万件のレコードがある長いテーブルでのフィルタリングクエリが8秒から2秒に改善されました。

もう1つの大きな改善点は、テーブルのブール列すべてを使用してインデックスを作成したことです。これにより、2秒のクエリが約1秒に短縮されました。 @willglynnの答えを確認してください。

お役に立てれば!

0
Nick Woodhams