20120924updaterelease/ja
2012-09-24 更新リリースのデータ破損問題に関する詳細
問題の説明
PostgreSQLのバージョン9.1と9.2には、ダーティブロックのメモリからのフラッシュ(または"チェックポイント")に関して性能改善と新機能の追加(主にログを取らないテーブル)の副作用として偶然混入したバグがあります。このバグは、以下の理由によりデータベースがシャットダウンまたは再起動した場合にある種のデータがディスクに書き込まれないことの原因となります。
- PostgreSQLのクラッシュ
- サーバクラッシュまたは電源喪失
- "immediate" シャットダウン (pg_ctl -m immediate)
- postmasterサービスに対する"kill -9" または Out-Of-Memory-Kill
- データベースがスタンバイからマスターに昇格した
これらの状況下では、データベースはリカバリ可能なデータ破損に陥る可能性があります。この破損の特徴は、一見正しいが実際には間違っている問い合わせ結果を返す場合があることです。このため、このデータ破損の影響を受けたかもしれないユーザはただちに復旧手順を実施することが重要です。
第一に、BTREEとGINインデックスの破損の可能性は低いです。正常にシャットダウンすればこの問題の拡散を防ぐことが出来ます。もしデータ破損が起きていた場合、おそらくインデックスが使用された時にエラーメッセージの形で現れるでしょう。
次に、リレーションの可視性マップ(訳注:visibility map)の破損が起こる有意な可能性(スタンバイではほぼ100%)があります。
PostgreSQL Global Development Groupはこの問題による不便についてお詫びします。
PostgreSQL 9.1 ユーザのための手順
もし9.1を利用しており、かつ過去数ヶ月の間にあなたのデータベースが予期せぬシャットダウンやフェイルオーバーをしていてデータベース破損の影響を受けている疑いがある場合は、以下の手順を実施してください:
- 新しい 9.1.6 のパッケージ群をダウンロードする
- 以下のいずれかの手段でPostgreSQLをクリーンシャットダウンする
- 起動スクリプトまたはサービスマネージャ
- pg_ctl -m start stop
- pg_ctl -m fast stop
- 9.1.6をインストールする
- 9.2 アップグレードのドキュメントに従い、データベースサーバを再起動する前のこの時点でpostgresql.confのvacuum_freeze_table_ageを0に設定して、この手順が完了してからそのエントリを削除するのがよいでしょう; vacuum_cost_delayをグローバルに設定できるのはこの時点です。
- データベースシステムを再起動する
- BTreeおよびGINのインデックスを順次再構築する(下記参照)
- データベース全体に対する手動vacuumを都合のよい負荷の低い時間帯にスケジュールする(下記参照)
もしあなたがPostgreSQL 9.2へのアップグレードを計画している場合は、最初にデータベース全体に対するVACUUMを実行することが重要です。
PostgreSQL 9.2 ユーザのための手順
もし9.2.0を利用しており、かつ過去数ヶ月の間にあなたのデータベースが予期せぬシャットダウンやフェイルオーバーをしていてデータベース破損の影響を受けている疑いがある場合は、以下の手順を実施してください:
- 新しい 9.2.1 のパッケージ群をダウンロードする
- 以下のいずれかの手段でPostgreSQLをクリーンシャットダウンする
- 起動スクリプトまたはサービスマネージャ
- pg_ctl -m start stop
- pg_ctl -m fast stop
- 9.2.1をインストールする
- 9.2 アップグレードのドキュメントに従い、データベースサーバを再起動する前のこの時点でpostgresql.confのvacuum_freeze_table_ageを0に設定して、この手順が完了してからそのエントリを削除するのがよいでしょう; vacuum_cost_delayをグローバルに設定できるのはこの時点です。
- データベースシステムを再起動する
- すぐにあなたのデータベース内の全てのテーブルをVACUUMする。
- BTreeおよびGINのインデックスを順次再構築する(下記参照)
全てのテーブルをVACUUMする方法
可視性マップの破損を修復するために、ユーザはvacuumを実行してマップ全体をリセットするために全データベースブロックのスキャンを強制しなければなりません。これは結果的にデータベース全体のスキャンを意味するので、相当量のIOを発生させ、大きなデータベースではかなり時間がかかるでしょう。並列で実行されるデータベースの影響を改善する方法、vacuumを拡散させるためにcost delayを使用することです:
SET vacuum_cost_delay = 50;
対話的VACUUM
データベースそれぞれについて、以下の手順を実施する必要があります:
- psqlにPostgresのスーパーユーザでログインする
- もしそうするならば、vacuum_cost_delayを設定する
- "VACUUM ( FREEZE, VERBOSE, ANALYZE );"を実行する(ANALYZEは省略可能)
このコマンドはデータベース全体のvacuumの進捗を確認できるように大量の出力を生成します。
vacuumの終わったものと終わっていないものを追跡するために、あなたは全部を順番に実行する代わりに一度に一つずつテーブルをVACUUMすることもできます。
vacuumdb
もしvacuumするデータベースが複数ある場合は、代わりにvacuumdbを使うほうが便利だと判断するかもしれません。この場合はこのように実行します:
- もしそうするならば、postgresql.confでvacuum_cost_delayを設定する(そしてデータベースをリロードする)
- postgresスーパーユーザで"vacuumdb -F -v -z -a"を実行する
データベースサーバに接続するために追加のパラメータをvacuumdbに指定する必要があるかもしれない点に注意して下さい。-z(analyze)や-v(verbose)オプションは省略可能です。
BTree/GINインデックスの再構築
更新リリースにより修正された問題によって破損したインデックスはアクセスされるとエラーメッセージを表示するので、容易に識別できそうです。しかし、いくつかのインデックスは(あまりなさそうですが)エラーなしで誤った応答を返すように破損しているかもしれません。
上で推奨されているVACUUM FREEZEは何種類かのインデックス破損を修復します。しかし、データの完全性に関する強い懸念を持つユーザや、サーバで過去に複数回のクラッシュやフェイルオーバーが発生していて特にリスクを感じているユーザは、考えうるあらゆる破損を除去するためにインデックスの再構築を追加手順として実施すべきです。
各インデックスの再構築
予防であってもインデックス破損を発見したためであっても、一度に一つずつインデックスを再構築できます。最も単純な方法はREINDEXを使うことです。
REINDEX TABLE <tablename>;
または、単一インデックスに対しては:
REINDEX INDEX <indexname>;
利用可能なRAMの1/8(最大で2GB)までmaintainance_work_memを増やして、REINDEXで使えるRAMを増やすこともできます。REINDEXはテーブル全体の書き込みロックを取得し、テーブルのサイズに依存しますが実行にかなりの時間がかかることがあります。同時に存在するデータベース負荷の下でインデックスを再構築するために、CREATE INDEX CONCURRENTLYが利用できます:
CREATE INDEX CONCURRENTLY <indexname>_tmp <index_definition>; BEGIN; DROP INDEX <indexname>; ALTER INDEX <indexname>_tmp RENAME TO <indexname>; END;
これは最後の削除とリネームの段階でのみテーブルをロックします。ただし、より複雑です。
どちらのアプローチも、大きなテーブルに実行している間は相当量のIOを発生させます。
BtreeおよびGINインデックスの一覧の取得
インデックス再構築のアプローチに関わらず、データベース内のBTreeおよびGINインデックスの一覧を取得できます。BTreeは最も一般的なインデックス種別であるため、あなたのデータベース内のほとんどのインデックスがこれに含まれるでしょう。GiSTインデックスはとても大きくなりうることを考慮して、それらを再構築の対象から外すことができます。
このクエリを使ってください:
SELECT tablename, indexname, indexdef FROM pg_indexes WHERE ( indexdef ILIKE '%USING btree%' OR indexdef ILIKE '%USING GIN%' ) AND schemaname <> 'pg_catalog' ORDER BY tablename, indexname;
全インデックスの再構築
要求されるダウンタイムを許容でき、全ての破損の予防を絶対的に確信したいのであれば、reindexdb ユーティリティを使ってデータベース内の全てのインデックスを再構築することができます。このコマンドは、破損の危険性がないにも関わらずGiSTインデックスも再構築してしまう点に注意してください。
一つのデータベースをインデックス再構築するにはpostgresスーパーユーザで以下を実行してください:
reindexdb <databasename>
または、全てのデータベースをインデックス再構築するには:
reindexdb -a
データベースサーバに接続するために、reindexdbの追加のオプションが必要になるかもしれません。reindexdbは全てのテーブルのロックを一度に一つずつ取得するので、ダウンタイム中に行うのが最適です。