3

カーソルを使用して、大きな postgres テーブルからレコードを取得しています。(子テーブルを使用してデータを分割する 4 億レコード。)私のカーソルは次のように定義されます。

select * from parent_table order by indexed_column

JDBC と psql の両方を使用すると、最初の数十万回の取得のパフォーマンスは一貫しています。その後、崖から落ちて元に戻りません。サーバーの CPU、メモリ、およびディスクのアクティビティはほぼ均等です。つまり、明らかな原因として目立ったシステム ベースはありません。最初はこれがネットワークの問題ではないかと疑っていましたが、別のネットワークからこれを再現しました。

psql は次のとおりです。

db@dbdev> fetch 100000 from all_persons;
Time: 13995.910 ms
db@dbdev> fetch 100000 from all_persons;
Time: 13852.955 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14037.631 ms
db@dbdev> fetch 100000 from all_persons;
Time: 13818.516 ms
db@dbdev> fetch 100000 from all_persons;
Time: 13952.260 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14257.836 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14115.941 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14375.485 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14898.741 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14086.004 ms
db@dbdev> fetch 100000 from all_persons;
Time: 59841.556 ms
db@dbdev> fetch 100000 from all_persons;
Time: 198176.211 ms
db@dbdev> fetch 100000 from all_persons;
Time: 162593.582 ms

JDBC は次のとおりです (一度に 10000 を取得します。左側の数字は、挿入されたレコードのフィルター処理されたセットの数です)。

...
536040 retrieve in 405; filtering in 28; insert in 1734
544739 retrieve in 413; filtering in 27; insert in 1713
553574 retrieve in 382; filtering in 27; insert in 1761
563167 retrieve in 348; filtering in 28; insert in 2019
572723 retrieve in 363; filtering in 27; insert in 2048
581736 retrieve in 363; filtering in 28; insert in 1784
591131 retrieve in 480; filtering in 28; insert in 1869
600260 retrieve in 377; filtering in 27; insert in 1831
608234 retrieve in 24074; filtering in 27; insert in 1566
616212 retrieve in 23711; filtering in 27; insert in 1649
624449 retrieve in 25913; filtering in 27; insert in 1587
632528 retrieve in 29981; filtering in 27; insert in 1527
641334 retrieve in 23231; filtering in 27; insert in 1728
650427 retrieve in 27883; filtering in 27; insert in 1996
659516 retrieve in 34422; filtering in 27; insert in 1774

psql のパフォーマンスは悪化しているように見えますが、JDBC のパフォーマンスは、少なくとも 100 万件のレコード (約 34k から 17k ミリ秒の間で跳ね返ります) の間、ほぼ一貫しています。

パフォーマンスが急激に低下した理由は何ですか?

(編集)作業ソリューション:

バッチ サイズ (取得/挿入) を 5000 に落とし、(親テーブルではなく) 各子テーブルに対して順番にカーソルを実行することで、これを解決しました。order by は順序付けられたインデックスに反していましたが、それが役立つように見えたので、order by もカーソルから削除しました。

私の推測では、これにより、postgres が完全なパーティションを一度にロードする最良の機会が得られると思います。

4

2 に答える 2

3

パフォーマンスについての私の推測は次のとおりです。

"indexed_column" にインデックスがあります。. . これは単なる推測です(名前に基づいています)。Postgres は、並べ替えにインデックス付きの列を使用しています。さらに、テーブルは段階的に作成されたので、テーブルの最初の 100 万行程度はすべて、データベース内の連続したページ セットにあります。

これが本当なら、次のことが起こっています。並べ替えは喜んでインデックスに移動し、必要なレコードを見つけます。ページがまだそこにない場合は、ページをメモリにロードします。ほとんどの場合、最初の 100 万行程度まではページが存在し、結果はすぐに返されます。

しかし、その後、何か悪いことが起こります。インデックスは行を指定しており、その行を含むページはメモリ内にない可能性があります。そのため、ページを取得する必要があり、多くの場合、既にキャッシュにあるページを置き換えます (フラッシュします)。つまり、各行参照には基本的にディスク I/O が必要です。

ちなみに、この状況は、特定の方法で作成されていないテーブルであっても、どのテーブルでも発生する可能性があります。ただし、順序付けされていない限り、100 万行を取得すると、キャッシュがいっぱいになる前に大量の行を取得する必要があります。

さて、どうすれば問題を解決できますか。最適な方法は、フィルタリング ロジックを、それが属するデータベースに配置することです。結局のところ、何億もの行をアプリケーションに返すことは、データベースの適切な使い方ではありません。それは私が最初に見る場所です。

何か抜本的なことをして、インデックスを削除し、実際の並べ替えを行った方が速いかどうかを確認できます。上記の説明が正しければ、より高速です。ただし、最初の行を長時間待つ必要があります。

できることの 1 つは、テーブルを再作成し、インデックス付きの列でデータを並べ替えてから、再度インデックスを作成することです (つまり、列をクラスター化インデックスに変換します)。これにより、将来的には改善されますが、そのプロセスには少し時間がかかります。

他に 2 つの可能性があります。テーブル内の列のサブセットのみが必要であると仮定して、一時テーブルを作成し、そこからデータを取得します。4 億レコードの場合、これにはしばらく時間がかかりますが、必要なフィールドが元のレコードに比べて小さい場合は、パフォーマンスが向上します。

次に、特定の順序でデータが必要ない場合は、順序を指定せずにレコードを取得します。これにより、インデックス スキャンが完全なテーブル スキャンに置き換えられ、ページのスラッシングがなくなります。

于 2012-07-28T17:07:39.833 に答える
0

私はゴードンのポイントを2番目に挙げ、キャッシュミスに関する自分の経験を追加します。基本的に何が起こるかというと、ある時点でキャッシュ内のデータが期限切れになり、突然大量のディスクI/Oが発生します。

私は顧客のために一括支払いのストアドプロシージャを実行しました。小さな支払いバッチでは完全に機能しましたが、大きなバッチでは窒息しました。いくつかのテストの後、次のことに気づきました。

最大数百の請求書が支払われたとき、それは非常にうまく機能するでしょう。ベンダーに200〜500の請求書が支払われると、速度が低下し始め、ベンダーに1000以上の請求書が支払われると、ハングしているように見えます。私は、コードを調べてすぐにキャッシュミスを提案したPostgreSQLの第一人者と話をしました。これを念頭に置いてコードを書き直し、パフォーマンスは許容範囲内になりました。

あなたの場合に私がお勧めするのは、アプリケーションの観点から何を取得する必要があるかを正確に把握し、そのクエリ内で可能な限り実行するためのクエリを作成することです。あなたが何をしているのか正確にはわからないので、並べ替えで十分かどうかはわかりません。ただし、HAVING句がこのようなパフォーマンスの問題を解決するのを見たことがあるので、並べ替えだけでなく集計も検討する価値があります。また、最初のクエリで読み取りと書き込みが組み合わされているかどうかも確認する価値があります(フィールドリストの関数呼び出しなど)。これは常にパフォーマンスキラーであり、キャッシュミスを非常に苦痛にします(これは実際、上記の私の学習経験における大きな問題でした)。

一般に、PostgreSQLでの私の経験では、アプリケーションが必要とする情報を正確に取得し、設定された処理ロジックの多くがデータベースにプッシュされると、パフォーマンスは常に最高になります。

于 2012-08-15T02:47:46.223 に答える