テーブルとインデックスの肥大化を測定して対処する

テーブルとインデックスの肥大化は、VACUUM オペレーションがテーブルに対するデータ操作言語(DML)の速度に追いつかない場合に発生します。トランザクションでそのスペースを再利用できるため、テーブルとインデックスの肥大化は避けられず、一般的に許容されます。過剰な肥大化は、クエリ時間の遅延、I/O の増加、共有バッファとキャッシュの使用効率の低下、ストレージ コストの増加など、パフォーマンスの問題を引き起こします。

ブロートを推定する最善の方法は、pgstattuple モジュールを使用することです。ただし、拡張機能がリレーションのすべてのページを読み取る必要がある場合、pgstattuple の使用は効率的ではありません。pgstattuple を使用できない場合は、他のスクリプトを使用してブロートを推定できますが、この方法では精度が低下します。

pgstattuple の出力を読み取る

pgstattuple を使用してテーブルまたはインデックスの物理ストレージ特性をレポートすると、出力は次の形式になります。

> SELECT * FROM pgstattuple('large_table');
-[ RECORD 1 ]------+-----------
table_len          | 7431888896
tuple_count        | 66666666
tuple_len          | 2796296271
tuple_percent      | 37.63
dead_tuple_count   | 0
dead_tuple_len     | 0
dead_tuple_percent | 0
free_space         | 3873153652
free_percent       | 52.12

出力の説明は次のとおりです。

table_len7431888896
テーブルの合計サイズ(バイト単位)。これには、ライブ タプル、デッド タプル、空き容量がすべて含まれます。この場合、large_table は約 7,088 GB を占有します。
tuple_count66666666
テーブル内のライブタプル(表示されアクティブな行)の数を表します。このテーブルには約 6,600 万件のアクティブな行が含まれています。
tuple_len2796296271
すべてのライブタプルの合計長(バイト単位)。約 2.6 GB です。
tuple_percent37.63

ライブタプルが占める table_len の割合。次のように計算します。

tuple_len/table_len)× 100 =(2796296271/7431888896)× 100 ≈ 37.63 パーセント

この出力は、アクティブなデータがテーブルのディスク容量の 40% 未満であることを示しています。

dead_tuple_count0

デッドタプルの数。更新または削除後にバキューム オペレーションでまだ削除されていない行の数です。値 0 は、テーブルがクリーンで、再利用を待機しているデッドタプルがないことを意味します。これは多くの場合、VACUUM(自動バキューム)がテーブルで効率的に実行されていることを示しています。

dead_tuple_len0

すべてのデッドタプルの合計長(バイト単位)。dead_tuple_count と同様に、0 の値は、デッド行がスペースを消費しないことを意味します。

dead_tuple_percent0

デッドタプルが占める table_len の割合。次のように計算します。

dead_tuple_len/table_len)× 100 = (0/7431888896)× 100 = 0%

free_space3873153652

この値は、テーブルのデータページ内の空き容量を表します。この領域は、テーブルの拡張を必要とせずに、新しい挿入や更新に再利用できます。これは、約 3.7 GB の空き容量があることを示しています。

free_percent52.12

この値は、table_len の空き容量の割合を表します。計算式は次のとおりです。

free_space/table_len)× 100 =(3873153652/7431888896)× 100 ≈ 52.12 パーセント

クエリを使用して膨張を見積もる

システム カタログ テーブルに対するクエリを使用して bloat を見積もることができますが、この方法は pgstattuple などの拡張機能を使用するよりも精度が低くなります。PostgreSQL テーブルの肥大化チェックなどのスクリプトを使用する場合は、テーブルの統計情報を最新の状態にする必要があります。そうしないと、推定値が不正確になります。

クエリの出力を pgstattuple モジュールと比較すると、推定値は比較可能ですが、ブロートの正確な測定値ではありません。SQL クエリは PostgreSQL の累積統計エンジンに依存しますが、モジュールは実際のデータページを読み取るため、モジュールの方がブロートの正確な指標となります。

次の例では、large_table という名前のテーブルの PostgreSQL でテーブルの肥大化を分析する 2 つの異なるメソッドの出力を比較しています。

> SELECT * FROM pgstattuple('large_table');
-[ RECORD 1 ]------+-----------
table_len          | 2475892736
tuple_count        | 22000000
tuple_len          | 922925931
tuple_percent      | 37.28
dead_tuple_count   | 0
dead_tuple_len     | 0
dead_tuple_percent | 0
free_space         | 1293696900
free_percent       | 52.25

Vs.

-[ RECORD 1 ]+------------
databasename | postgres
schemaname   | public
tablename    | large_table
can_estimate | t
est_rows     | 21965400
pct_bloat    | 54
mb_bloat     | 1269.24
table_mb     | 2361.195

アドレスの肥大化

ブロートは次の方法で管理できます。それぞれにメリットとデメリットがあるため、慎重な計画が必要です。

  • フル掃除を実行します。このオペレーションでは、バキュームの期間中、テーブル全体がロックされ、ダウンタイムが発生します。
  • データを新しいテーブルに移動します。このメソッドを使用するには、テーブルの変更を停止して、変更が新しいリレーションに転送されないようにする必要があります。
  • pg_squeeze を使用します。この拡張機能は、オンラインでのブロートを軽減します。この拡張機能は、シャドー テーブルを作成し、すべてのデータを挿入してから、最後のステップ(肥大化したテーブルを新しく作成された肥大化していないテーブルに切り替える)まで、レプリケーション スロットを使用してテーブルの変更を維持します。次に、膨張していないテーブルを移動して膨張したテーブルを置き換えるために、短いテーブルロックをリクエストします。この方法では、オペレーションの実行中にオブジェクトの 2 倍のスペースが必要になりますが、通常のオペレーションへの影響は最小限に抑えられます。