Open9

postgres, wal

がまがま

WAL

負荷テスト時、書き込み処理が著しく劣化した原因について調査する

がまがま
  1. vg1-swap、vg1-root に負荷がかかっている形跡
  2. free disk space の占有
  3. postgres.log に大量の checkpoint 作成のログ
    その際に、write処理時間が著しく劣化(?)
がまがま

WAL とは?

WAL (Write-Ahead Logging) は、データベース管理システム(DBMS)における信頼性とデータ保護を目的とした技術です。WAL は以下の仕組みで動作します。

  1. トランザクションの順序性の確保: データを直接書き換える前に、変更内容をログファイル(WALファイル)に記録します。このログは、ディスクへの書き込みが行われる前に、まず安全に保管されます。

  2. 障害回復のサポート: サーバがクラッシュしたり障害が発生しても、WAL に記録されたログを元に、未完了のトランザクションを再適用したり、完了済みのトランザクションを巻き戻すことができます。

  3. データの整合性維持: WAL によって、データベースのトランザクションが確実に安全に行われるよう保証されます。すべての変更がログに先に記録されることで、後から復元や再実行が可能になります。

PostgreSQL の場合

PostgreSQL などのデータベースでは、WAL はデータの信頼性と復旧を支える重要な機能です。データベースのパフォーマンスにも影響しますが、耐障害性を高めるための基本的な技術となっています。

WAL の主な利点は、次のような場面でデータを安全に守る点です。

  • システムクラッシュ時の復旧
  • レプリケーションやバックアップとの統合

WAL は基本的に、データの変更がディスクに反映される前にログに記録するという、信頼性の高い方法です。

がまがま

pg_walのばしょかくにん

SHOW data_directory;
# '/var/lib/pgsql/data'

サイズ上限の設定値

SHOW wal_keep_size;
# 0

wal_keep_size (integer)

ストリーミングレプリケーションにおいて、スタンバイサーバが過去のファイルセグメントを取得する必要がある場合に備え、pg_walディレクトリに保持しておくファイルセグメントの最小サイズを指定します。 もし送出サーバに接続しているスタンバイサーバがwal_keep_sizeメガバイトを越えて遅延した場合、送出サーバはスタンバイサーバが今後とも必要とするWALセグメントを削除する可能性があります。 この場合、レプリケーション接続は終了させられます。結果として下流に対する接続も結局は終了されることがあります。(しかし、WALアーカイブが使用されていれば、スタンバイサーバはアーカイブからセグメントを取り出し、復旧することができます。)

pg_walに保持され続けるセグメントの最小値のみを設定します。 システムはWALアーカイブのため、またはチェックポイントからの復旧のため、より多くのセグメント保持が必要となることがあります。 もしwal_keep_sizeが(デフォルトの)ゼロの場合、システムはスタンバイサーバのために追加セグメントを保持することはしません。 従って、スタンバイサーバが使用できる古いWALセグメントの数は、直前のチェックポイントの場所とWALアーカイブの状況によって算出されます。 この値が単位無しで指定されると、メガバイトであると見なします。 このパラメータは、postgresql.confファイル、もしくはサーバコマンドラインでのみ設定可能です。

がまがま

めも

wal_buffersに関する解説

wal_buffersは、PostgreSQLのWAL(Write-Ahead Logging)データがメモリ上に一時的に保存されるバッファで、ディスクへの書き込みが効率的に行われるように最適化されています。wal_buffersに関連する気になる点について、以下で詳細に解説します。


1. トランザクション内での書き込みがwal_buffersのサイズを超えた場合のフラッシュについて

