PostgreSQL:バキュームの場合

PostgreSQLを高負荷で運用しているクライアントの1人が、トランザクションカウンターオーバーフロー(xidラップアラウンド)に関連する問題に遭遇し、通常の手段を使用して解決する方法はありませんでした。 手術で問題を解決し、将来そのような状況を防ぐためのパッチをリリースしました。







この記事では、問題が発生する方法と理由、およびそれを防ぐ方法について説明します。







PostgreSQLトランザクションカウンターデバイス



PostgreSQLの最も魅力的な機能の1つは、データの競合が激しい状況で作業できることです。読み取りトランザクションは書き込みをブロックせず、逆も同様です。 これはすべて、マルチバージョン性(MVCC)のメカニズムのおかげです。 実装は、PostgreSQLの各トランザクションがxidと呼ばれる独自の番号(識別子)を持っているという事実に基づいています。 数値は常に増加するため、小さい番号のトランザクションはより早く開始され、大きい番号のトランザクションは後で開始されると見なされます。 テーブルの各行には、特に、ユーザー要求には表示されない2つの追加のシステムフィールドがあります。これらはxminおよびxmaxと呼ばれます。 xminフィールドには、この行を作成したトランザクションの番号とxmax-削除したトランザクションの番号が格納されます(もちろん、これが発生した場合を除く)。 したがって、各行は、異なるスコープを持つ複数のバージョンを持つことができます。 データストレージを整理するこのアプローチは、バージョン管理と呼ばれます。







PostgreSQLのカスタム行の内容は変更されず、システムフィールドのみが変更されます。 データの更新(UPDATE)は、行を削除済みとしてマークします。つまり、xmax = xid_currentを設定し、更新されたコンテンツでxmin = xid_currentの行の新しいコピーを作成します。







PostgreSQLがテーブルからデータを読み取るとき、これは常にスナップショットのコンテキストで発生します。 データスナップショットを作成すると、現在のトランザクション番号が記憶されます。この番号から、現在のスナップショットのコンテキストで表示される行のいくつかのバージョンから選択できます。 さらに、現在完了していないすべてのトランザクションのリストがスナップショットに含まれます。これは、そのようなトランザクションによって行われた変更をスナップショットに含めるべきではないためです。







既存のすべてのトランザクションの範囲を超える行バージョンは不要になります。 データベースが過度に大きくならないようにするために、autovacuumと呼ばれる特別なバックグラウンドプロセスにより、文字列の古いバージョン(xmaxが現在実行中のすべてのトランザクションよりも若いバージョン)が削除されます。







トランザクションカウンターのサイズは32ビットです。つまり、約40億の値を格納できます。 もちろん、これはそれほどではありません。 64ビットにする提案がありましたが、この場合、オーバーヘッドのためにデータベースが大幅に成長することを忘れないでください-実際、xminとxmaxは各行に格納されます。 2 ^ 32-1のカウンター制限に達したと想像してください。 1つ追加すると、カウンターがオーバーフローし、ゼロにリセットされます。 これは災害につながります-結局、PostgreSQLはトランザクション数が常に増加することを期待しています。







もちろん、このような状況を防ぐメカニズムがあります。 まず、トランザクション番号のスペースがループします。実際には、若い番号は番号が小さいトランザクションではなく、一方が他方の円の半分未満であると見なされます。 次に、クリーニング(VACUUM)テーブル中に、いわゆるフリーズが実行されます。 バキューム/自動バキュームプロセスは、古いxmaxでデッドラインを削除することに加えて、古いxmin値でライブラインも処理します。 xminが最も古い実行中のトランザクションよりもはるかに小さく、「年齢」がvacuum_freeze_min_ageを超える行は「凍結」されます(特別なサービスビットでマークされます)。 彼らは通常の可視性の規則に従わなくなり、常に通常のトランザクションよりも古いと見なされます。 このように、クリアすると、トランザクションカウンターの円に沿って、古い行が常にフリーズします。







PostgreSQLトランザクションカウンター







データベース内の最も古いトランザクションの経過時間は、システムディレクトリに保存されます。







SELECT datname, age(datfrozenxid) FROM pg_database;
      
      





各テーブルの統計も維持されます。







 SELECT relname, age(relfrozenxid) FROM pg_class;
      
      





