読み取りパフォーマンスのためのPostgreSQLの構成


39

私たちのシステムは大量のデータを書き込みます(ビッグデータシステムの一種)。書き込みパフォーマンスはニーズには十分ですが、読み取りパフォーマンスは本当に遅すぎます。

主キー(制約)構造は、すべてのテーブルで類似しています:

timestamp(Timestamp) ; index(smallint) ; key(integer).

テーブルには数百万行、さらには数十億行あり、通常、読み取り要求は特定の期間(タイムスタンプ/インデックス)とタグに対して行われます。通常、約20万行を返すクエリがあります。現在、1秒あたり約15,000行を読み取ることができますが、10倍高速にする必要があります。これは可能ですか?もし可能なら、どのように?

注: PostgreSQLはソフトウェアにパッケージ化されているため、ハードウェアはクライアントごとに異なります。

これは、テストに使用されるVMです。VMのホストは、24.0 GBのRAMを備えたWindows Server 2008 R2 x64です。

サーバー仕様(仮想マシンVMWare)

Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)

postgresql.conf 最適化

shared_buffers = 512MB (default: 32MB)
effective_cache_size = 1024MB (default: 128MB)
checkpoint_segment = 32 (default: 3)
checkpoint_completion_target = 0.9 (default: 0.5)
default_statistics_target = 1000 (default: 100)
work_mem = 100MB (default: 1MB)
maintainance_work_mem = 256MB (default: 16MB)

テーブル定義

CREATE TABLE "AnalogTransition"
(
  "KeyTag" integer NOT NULL,
  "Timestamp" timestamp with time zone NOT NULL,
  "TimestampQuality" smallint,
  "TimestampIndex" smallint NOT NULL,
  "Value" numeric,
  "Quality" boolean,
  "QualityFlags" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag" ),
  CONSTRAINT "FK_AnalogTransition_Tag" FOREIGN KEY ("KeyTag")
      REFERENCES "Tag" ("Key") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE,
  autovacuum_enabled=true
);

問い合わせ

クエリはpgAdmin3で実行するのに約30秒かかりますが、可能であれば5秒以内に同じ結果を得たいと思います。

SELECT 
    "AnalogTransition"."KeyTag", 
    "AnalogTransition"."Timestamp" AT TIME ZONE 'UTC', 
    "AnalogTransition"."TimestampQuality", 
    "AnalogTransition"."TimestampIndex", 
    "AnalogTransition"."Value", 
    "AnalogTransition"."Quality", 
    "AnalogTransition"."QualityFlags", 
    "AnalogTransition"."UpdateTimestamp"
FROM "AnalogTransition"
WHERE "AnalogTransition"."Timestamp" >= '2013-05-16 00:00:00.000' AND "AnalogTransition"."Timestamp" <= '2013-05-17 00:00:00.00' AND ("AnalogTransition"."KeyTag" = 56 OR "AnalogTransition"."KeyTag" = 57 OR "AnalogTransition"."KeyTag" = 58 OR "AnalogTransition"."KeyTag" = 59 OR "AnalogTransition"."KeyTag" = 60)
ORDER BY "AnalogTransition"."Timestamp" DESC, "AnalogTransition"."TimestampIndex" DESC
LIMIT 500000;

説明1

"Limit  (cost=0.00..125668.31 rows=500000 width=33) (actual time=2.193..3241.319 rows=500000 loops=1)"
"  Buffers: shared hit=190147"
"  ->  Index Scan Backward using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..389244.53 rows=1548698 width=33) (actual time=2.187..1893.283 rows=500000 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-16 01:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-16 15:00:00-04'::timestamp with time zone))"
"        Filter: (("KeyTag" = 56) OR ("KeyTag" = 57) OR ("KeyTag" = 58) OR ("KeyTag" = 59) OR ("KeyTag" = 60))"
"        Buffers: shared hit=190147"
"Total runtime: 3863.028 ms"

説明2

私の最新のテストでは、データを選択するのに7分かかりました!下記参照:

"Limit  (cost=0.00..313554.08 rows=250001 width=35) (actual time=0.040..410721.033 rows=250001 loops=1)"
"  ->  Index Scan using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..971400.46 rows=774511 width=35) (actual time=0.037..410088.960 rows=250001 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-22 20:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-24 20:00:00-04'::timestamp with time zone) AND ("KeyTag" = 16))"
"Total runtime: 411044.175 ms"

回答:


52

データのアライメントとストレージサイズ

実際、タプルごとのオーバーヘッドは、タプルヘッダーの場合は24バイト、アイテムポインターの場合は4バイトです。
この関連する回答の計算の詳細:

SOに関するこの関連する回答のデータのアライメントとパディングの基本:

主キーには3つの列があります。

PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag")

"Timestamp"      timestamp (8 bytes)
"TimestampIndex" smallint  (2 bytes)
"KeyTag"         integer   (4 bytes)

結果:

 ページヘッダーの4バイトのアイテムポインター(8バイトの倍数にはカウントされません)