wal_buffersのサイズを超えるデータがトランザクション内で書き込まれた場合、WALデータは自動的にディスクにフラッシュされます。具体的には、以下のような動作になります。

  • WALの自動フラッシュ: wal_buffersが満杯になると、そのバッファに書き込まれたWALデータはディスクに書き出され、バッファが再利用されます。この動作は、WALバッファが溢れるたびに繰り返されます。

  • トランザクションの進行中: トランザクション内で何度もwal_buffersの容量を超える書き込みが発生する場合、その都度ディスクへのフラッシュが発生します。フラッシュが発生するタイミングはトランザクションのCOMMITに依存せず、wal_buffersが超えた時点で即座に行われます。

  • パフォーマンスへの影響: フラッシュが頻繁に発生する場合、ディスクI/Oに負荷がかかり、特に大量のデータを短期間で書き込むトランザクションでは、パフォーマンスの低下が懸念されます。

  • ディスクについての補足: この「ディスク」は、WALファイルの保存先を指します。WALデータは、データベースの永続化先のディスクとは別の場所に保存されることが一般的です。wal_buffersが溢れると、WALファイルのディスクにフラッシュされます。


2. wal_buffersは別トランザクションと共通か?

はい、wal_buffersはシステム全体で共通のバッファです。つまり、複数のトランザクションが同時に実行されている場合でも、それらは同じwal_buffersを共有します。

  • マルチトランザクションの処理: 複数のトランザクションからのWALデータが同じバッファに書き込まれるため、大量のトランザクションが同時に発生する場合、wal_buffersが溢れる可能性が高くなります。これは、フラッシュ頻度が増加し、ディスクI/Oが増える結果を招きます。

  • 共有バッファのメリットとデメリット:

    • メリット: 複数のトランザクションが同じwal_buffersを共有することで、ディスクへの書き込みが効率的に行われ、システム全体のパフォーマンスが向上します。
    • デメリット: トランザクションが急増し、wal_buffersが頻繁に溢れる場合、ディスクI/Oがボトルネックとなり、パフォーマンスの低下が懸念されます。

3. wal_buffersが溢れた場合の挙動と長期間の超過状態の懸念

wal_buffersが溢れた場合や、複数のトランザクションが大量のデータをINSERTUPDATEし続け、長時間にわたってwal_buffersが超過し続ける状況についての懸念を以下に説明します。

  • wal_buffersの超過時の挙動:

    • wal_buffersが溢れた場合、WALデータは即座にディスクにフラッシュされます。このフラッシュはトランザクションのCOMMITを待たずに実行されるため、バッファの超過が発生するたびにディスクI/Oが発生します。

    • フラッシュ頻度の増加に伴い、ディスクへの書き込みが頻発し、特にディスクI/Oが遅いストレージを使用している場合に、パフォーマンスが低下する可能性があります。

  • 長時間のwal_buffers超過状態の懸念:

    • ディスクI/O負荷の増加: 長時間にわたりwal_buffersが頻繁に溢れ、ディスクへの書き込みが継続すると、ディスクI/Oが過負荷になり、全体のクエリ処理が遅延する可能性があります。特に、SSDではなくHDDなどのストレージを使用している場合、この影響が顕著に現れる可能性があります。

    • パフォーマンスの低下: ディスクI/Oに負荷がかかることで、クエリの応答時間が長くなり、特に書き込み操作が多いワークロードでは、アプリケーション全体のパフォーマンスに影響が出る可能性があります。WALデータが継続的にフラッシュされることで、CPU負荷やメモリの使用量も増加することがあります。

    • 他のプロセスへの影響: ディスクI/OがWALフラッシュに専有されると、他のプロセス(データの読み込みや他のバックグラウンドタスクなど)が遅延する可能性があります。これは、特に書き込み操作が集中しているシステムでは問題となります。


改善策

