メモリ使用量を管理するためのベスト プラクティス

このページでは、Cloud SQL インスタンスのメモリ使用量を構成する方法について説明します。

はじめに

Cloud SQL インスタンスを作成する際にインスタンスのメモリ容量を選択します。PostgreSQL データベースのワークロードが増加すると、インスタンスのメモリ使用量も増加します。大量のメモリを消費するインスタンスでは、メモリ不足(OOM)の問題につながる可能性があるパフォーマンスのボトルネックが発生することがあります。

Cloud SQL インスタンスが需要の増加によりメモリ不足になると、データベースのダウンタイムが発生する可能性があります。したがって、インスタンスのメモリとメモリ関連のデータベース フラグを適切に構成し、メモリ使用量をモニタリングして、インスタンスが正常な状態で動作するようにすることが重要です。

PostgreSQL のメモリ コンポーネントは、大きく次の 2 つの部分に分かれています。

  • グローバル メモリ。クエリを実行するすべてのプロセスで共有されます(例: shared_buffersmax_connections など)。
  • ローカルメモリ。各接続に割り当てられた専用メモリです(例: work_memmaintenance_work_memtemp_buffers など)。

構成に関するその他の考慮事項については、一般的なベスト プラクティスオペレーション ガイドラインをご覧ください。

メモリ使用量とフラグ

Cloud SQL インスタンスによるメモリ使用量が多い場合、次のような疑問が生じるかもしれません。

  • どのクエリやプロセスがメモリを多く使用しているのか?
  • メモリの設定は、データベースの動作に適しているか?
  • どのようにすれば、メモリ設定を変更できるか?

PostgreSQL データベースが動作する際に使用されるメモリは、ほとんどが以下のいくつかの領域です。

  • 共有バッファ: readwrite のオペレーションでテーブルデータを保持するために PostgreSQL が割り当てる共有メモリです。read オペレーションでは、ディスクからリクエストされたデータはまず RAM に取り込まれ、それからクライアントに渡されます。同様に、PostgreSQL では、データがリクエストされると(SELECT * from emp など)、まずディスクから shared_buffers に取り込まれてキャッシュ保存され、続いてクライアントに渡されます。write オペレーションでも同じことが起こります。

    共有バッファは、データ キャッシュ保存、接続キャッシュ保存、データ操作言語(DML)オペレーションなどのデータベース アクティビティのすべてのプロセスと接続の共有メモリ領域でもあります。この領域に割り当てることができる最大値は shared_buffers フラグで指定され、デフォルトはインスタンスのメモリの 33% です。shared_buffers の値が大きい場合、メモリにキャッシュ保存されているデータのサイズは大きくなります。

  • クエリの作業メモリ: PostgreSQL は、クエリの実行時に、並べ替えやハッシュなどのオペレーションごとにローカルメモリを割り当てます。一時ディスク ファイルに書き込む前にクエリの各オペレーションに割り当てることができる最大値は、work_mem フラグによって構成され、デフォルト値は 4 MB です。work_mem の値が大きければ、メモリ内で並べ替え可能なデータの量が多くなります。
  • メンテナンス用の作業メモリ: VACUUMCREATE INDEXALTER TABLEADD FOREIGN KEY などの一部のメンテナンス オペレーションでは、PostgreSQL が割り当てる個別のローカルメモリが必要になります。これらのオペレーションで使用するバックエンド プロセスの最大量は maintenance_work_mem フラグで構成できます。デフォルト値は 64 MB です。なお、自動バキューム ワーカーもメンテナンス用の作業メモリを使用し、その最大値が autovacuum_work_mem フラグでオーバーライドできることに注意してください。maintenance_work_mem の値が大きければ、VACUUM オペレーションの実行速度は速くなります。
  • 一時バッファ: データベース セッションで一時テーブルが使用されると、PostgreSQL はセッション ローカルの一時テーブルを保持するために一時バッファを割り当てます。最大量は temp_buffers フラグで指定でき、デフォルト値は 8 MB です。
  • データベース接続: クライアントがデータベースに接続すると、PostgreSQL はクライアント セッションを提供するバックエンド プロセスを作成します。クエリを実行するためのメモリに加えて、PostgreSQL はシステム カタログ キャッシュや準備されたクエリプランなどの情報を保持するために追加のメモリを割り当てます。データベース サーバーに対して許可される同時接続の最大数は、max_connections フラグで構成できます。各アイドル状態の接続では、約 2 MB~3 MB の共有メモリが使用されます。max_connections の値が大きい場合、インスタンスにはさらに多くの接続が可能になりますが、メモリの使用量は増えます。