バックグラウンドプロセスは自動モードでトランザクションを監視しますが、PostgreSQLを使用する場合、管理者はデータベース内の最も古いトランザクションの年齢が2 ^ 31(半円、すべての許容値の半分)に近づかないようにする必要があります。 これにより、PostgreSQLはトランザクションの経過時間を正しく判断できるようになります(カウンターの周期的な性質を考慮して)。 datfrozenxidの年齢が指定したポイントに近い場合、PostgreSQLはトランザクション番号を発行できなくなり、データの安全上の理由で動作を停止し、手動の介入とクリーニング(VACUUM)が必要になります。

そのため、カウンタが20億増加する超長時間のトランザクションを回避する必要があります。







問題と治療



お客様のいずれかで発生したdatfrozenxidオーバーフローでした。 管理者はVACUUM FREEZEコマンドを手動で起動し、8日間機能しました。 この間に、負荷がかかった状態で約2 ^ 31の新しいトランザクション番号が発行されました。 VACUUMはトランザクションの外部で動作しますが、起動時にデータスナップショットを作成し、古いバージョンの文字列と現在のバージョンを区別することに注意してください。 システムは停止し、手動の介入が必要でしたが、サービスが実行されたという事実にもかかわらず、オペレーティングモードで起動できなくなりました。







問題は、共有メモリにある変数を更新する前に、VACUUMチームが新しいトランザクション番号を取得して、フリーズが正しく行われ、システムに「未来から」のトランザクションがないことを確認しようとしたことです。 使用可能な番号が終了したため、コマンドはエラーで終了しました。そのため、使用可能な番号の範囲を担当する変数は更新されませんでした。 この問題を解決するために、2つの解決策が考えられました。運用と永続です。







顧客データベースをできるだけ早く再開するには、運用ソリューションが必要でした。 これを行うには、共有メモリ内の変数を手動で編集する必要がありました。









これらのアクションの結果、利用可能なトランザクションの範囲を担当する値は共有メモリとディスクの両方で更新され、顧客のDBMSは再び通常の操作を継続できました。







問題の永続的な解決策は、VACUUMチームが個別のトランザクション番号をまったく受け取らないことです。 状況を再現するためのパッチと手順は、 ハッカーのメーリングリストに送信されました。 この修正は、PostgreSQLのすべてのバージョンに含まれます







9.6での変更



フリーズの不快な特性は、このためにテーブル全体をスキャンする必要があることです。 通常のバキューム処理はより巧妙です:プロセス中にページ上の行のすべてのバージョンが関連していることが判明した場合(つまり、xmax = 0)、そのようなページは可視性マップと呼ばれる特別なファイルでマークされます。 バキュームは、変更が行われるまで(可視性マップのマークが自動的に削除されるまで)そのようなページに戻りません。







残念ながら、フリーズでは可視性マップを使用しません。結局、最新バージョンの文字列のみを含むページでも、xminフィールドでフリーズされていないトランザクション番号は拒否される可能性があります。 定期的なフルスキャンは、非常に大きなテーブルサイズで問題を引き起こす可能性があります。







既にベータ版がリリースされているPostgreSQL 9.6では、この複雑さが克服されました。 可視性マップは、「フリーズマップ」を含むように拡張されました。すべてのトランザクションが既にフリーズされているページをマークします。







トランザクションカウンターオーバーフローの監視



オーバーフローを制御するには、pg_databaseシステムカタログからdatfrozenxidトランザクションの経過時間を表示する必要があります。 Zabbixシステムを使用している場合、mamonsu監視クライアントを試してください。クライアントには必要なメトリックがすでにあります。 お客様はmamonsuでご利用いただけます。







結論



リレーショナルDBMSなどの複雑な製品にエラーが発生することはありません。 PostgreSQLの信頼性にもかかわらず、操作中に不快な問題が発生する場合があります。 コミュニティはサポートを提供しますが、まず、エラーメッセージを記入するために多くの作業を行う必要があり(開発者があなたの状況を再現できるように)、次に、誰も修正期間を保証しません。







そのため、ビジネスに不可欠なシステムで作業する場合、開発者がソースコードに精通し、必要な修正を行い、最短時間でそれを行うことができるベンダー企業から技術サポートを受けることが有用です。








All Articles