長時間にわたってwal_buffersが超過し続ける場合、以下のような対策が効果的です。

  1. wal_buffersのサイズを増やす:

    • wal_buffersのデフォルトサイズは一般的に小さめに設定されています。大量のトランザクションや書き込みが頻繁に発生する環境では、wal_buffersを増やすことでフラッシュ頻度を減らし、ディスクI/O負荷を軽減できます。サイズはシステムのメモリに応じて適切に設定する必要があります。
  2. ディスクI/Oの最適化:

    • 高速なディスク(SSDやNVMe)を使用することで、WALのフラッシュ時にかかる時間を短縮し、全体的なパフォーマンスを向上させることができます。
  3. synchronous_commitの設定変更:

    • 書き込み処理のパフォーマンスを改善するために、synchronous_commitoffに設定することで、WALのディスクフラッシュを非同期にし、COMMIT時のディスクI/O負荷を軽減することができます。ただし、これにより障害発生時のデータ損失リスクが増加するため、要件に応じて調整する必要があります。

まとめ

  • WALバッファの超過時: wal_buffersが溢れた場合、WALデータはディスクに自動的にフラッシュされます。トランザクションが続いている間でもフラッシュは発生するため、バッファが満杯になるたびにディスクI/Oが発生します。

  • 共有バッファ: wal_buffersは複数のトランザクションで共有されているため、同時に大量のトランザクションが発生すると、バッファが頻繁に超過しやすくなります。

  • 長時間の超過時の懸念: wal_buffersが超過し続けると、ディスクI/Oに負荷がかかり、システム全体のパフォーマンスに悪影響を及ぼす可能性があります。wal_buffersのサイズ調整やディスク性能の向上が必要です。

  • ディスクについての補足: この「ディスク」は、WALファイルの保存先を指し、データベースの永続化先のディスクとは別の場所にWALデータが保存されます。

がまがま

めも

トランザクションからWAL、チェックポイントまでの時系列(CPU負荷の観点を含む)

トランザクション開始からクエリ実行、WALの書き込み、そしてチェックポイントまでの流れを時系列に沿って整理し、それぞれの操作がどのタイミングで行われるか、またWALに対する負荷や影響、さらにCPU負荷の観点についても解説します。


