Postgres 10 データベースで奇妙なパフォーマンスの問題が発生しています。
私はそのようなテーブルを持っています:
CREATE TABLE articles
(
article_id bigint NOT NULL,
content jsonb NOT NULL,
-- a few other fields...
CONSTRAINT articles_pkey PRIMARY KEY (article_id)
)
このテーブルにアクセスするための Python lib (psycopg2 ドライバーを使用) があります。テーブルに対して単純なクエリを実行します。そのうちの 1 つはコンテンツの一括ダウンロードに使用され、次のようになります。
SELECT content FROM articles WHERE id BETWEEN $1 AND $2
一部の ID ではかなり高速に動作するのに対し、他の ID では 4 ~ 5 倍遅いことに気付きました。このテーブルには、ID の 2 つの主要な「カテゴリ」があることに注意してください。1 つ目は 2 億 7000 万から 5 億の範囲で、2 つ目は 100 億から 1000 3000 万の範囲です。最初の範囲では「高速」、2 番目の範囲では「低速」です。int
より大きな絶対数が からそれらを引き出すことに加えbigint
て、2 番目の範囲の値はより「まばら」です。つまり、最初の範囲の値はほとんど帰結的 (欠損値の「穴」がほとんどない) ですが、2 番目の範囲でははるかに多くの値があります。 「穴」 (平均充填率は 30%)。
他のライブラリの影響を排除するために、次のクエリを使用して合成テストを実施しました。
SELECT count(*), sum(length(content::text))
FROM storage.articles
WHERE article_id BETWEEN %s AND %s
要求されたチャックの長さを調整して、要求ごとの JSONB の要約の長さを平均的に等しくしました。
さらに、返された処理速度をcount(*)
実行時間で割って計算します。最初の範囲は 800 ~ 1050 行/秒、2 番目の範囲は 120 ~ 250 行/秒です。もう 1 つのメトリックは、1 時間あたりの処理された JSON の長さです。クエリの主な問題は JSONB データの処理であるように見えるので、どちらの場合も平均的に同じであるに違いないと思います。しかし違います: 7-10 MB/s 対 1.5-2.5 MB/s です。
そのため、問題が再現されました。絶対値では高速になりましたが (ネットワーク転送がないため)、パフォーマンスの大きな違いは持続します。両方の範囲でクエリの実行プランを比較しましたが、同等に見えます ( EXPLAIN ANALYZE
):
Aggregate (cost=8046.71..8046.72 rows=1 width=8) (actual time=2924.410..2924.411 rows=1 loops=1)
-> Index Scan using articles_pkey on articles (cost=0.57..8000.02 rows=4669 width=107) (actual time=11.482..61.258 rows=5000 loops=1)
Index Cond: ((article_id >= 461995000) AND (article_id <= 461999999))
Planning time: 1.028 ms
Execution time: 2924.650 ms
Aggregate (cost=6216.75..6216.76 rows=1 width=8) (actual time=17704.635..17704.636 rows=1 loops=1)
-> Index Scan using articles_pkey on articles (cost=0.57..6180.69 rows=3606 width=107) (actual time=0.154..563.578 rows=3059 loops=1)
Index Cond: ((article_id >= '100017010000'::bigint) AND (article_id <= '100017019999'::bigint))
Planning time: 0.404 ms
Execution time: 17704.746 ms
私が見る唯一の違いはbigint
、1回限りの変換です。
何か不足していますか?それを調査(および修正)する方法は?
更新 1:EXPLAIN
で追加BUFFERS
:
Aggregate (cost=8635.91..8635.92 rows=1 width=16) (actual time=6625.993..6625.995 rows=1 loops=1)
Buffers: shared hit=26847 read=3914
-> Index Scan using articles_pkey on articles (cost=0.57..8573.35 rows=5005 width=107) (actual time=21.649..1128.004 rows=5000 loops=1)
Index Cond: ((article_id >= 438000000) AND (article_id <= 438005000))
Buffers: shared hit=4342 read=671
Planning time: 0.393 ms
Execution time: 6626.136 ms
Aggregate (cost=5533.02..5533.03 rows=1 width=16) (actual time=33219.100..33219.102 rows=1 loops=1)
Buffers: shared hit=6568 read=7104
-> Index Scan using articles_pkey on articles (cost=0.57..5492.96 rows=3205 width=107) (actual time=22.167..12082.624 rows=2416 loops=1)
Index Cond: ((article_id >= '100021000000'::bigint) AND (article_id <= '100021010000'::bigint))
Buffers: shared hit=50 read=2378
Planning time: 0.517 ms
Execution time: 33219.218 ms
更新 2:その他の実験:
テーブル内のすべてのデータに対して上記のアプローチを活用するために、さらにいくつかのテストを実施しました。簡単なプログラムを使用して、セグメントの境界を繰り返し処理し、最終的に両方の範囲のすべてのデータを処理します。この間、EXPLAIN ANALYZE の結果を解析し、次の一連のメトリックを収集します。
- テーブルのバッファ ヒット/読み取り。
- インデックスのバッファ ヒット/読み取り。
- 行数 (から
Index Scan...
) - 実行期間
上記のメトリックに基づいて、継承されたメトリックを計算します。
- ディスク読み取り率: (インデックス読み取り + テーブル読み取り) * 8192 / 期間。
- 読み取り率: (インデックス読み取り + テーブル読み取り) / (インデックス読み取り + テーブル読み取り + インデックス ヒット + テーブル ヒット)
- データ レート: (インデックス読み取り + テーブル読み取り + インデックス ヒット + テーブル ヒット) * 8192 / 期間
ID の「密度」は「小さい」範囲と「大きい」範囲で異なるため、どちらの場合も反復ごとに約 5000 行になるようにチャンクのサイズを調整しましたが、私の実験では、チャンク サイズはそれほど重要ではないことが示されています。 .
上記の結果は、 1 番目と 2 番目の範囲全体で確認されました(したがって、ランダムにキャッシュされたデータだけが原因ではありませんでした)。
キャッシュの影響を排除するために、バッファをフラッシュして Postgres サーバーを再起動しました。
postgresql stop; sync; echo 3 > /proc/sys/vm/drop_caches; postgresql start
この後、テストを繰り返し、ほぼ同じ写真を取得しました。
前回のテストのシリーズをチャートにレンダリングし、ここに投稿しました。
これは「小さい」範囲の写真です。ディスクの読み取り速度は、テスト全体で一様に約 10 ~ 11 MB/秒です。
質問: メモリー・バッファーをフラッシュしたのに、ヒット率が非常に高い (読み取り率が低い) のはなぜですか?
これは「大きい」範囲の写真です。それは私には非常識に見えます。ほとんどの場合、ディスクの読み取り速度は約 2 MB/秒でしたが、26 ~ 30 MB/秒に跳ね上がることもありました。読み取り率も大きく異なります。
テスト中にディスクの読み取り速度を検証したiotop
ところ、その表示が私が計算したものに非常に近いことがわかりました。ずっと監視していたとは言えませんが、第2レンジで2MB/sと22MB/s、第1レンジで10MB/sのときに確認しました。htop
また、CPU がボトルネックではなく、テスト中に約 3% であることも確認しました。
この問題は、マスター サーバーとスレーブ サーバーの両方で再現可能です。私のテストはスレーブで実施されましたが、DBMS に他の負荷がかかったり、DBMS に関係のないホストでディスク アクティビティが発生したりすることはありませんでした。
私の唯一の仮定は、仮想化または何かのために、データのさまざまなフラグメントがさまざまな速度で読み取られているということですが...なぜこれらの範囲に厳密にバインドされているのですか? 2 つの異なるマシンで同じになるのはなぜですか?
何か案は?