PHP と postgres バックエンド (9.1) で動作する Web アプリケーションがあります。
重い DB リフティング作業のほとんどは、postgres ストアド プロシージャを介して行われます。
アプリ内のプロセスの 1 つは、データのインポート ルーチンです。ストアド プロシージャはインポートに非常に集中していますが、開発中にテスト シート (約 20 行のデータ) を約 15 秒でインポートできます。
これは、ローカル デスクトップの 4 コアの ubuntu VM で、デフォルトの postgres 構成 (VM に 1GB の RAM が割り当てられている) で実行されます。私のCPUはIntel i7です。
ローカル マシンで pg_top を使用しましたが、SELECT プロセスは約 60% の CPU 使用率で急増し、15 秒以内に終了します。
そのため、アプリを 1and1 のプロフェッショナル サーバーであるライブ環境にデプロイしました。32 コア、64 GB の RAM、2 TB のハード ドライブ。非常に高価で非常に大きな数です!
現在、ライブ サーバーで同じインポート ルーチンを実行すると 6 分以上かかり、postgres SELECT ステートメントを 100% CPU で実行すると約 6 分かかります。
私はpostgres conf設定の多くを経験し、より高性能なボックスに一致するようにメモリ番号を増やしましたが、何を変更しても、非常に低いパフォーマンスに影響を与えることはないようです.
クエリのパフォーマンスが大幅に低下する理由を知っている人はいますか?
1GB RAM の 4 コア VM でわずか 15 秒
ただし、64 GB の RAM を搭載した 32 コアの専用サーバーでは 6 分
線に沿って何かが明らかに台無しになっていますが、それが何であるかはわかりません:(
編集:
わかりました、これは私が問題を特定したと思うクエリです(小さなデータセットでは10ミリ秒ではなく、大きなデータセットでは50/60ミリ秒かかります)
EXPLAIN UPDATE artwork_entity SET "updated_on"=NOW(), "category"='blah', "category:oid"=47425
WHERE artwork_entity."id" IN (
SELECT n."id" FROM (
SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425 OR e."id" IN
(SELECT l."descendant_id" FROM artwork_relation l
LEFT JOIN artwork_entity e1 ON l."descendant_id"=e1."id"
WHERE l."depth">0 AND l."ancestor_id"=47425
AND (e1."category:oid"=(SELECT e2."category:oid" FROM artwork_entity e2 WHERE e2."id"=l."ancestor_id") OR e1."category:oid" IS NULL))
) AS n);
Update on artwork_entity (cost=3864.35..7743.46 rows=21118 width=451)"
-> Hash Semi Join (cost=3864.35..7743.46 rows=21118 width=451)"
Hash Cond: (artwork_entity.id = e.id)"
-> Seq Scan on artwork_entity (cost=0.00..3364.36 rows=42236 width=445)"
-> Hash (cost=3600.38..3600.38 rows=21118 width=10)"
-> Seq Scan on artwork_entity e (cost=24.84..3600.38 rows=21118 width=10)"
Filter: ((id = 47425) OR (hashed SubPlan 2))"
SubPlan 2"
-> Nested Loop Left Join (cost=0.00..24.83 rows=1 width=4)"
Filter: ((e1."category:oid" = (SubPlan 1)) OR (e1."category:oid" IS NULL))"
-> Index Scan using artwork_relation_ancestor_id_descendant_id_key on artwork_relation l (cost=0.00..8.28 rows=1 width=8)"
Index Cond: (ancestor_id = 47425)"
Filter: (depth > 0)"
-> Index Scan using artwork_entity_pkey on artwork_entity e1 (cost=0.00..8.27 rows=1 width=8)"
Index Cond: (l.descendant_id = id)"
SubPlan 1"
-> Index Scan using artwork_entity_pkey on artwork_entity e2 (cost=0.00..8.27 rows=1 width=4)"
Index Cond: (id = l.ancestor_id)"
また、このクエリは、どの列にもインデックスを追加せずに実行されました。
また、内部の select ステートメントは、大規模なデータセットで実行するのに約 10/20 ミリ秒しかかからないことに注意してください (したがって、更新に違いありませんか?) 利用可能な大量の行のうち 2 行のみを更新しています。
編集2:
EXPLAIN SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425 OR e."id" IN
(
SELECT l."descendant_id" FROM artwork_relation l
LEFT JOIN artwork_entity e1 ON l."descendant_id"=e1."id"
WHERE l."depth">0 AND l."ancestor_id"=47425
AND (e1."category:oid"=(SELECT e2."category:oid" FROM artwork_entity e2 WHERE e2."id"=l."ancestor_id") OR e1."category:oid" IS NULL)
)
次に、シーケンス スキャン用に 21k 行の取得を試みます。
しかし、それを次のように 2 つの個別のクエリに分割すると、次のようになります。
EXPLAIN SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425
これは1行のみを取得し、次にクエリの他の部分を取得します
EXPLAIN SELECT l."descendant_id" FROM artwork_relation l
LEFT JOIN artwork_entity e1 ON l."descendant_id"=e1."id"
WHERE l."depth">0 AND l."ancestor_id"=47425
AND (e1."category:oid"=(SELECT e2."category:oid" FROM artwork_entity e2 WHERE e2."id"=l."ancestor_id") OR e1."category:oid" IS NULL)
また、1 行しか取得しませんが、2 番目のクエリが in の一部である場合、21k 行すべてを取得しようとします。
どうして?
編集3:
初期スキャンで 21,000 行を返すステートメントを次のように簡素化しました。
EXPLAIN SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425 OR e."id" IN
(
SELECT l."descendant_id" FROM artwork_relation l
WHERE l."depth">0 AND l."ancestor_id"=47425
)
別々に実行すると、どちらも単一の行が返されますが、一緒に追加すると、データセット全体がクエリされます。