【AWS Database Blog】Amazon RDS for PostgreSQL でトランザクションID周回の早期検知のための実装
AWS Database Blog では、 AWS の様々なデータベースサービスや利用時の Tips をご紹介しています。今回は、AWS のデータベースエンジニアであるShawn McCoy が書いた Implement an Early Warning System for Transaction ID Wraparound in Amazon RDS for PostgreSQL を翻訳してご紹介します。
- 江川大地
------------
PostgreSQL を運用している方は理解している思いますが、重要なメンテナンスが autovacuum プロセスによって行われています。このバックグラウンドプロセスの最も効率的なことはリリースのたびに、改善がなされることです。多くの PostgreSQL ユーザにとって、そのデフォルト設定はうまくいくでしょう。しかしながら、いくつかのワークロードや利用パターンにおいては、パラメーターのカスタマイズが必要となります。この投稿では、2つの領域をカバーする予定です。1つは、Amazon RDS for PostgreSQL のトランザクションID の健全性をどう監視できるか、2つ目は、問題が発覚した場合にお客様がどうすべきかについて役立つ内容を紹介したいと思います。
"トランザクション ID" の健全性とは?
PostgreSQL データベースは、データロスを避けるための重大なアクションを取るまで、約20億(2^31)の VACUUM されない"実行中の"トランザクションを保持することが可能です。もし VACUUM されないトランザクション数が、(2^31 - 10,000,000)
に達した場合、ログにより VACUUM が必要だという警告が始まります。もし、もし VACUUM されないトランザクション数が、 (2^31 - 1,000,000)
に達した場合、PostgreSQL データベースは read-only モードとなりなります。これを解決するためには、サーバを停止し、シングルユーザモードでサーバを起動して、スタンドアローンの VACUUM を実施する必要があります。 この VACUUM には、数時間あるいは数日(データベースサイズに依存します)のダウンタイムを要します。トランザクション ID 周回についての詳細な説明は PostgreSQL ドキュメントに記載されていますので、参照してください。
早期発見のためのポイント
約21億の VACUUM されないトランザクションが、"where the world ends(世界が終わる場所)" であることをソースコードのコメントから知ることができます。トランザクションが、この数を重ねた ID になる理由はいくつかありますが、まずは、この差し迫った問題を AWS 上でどのように警告できるのかを見ていきましょう。手動でクエリを投げることで監視することも可能ですが、ここでは、より簡単にアラートをあげる設定をするために Amazon CloudWatch のメトリクス、MaximumUsedTransactionIDs を紹介します。このメトリクスを作り出すために、Amazon RDS のエージェントは下記のクエリを実行してます:
SELECT max(age(datfrozenxid)) FROM pg_database;
お客様と仕事をさせていただいた経験から、このメトリクスが、10億に達した場合にアラームを発行するような設定を推奨しています。いくつかのワークロードでは、5億といった緊急度の低い閾値を設定するのが適切かもしれません。では、CloudWatch のドキュメント にしたがって、CloudWatch アラームの設定を見ていきましょう。
まず、AWS マネジメントコンソールにて、 CloudWatch を選択します。
そして、ナビゲーションペインにある Alarms を選択します。
Create Alarm を選択して、新しいアラームを作成していきます。
MaximumUsedTransactionIDs と検索ウィンドウに入力してエンターキーをクリックし、インスタンスの一覧を表示させます。以下の例では、そのうちの1台を選択します。もし、複数台のインスタンスがある場合、その複数台をまとめて1つのアラームとして登録することも可能です。
設定したいインスタンスを選択後、 Next をクリックして、閾値の設定を行っていきます。
閾値(1000000000) と通知先の Email の設定を行います。
私の例では、すでに閾値に到達していたので、すぐに下記のようなメールを受け取りました。
このメッセージは、CloudWatch のダッシュボードにも表示されます。
閾値を10億に設定すると、この問題を調査するための十分な時間を持つことができます。解決にどのくらいの時間がかかるかは、この問題の原因に依存します。デフォルトの autovacuum_freeze_max_age の値は、2億です。もし、最も古いトランザクションの年代(age)が 10億になったら、autovacuum はこの閾値を2億まで減らします。これらの問題を解決するのに、さらに十分な時間が与えられるということです。システムを通常の作業負荷の下に保ちながら、こういった問題を修正しようとすることほど不満がたまるものはありません。
監視システムがアラームを発行した次のステップは?
監視システムがアラートメールを発行し、問題があるということがわかるでしょう。お客様とともに解決にあたった際によくあった原因とその解決方法を特定するための手順を紹介したいと思います。問題が発覚して、まずよくやることの1つが、 vacuum verbose コマンドの実行と、そのアクションがどのくらいかかるかを確認することです。このアプローチは、すぐに問題を解決し、長い目で見た対応策を計画するのに役立ちます。
ここから、以下の3つのことがわかります。
- autovacuum が現在動いている場所
- どのデータベースが年代を重ねているか
- どのテーブルが年代を重ねているか
この情報を元に、何が起きているのかを分析できます。
autovacuum が何をしているか?
autovacuum が何をしているか確認するため、次のようなアクションを取ることができます。このアクションをとるには、rdsadmin が実行するすべてのプロセスに対する参照権限が必要になるため、RDS Postgres 9.3.12以降、 9.4.7以降もしくは、9.5.2以降である必要があります。下記のクエリによって、確認したい情報を出力することができます。
SELECT datname, usename, pid, waiting, current_timestamp - xact_start
AS xact_runtime, query
FROM pg_stat_activity WHERE upper(query) like '%VACUUM%' ORDER BY
xact_start;
どのデータベースが年代を重ねているか
下記のクエリによって、どのデータベースが年代を重ねているかを確認できます。
SELECT datname, age(datfrozenxid) FROM pg_database ORDER BY 2 DESC LIMIT 20;
どのテーブルが年代を重ねているか
どのテーブルが年代を重ねているか確認するために、先のクエリ結果で示された最も年代を重ねているデータベースに接続します。このデータベースには、最も古いトランザクションが動いているテーブルがあり、次のクエリによって、上位20の古いテーブルとそのサイズを確認できます。おそらく、これらのテーブルは、最初のクエリで出力された autovacuum が動いていたものだと推測できるでしょう。
SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age,
pg_size_pretty(pg_table_size(c.oid)) as table_size
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind = 'r'
ORDER BY 2 DESC LIMIT 20;
では、2つのシナリオで、これらのクエリの結果を見ていきましょう。
長時間動いている autovacuum セッションの場合
1つめの例では、長い間動いているセッションがあります。この状況では、maintenance_work_mem がテーブルサイズに比べて非常に小さく設定されていることが推測されます。
これらのセッションは、おそらく完了することはないでしょう。こういったシナリオでよくある原因を 2つあげます。
- もっともよくあるのが、大きいテーブルに対して、maintenance_work_mem が非常に小さく設定されていることです。この重要なパラメーターは、autovacuum ワーカーが動いている間に、どのくらいのタプルの情報をメモリ内に保持できるかを決定します。もし、このパラメーターがテーブルサイズに比較して非常に小さい場合、完了するためにそのテーブルに対して複数回にわたっての処理が必要になります。詳細は、RDS でのmaintenance_work_mem推奨設定をごらんください。
- もし、maintenance_work_mem の設定値を調節しても、autovacuum ワーカーの実行が長時間続くならば、大きいテーブルに対して、個別のパラメーター設定が必要となる場合があります。このアプローチの良いところは、システム全体に対しては、積極的なautovacuum が行われることなく、大きいテーブルに対してのみ積極的に実行できるところです。デフォルトのパラメーター設定では、autovacuum は、内部的なカウンタである auto_vacuum_cost_limit に達するたびに autovacuum_vacuum_cost_delay(20 ミリ秒) の休み(中断)を取ります。大きいテーブルだと、追加のリソース消費があるものの autovacuum に"休み"を取らせないことが有効です。この通りにするには、次の設定を行います。
ALTER TABLE mytable SET (autovacuum_vacuum_cost_delay=0);
より詳細な情報については、 RDS ドキュメントのautovacuum に影響を与える他のパラメーターを参照し、設定方法を確認してください。
autovacuum セッションは完了したようにみえるが、バックエンドで落ちている場合
autovacuum セッションは完了したが、バックエンドで落ちているシナリオは、修正がより困難な可能性があります。デフォルトでは、autovacuum_max_workers は、3 に設定されています。この設定はたいていうまく動作します。しかし、非常に多くのテーブル(30,000以上)を持っている場合、3つのワーカーでは、対応しきれない可能性があります。先ほど紹介したクエリを再度実行し、autovacuum が何をしているか確認しましょう。下記に示した結果からは、うまくいっていることが読み取れます。
この結果は実際、特に問題がないように見えます。autovacuum セッションは、長時間実行されているわけではなく、全3つのワーカーがテーブル上で動いています。しかし、データベースの年代(age)が10億という閾値を超えたことを示すアラームが発行されています。何が問題なのでしょうか?
もし、例のクエリでデータベース、テーブルの年代を確認すれば、autovacuum によるテーブルからの不要領域の回収が何者かによって妨げられていることが分かります。
ここで、autovacuum が実行されているテーブル数を正確にチェックします。というのも、これらのクエリは、アラームを発行するのに十分なほどテーブルが年代を重ねていること以外に、何の問題も示していません。
SELECT count(*) FROM pg_tables;
スキーマ設計に関する質問はさておき、デフォルトの3つのワーカーで処理するには、あまりにもテーブル数が多いに違いありません。この問題の典型的な修正方法としては、autovacuum_max_workers を増やすことです。注意すべきことは、コストパラメータである autovacuum_vacuum_cost_limit は、利用可能なワーカーに均等に分散されることと、各ワーカーが、maintenance_work_mem で指定したメモリを消費することの 2つです。この場合、autovacuum の進捗状況と CloudWatch が示すグラフが下降傾向を示すかどうか確認するために、下記の設定が推奨です。
また、スキーマ設計を改善できるか確認して、全体のテーブル数を減らすことも推奨します。
その他のシナリオ
この投稿では、重要な PostgreSQL のメトリクスを監視するプロセスを確認し、問題が起きた場合にどのようにトラブルシューティングを開始するためのステップを示し、RDS を利用するお客様と解決したよくある2つのシナリオを説明しました。他のシナリオが起こる可能性もありますが、この投稿が皆様がトラブルシューティングを開始するための助けになればと思います。
CloudWatch メトリクスとあわせて、autovacuum のログ記録を有効にすることが有用でしょう。そうすることで、どこに時間を消費しているか確認できます。しかしながら、この記事の冒頭でお話した 20億の "age" に近づいているテーブルに対して、手動の "vacuum freeze" を実行することは有益かもしれません。この操作による所要時間を確認できるという意味で有益です。verbose オプションをつけると、何が起きているかについてより詳しく知ることができるでしょう。たとえば、この操作をするにあたって余分な時間を費やす、利用していないインデックスが判明するならば、削除する良い機会になります!こちらの RDS ドキュメント には、手動で vacuum verbose を実行するためのステップを紹介しています。
autovacuum に関する情報は、RDS ドキュメントの Amazon RDS での PostgreSQL Autovacuum の使用 にて確認できます。もし、何か質問があれば、本記事にコメントして知らせてください。
コメント