---
タプルヘッダー用の23バイト
 データアライメント用の1バイトパディング(またはNULLビットマップ)
 8バイト「タイムスタンプ」
 2バイト「TimestampIndex」
 データアライメント用の2バイトのパディング
 4バイト「KeyTag」 
 8バイトの倍数に最も近い0パディング
-----
タプルあたり44バイト

この関連する回答のオブジェクトサイズの測定の詳細:

複数列インデックスの列の順序

次の2つの質問と回答を読んで理解してください。

インデックス(主キー)を使用する方法では、並べ替え手順なしで行を取得できますLIMIT。これは特に魅力的です。ただし、行の取得は非常にコストがかかるようです。

一般に、複数列のインデックスでは、「等価」列が最初になり、「範囲」列が最後になります。

したがって、列の順序を逆にして追加のインデックスを試してください。

CREATE INDEX analogransition_mult_idx1
   ON "AnalogTransition" ("KeyTag", "TimestampIndex", "Timestamp");

データの分布に依存します。しかし、millions of row, even billion of rowsこれにより大幅に高速化される可能性があります。

タプルサイズは、データのアライメントとパディングのために8バイト大きくなります。これをプレーンインデックスとして使用している場合、3番目の列を削除しようとする場合があります"Timestamp"。少し速くなる場合もあれば、そうでない場合もあります(ソートに役立つ場合があるため)。

両方のインデックスを保持することもできます。多くの要因に応じて、元のインデックスが望ましい場合があります-特に小さいLIMIT

自動バキュームおよびテーブル統計

テーブルの統計は最新である必要があります。autovacuumが実行されていると確信しています。

テーブルは巨大で、適切なクエリプランにとって統計が重要であると思われるため、関連する列の統計ターゲットを大幅に増やします。

ALTER TABLE "AnalogTransition" ALTER "Timestamp" SET STATISTICS 1000;

...またはさらに数十億行の場合。最大は10000、デフォルトは100です。

WHEREor ORDER BY句に含まれるすべての列に対してこれを行います。次にを実行しANALYZEます。

テーブルレイアウト

その間、データのアライメントとパディングについて学んだことを適用すると、この最適化されたテーブルレイアウトはディスク領域を節約し、パフォーマンスを少し向上させるはずです(pkとfkを無視):

CREATE TABLE "AnalogTransition"(
  "Timestamp" timestamp with time zone NOT NULL,
  "KeyTag" integer NOT NULL,
  "TimestampIndex" smallint NOT NULL,
  "TimestampQuality" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  "QualityFlags" smallint,
  "Quality" boolean,
  "Value" numeric
);

CLUSTER / pg_repack

特定のインデックスを使用するクエリの読み取りパフォーマンスを最適化するために(元のインデックスまたは私の提案する代替)、インデックスの物理的な順序でテーブルを書き換えることができます。CLUSTERそれは行いますが、かなり侵襲的であり、操作中は排他ロックが必要です。pg_repackテーブルの排他ロックなしで同じことを実行できる、より洗練された代替手段です。
これは、読み込む必要のあるテーブルのブロックがはるかに少ないため、巨大なテーブルで大幅に役立ちます。

RAM

一般に、2GBの物理RAMでは、数十億行をすばやく処理するには不十分です。より多くのRAMが大いに役立つ可能性があります-適応された設定をeffective_cache_size伴います。


2
KeyTagのみに単純なインデックスを追加しましたが、今ではかなり高速になっているようです。また、データのアライメントに関する推奨事項を適用します。どうもありがとう!
JPelletier

9

だから、私は1つのことを見る計画から:あなたのインデックスは肥大化する(そして基礎となるテーブルと一緒に)か、単にこの種のクエリには本当によくない(上記の私の最新のコメントでこれに対処しようとしました)。

インデックスの1行には14バイトのデータ(およびヘッダー用のデータ)が含まれています。プランで指定された数値から計算すると、190147ページから500,000行を取得しました。つまり、平均して1ページあたり3行未満、つまり8 kbページあたり約37バイトです。これは非常に悪い比率ですね。インデックスの最初の列はTimestampフィールドであり、クエリで範囲として使用されるため、プランナーはインデックスを選択して一致する行を見つけることができます。ただしTimestampIndexWHERE条件には言及されてKeyTagいないため、これらの値はインデックスページにランダムに表示されるため、フィルタリングはあまり効果的ではありません。

したがって、可能性の1つは、インデックス定義を

CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp", "KeyTag", "TimestampIndex")

(または、システムの負荷を考慮して、このインデックスを新しいインデックスとして作成します。

CREATE INDEX CONCURRENTLY "idx_AnalogTransition" 
    ON "AnalogTransition" ("Timestamp", "KeyTag", "TimestampIndex");
  • これには確かに時間がかかりますが、それまでの間は引き続き作業できます。)

インデックスページの大部分が無効な行で占められている可能性がありますが、それはバキューム処理によって削除できます。設定でテーブルを作成しましたが、autovacuum_enabled=true自動バキュームを開始したことがありますか?またはVACUUM手動で実行しますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.