PostgreSQL におけるメモリ コンポーネントの完全なリストについては、PostgreSQL のドキュメントをご覧ください。このセクションに記載されているフラグを変更または修正するには、データベース フラグを構成するをご覧ください。

メモリ使用量をモニタリングする

Cloud Monitoring で定期的にインスタンスのメモリをモニタリングし、メモリの上限を超えないようにします。Cloud Monitoring でアラートを設定し、使用量が 6 時間の上限の 90% を超えた場合にアラートを送信することをおすすめします。このアラートにより、メモリ使用量が上限に近づくと必ず警告を出せます。

さらに、メモリ不足インシデントをモニタリングします。そのために、Cloud Monitoring で server process .* was terminated by signal 9: Killed メッセージのログベースの指標を設定してメモリ不足イベントをカウントし、そのようなイベントが発生するたびにアラートを出します。

インスタンスが常にメモリの上限の 90% 以上で動作している場合、またはメモリ不足イベントが発生した場合は、インスタンスのメモリを増やすことができます。あるいは、データベース接続の数を制限するか、shared_bufferswork_memmax_connections などのデータベース フラグを低くして、メモリ使用量を減らすこともできます。これらのフラグを低くすると、インスタンスのパフォーマンスが制限される可能性があります。

メモリ不足

データベースのワークロードを処理するのに十分なメモリがない場合、最後の手段として、基盤となる Linux オペレーティング システムは out-of-memory (OOM) killer を使用してプロセスを終了し、メモリを解放します。Cloud SQL は、OOM killer が PostgreSQL ワーカー プロセスのみをターゲットとするように構成されています。ポストマスター プロセスはこの状況で保持されるため、既存のデータベース接続をすべて終了して、データベースの完全性を保つために復元を実行するだけでよいのです。このような事態が発生すると、データベースでサービスが停止し、ダウンタイムが発生します。PostgreSQL データベース ログには、以下のようなメッセージが表示されます。

2021-10-24 23:34:22.265 UTC [7]: [663-1] db=,user= LOG: server process (PID 1255039) was terminated by signal 9: Killed
2021-10-24 23:34:22.265 UTC [7]: [664-1] db=,user= DETAIL: Failed process was running: SELECT * FROM tab ORDER BY col
2021-10-24 23:34:22.277 UTC [7]: [665-1] db=,user= LOG: terminating any other active server processes
2021-10-24 23:34:22.278 UTC [1255458]: [1-1] db=postgres,user=postgres WARNING: terminating connection because of crash of another server process
2021-10-24 23:34:22.278 UTC [1255458]: [2-1] db=postgres,user=postgres DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
2021-10-24 23:34:22.278 UTC [1255458]: [3-1] db=postgres,user=postgres HINT: In a moment you should be able to reconnect to the database and repeat your command.
2021-10-24 23:34:22.278 UTC [1255458]: [4-1] db=postgres,user=postgres CONTEXT: while updating tuple (27,18) in relation "tab"
...
2021-10-24 23:34:22.558 UTC [1255477]: [1-1] db=postgres,user=postgres FATAL: the database system is in recovery mode
...
2021-10-24 23:34:25.579 UTC [7]: [666-1] db=,user= LOG: all server processes terminated; reinitializing
...
2021-10-24 23:34:25.691 UTC [1255482]: [1-1] db=,user= LOG: database system was interrupted; last known up at 2021-10-24 23:31:53 UTC
2021-10-24 23:34:25.776 UTC [1255482]: [2-1] db=,user= LOG: database system was not properly shut down; automatic recovery in progress
2021-10-24 23:34:25.789 UTC [1255482]: [3-1] db=,user= LOG: redo starts at 227/AB359400
2021-10-24 23:34:38.957 UTC [1255482]: [4-1] db=,user= LOG: redo done at 229/4621F508
2021-10-24 23:34:38.959 UTC [1255482]: [5-1] db=,user= LOG: last completed transaction was at log time 2021-10-24 23:34:18.5535+00
2021-10-24 23:34:39.290 UTC [7]: [667-1] db=,user= LOG: database system is ready to accept connections

次のステップ