1. トランザクション開始(BEGIN

  • 操作: トランザクションが開始される(BEGIN)。
  • WALとの関係: この時点では、WALには何も記録されません。トランザクション内で行われる変更が、WALに影響を与えます。
  • 設定値に依存する項目: 特に関連する設定値はありません。
  • 負荷:
    • CPU負荷: トランザクション開始時点ではCPU負荷はほぼ発生しません。トランザクション開始時にメモリリソースが確保される程度です。
    • ディスクI/O負荷: この時点ではほぼ負荷はありません。

2. クエリ実行(INSERTUPDATEDELETEなど)

  • 操作: データの変更が実行されます。クエリが実行されると、変更内容がメモリ上に反映されます。
  • WALとの関係:
    • WAL書き込み: クエリが実行され、データの変更があると、即座に変更内容がWALに書き込まれます(まずはwal_buffersに書き込まれる)。この書き込みは、トランザクションがCOMMITされるかROLLBACKされるかにかかわらず実行されます。つまり、クエリ実行時にWALへの書き込みはCOMMITとは無関係です。
    • WALに書き込まれた変更内容は、トランザクションがCOMMITされれば有効化され、ROLLBACKされれば無効化されますが、いずれの場合でもWALには書き込まれます。
  • 設定値に依存する項目:
    • wal_buffers: WALのバッファサイズ。この値が小さいと、ディスクへのフラッシュが頻繁になりパフォーマンスに悪影響が出る可能性があります。
  • 負荷:
    • CPU負荷: クエリの実行による計算処理やデータの変更に伴い、CPU負荷が発生します。特に、インデックスの更新やテーブルのスキャンが行われる場合にCPU負荷が増加します。
    • ディスクI/O負荷: WALバッファに書き込まれたデータがディスクにフラッシュされる場合、ディスクI/Oが発生します。特に、大量のデータを書き込むトランザクションでは、この負荷が顕著になる可能性があります。

3. WALのフラッシュ

  • 操作: WALバッファに書き込まれたデータが、特定のタイミングでディスクにフラッシュされます。fsyncを用いてディスクに確実に書き込むことで、障害時のデータ消失を防ぎます。
  • 設定値に依存する項目:
    • synchronous_commit: トランザクションのCOMMITの際、WALがディスクにフラッシュされるかどうかを制御します。onに設定されている場合、COMMITはWALがディスクに書き込まれるまで待機します。offの場合、フラッシュを待たずに即座にCOMMITが完了します。
    • fsync: WALのフラッシュ処理時にデータをディスクに同期させる設定。offにすると、書き込みは高速化しますが、障害時にデータの損失リスクがあります。
  • 負荷:
    • CPU負荷: フラッシュ処理自体は主にディスクI/Oに依存するため、CPU負荷は比較的少ないですが、大量のデータが処理される場合に負荷が増えることがあります。
    • ディスクI/O負荷: WALバッファからディスクへの書き込みは、ディスクI/Oを消費します。特にfsync操作によるフラッシュが頻繁に発生すると、ディスクI/Oの負荷が高まります。

4. トランザクションのCOMMIT

  • 操作: トランザクションが終了し、データの変更が確定されます(COMMIT)。
  • WALとの関係: COMMIT時に、WALに書き込まれたトランザクションの変更内容がディスクにフラッシュされます。これは、トランザクションの確定に不可欠なプロセスであり、ここでWALの同期が完了していないと、障害時にデータが失われる可能性があります。
  • 設定値に依存する項目:
    • synchronous_commit: COMMIT時にWALがディスクにフラッシュされるタイミングを制御します。onの場合、WALが確実にディスクに書き込まれるまでCOMMITが完了しません。offにすると、ディスクに書き込まれる前にCOMMITが終了し、パフォーマンスは向上しますがデータ損失のリスクが高まります。
  • 負荷:
    • CPU負荷: COMMITの処理はCPUに大きな負荷をかけませんが、大量のトランザクションが同時にCOMMITされると、負荷が高くなる可能性があります。
    • ディスクI/O負荷: COMMITが完了するまでの間にWALのフラッシュが必要になるため、ディスクI/Oに負荷がかかります。

5. トランザクションのROLLBACK

  • 操作: トランザクションが取り消され、変更内容が破棄されます(ROLLBACK)。
  • WALとの関係:
    • WALの挙動: ROLLBACKされた場合でも、WALにはクエリ実行時に書き込まれた変更が記録されています。しかし、ROLLBACK時にPostgreSQLはその変更を適用せず、実際のデータファイルへの反映は行われません。つまり、WALに記録されたデータが無効化され、トランザクションが取り消されたという記録だけが残ります。
  • 設定値に依存する項目: 特に影響する設定はありません。
  • 負荷:
    • CPU負荷: ROLLBACKそのものの処理には比較的低いCPU負荷しかかかりませんが、トランザクションが大規模である場合、無効化される操作が多いほど負荷が増える可能性があります。
    • ディスクI/O負荷: ROLLBACKされた場合でもWALは書き込まれるため、ディスクI/Oの負荷が発生する場合があります。ただし、実際のデータファイルには反映されないため、負荷は低めです。

6. WALのアーカイブ(任意)

  • 操作: WALのアーカイブが有効な場合、一定量のWALデータがディスクに書き込まれた後、アーカイブコマンドが実行され、WALセグメントが別の場所に保存されます。
  • 設定値に依存する項目:
    • archive_mode: WALのアーカイブを有効にするかどうか。
    • archive_command: WALアーカイブの方法を指定するコマンド。
  • 負荷:
    • CPU負荷: アーカイブ処理自体は主にディスクI/Oに依存しますが、アーカイブ時に多くのWALデータを処理する場合にはCPU負荷が発生します。
    • ディスクI/O負荷: アーカイブの際に、WALセグメントを別のストレージやリモートのバックアップ先にコピーするため、ディスクI/Oやネットワーク帯域を使用します。

7. チェックポイント(CHECKPOINT

  • 操作: チェックポイントは、WALに記録されたデータをディスクの実際のデータファイルに反映させるプロセスです。チェックポイントが発生すると、WALに記録された変更内容がディスクのデータファイルに書き出され、以降WALの古いデータが削除されます。これにより、障害発生時のリカバリ時間が短縮されます。
  • 設定値に依存する項目:
    • checkpoint_timeout: チェックポイントが実行される時間間隔を設定します。これが短いと頻繁にチェックポイントが発生し、ディスクI/Oが増加します。
    • checkpoint_completion_target: チェックポイントの処理をどの程度分散して実行するかを設定します。この値が高いと、チェックポイント処理を分散して実行し、ディスクI/Oのピークを避けます。
    • max_wal_size: WALファイルの総サイズがこの値に達すると、強制的にチェックポイントが発生します。
  • 負荷:
    • CPU負荷: チェックポイント処理にはCPUも使用されますが、主にディスクI/Oがボトルネックになることが多いです。CPU負荷は、変更データの圧縮や同期などの処理によって増加します。
    • ディスクI/O負荷: チェックポイントはWALからディスクへの大量のデータ書き込みを伴うため、ディスクI/Oに大きな負荷がかかります。特に、WALサイズが大きくなると、チェックポイント時にディスクI/Oのピークが発生します。

まとめ

ステージ タイミング 主要設定値 負荷がかかる操作
トランザクション開始 BEGINコマンド なし ほぼ負荷なし
クエリ実行 INSERTUPDATEDELETEなど wal_buffers CPU負荷、WAL書き込みのディスクI/O負荷
WALのフラッシュ クエリ実行後、COMMIT synchronous_commitfsync ディスクI/O負荷、CPU負荷は比較的少ない
トランザクションのCOMMIT COMMIT synchronous_commit ディスクI/O負荷、CPU負荷は少ないが同時に大量のCOMMITがある場合は増加する
トランザクションのROLLBACK ROLLBACK 特になし CPU負荷は少ないがトランザクションが大きい場合増加、WALは書き込まれる
WALのアーカイブ WALファイルのローテーション時 archive_modearchive_command ディスクI/Oおよびネットワーク負荷、CPU負荷も発生する可能性
チェックポイント 一定間隔、またはWALサイズ超過時 checkpoint_timeoutcheckpoint_completion_targetmax_wal_size ディスクI/O負荷、CPU負荷も増加する可能性あり

追加の重要ポイント

  • クエリ実行時にWALに書き込まれる内容はCOMMITされるかどうかに関係なく書き込まれるため、クエリ実行のたびにWALには負荷がかかります。
  • ROLLBACKされた場合もWALには記録されるが、そのデータは実際のデータファイルには反映されないため、実際のディスクへの書き込み負荷は少ないです。ただし、トランザクションが大規模である場合、無効化されたWALの記録も多くなる可能性があります。
  • CPU負荷の考慮: クエリ実行やチェックポイント処理はCPUにも負荷がかかります。特に、大量のデータを処理するクエリやインデックスの更新が絡む場合、CPU負荷が増加し、パフォーマンスに影響を与える可能性があります。
がまがま

sdaってなに

sda とは

sda は、Linuxシステムにおけるディスクデバイスの名前を指します。

具体的には、sdaSCSI(Small Computer System Interface)や SATA(Serial ATA)ドライブなど、通常のハードディスクやSSDを示すための名前であり、システムで最初に検出されたディスクデバイスに対して割り当てられます。

デバイス命名の例

  • sda: 一番目のディスクデバイス(通常、最初のSCSIまたはSATAディスク)
  • sdb: 二番目のディスクデバイス
  • sdc: 三番目のディスクデバイス

/dev/sda というパスで参照されることが多く、sda1, sda2のようにパーティションを持つ場合もあります。

  • /dev/sda: 最初に検出されたディスクデバイス
  • /dev/sda1: sda上の最初のパーティション
  • /dev/sda2: sda上の二番目のパーティション

まとめ

  • sdaは、システム内のディスク(HDDやSSDなど)のうち最初に認識されたものを指します。
  • sda1, sda2 などは、sda上のパーティションです。