PostgreSQL 高度技術者育成テキスト ch5 (障害対応)

OSS-DBPostgreSQL勉強メモ

[asin:B00P4WD4QG:detail]


障害対応の基本

  • 理想的な流れ

    1. 事象を確認する
    2. 状況を把握/整理する
    3. 目標を定める
    4. 対応案を検討する
    5. 対応を施す
    6. 効果を確認する
  • 【所感】不具合修正と同じ

    1. 現象を再現する
    2. 落ちるテストを書く

      • 状況の把握・整理
      • 目標を定める = どこまで直すか
    3. 修正を施す
    4. 修正を確認する

      • テストが通ること

サーバダウン障害への対応

PostgreSQLが起動できない

事象

  • PostgreSQLが起動できない

状況把握

  • ログ
  • プロセス

主な原因・対処案

メモリ不足

  • postgresql.confのshared_buffersがカーネルパラメータshmmaxを超えると、PostgreSQLサーバ起動時に怒られる
  • kernel.shmmaxが16GB、shared_buffersが32GBの例
...
2020-02-02 17:10:40.948 UTC [97] FATAL:  could not map anonymous shared memory: Cannot allocate memory
2020-02-02 17:10:40.948 UTC [97] HINT:  This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages. To reduce the request size (currently 35207938048 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.
2020-02-02 17:10:40.948 UTC [97] LOG:  database system is shut down
 stopped waiting
pg_ctl: could not start server
Examine the log output.
  • 適切な値を設定する

データベースクラスタの権限設定ミス

~$ initdb data2
~$ chmod 770 data2
~$ pg_ctl start -D data2 -o "-p 5433"
waiting for server to start....2020-02-29 11:20:16.530 UTC [145] FATAL:  data directory "/var/lib/postgresql/data2" has invalid permissions
2020-02-29 11:20:16.530 UTC [145] DETAIL:  Permissions should be u=rwx (0700) or u=rwx,g=rx (0750).
 stopped waiting
pg_ctl: could not start server
Examine the log output.
  • 700にしようね

動作不良

事象

  • 接続できない
  • 問い合わせに対する応答がない

状況把握

  • ログ
  • プロセス
  • そもそもOSやNWで止まっていないか?

    • timeoutかrefusedかの切り分けとか

主な原因

接続できない系

  • 名前解決できてない
psql -h hoge
psql: error: could not connect to server: could not translate host name "hoge" to address: Name or service not known
  • 名前解決できているが、当該ホストの指定のポートでサーバが動いていない
psql -p 5433
psql: error: could not connect to server: could not connect to server: No such file or directory
	Is the server running locally and accepting
	connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5433"?
  • 当該ポートでサーバが動いているが、host based authenticationに引っかかっている
psql -U syaro -d postgres
psql: error: could not connect to server: FATAL:  pg_hba.conf rejects connection for host "[local]", user "syaro", database "postgres", SSL off
  • 対処案

    • L4までのレベルでの接続可否の確認
    • 設定見直し

接続できているが処理が帰ってこない系

  • 原因

    • リソースの解放待ちで処理が進まない
    • 同期レプリケーションでstandbyから処理が帰ってこない
  • 対処案

    • 必要ならばロングトランザクションをcancelする等

リソース枯渇

事象

  • PostgreSQLがクラッシュ停止する

状況把握

  • ログ確認

主な原因

  • セマフォ不足
  • ディスク容量不足

    • wal領域がディスクフルになるとpanic
  • メモリ不足

対処案

  • 設定見直し

    • カーネルパラメータ含む
  • リソース追加

データ消失と復旧

バックアップからのリカバリ

トランザクションログ復旧

事象

  • トランザクションログの破損/消失が原因でPostgreSQLが起動できなくなる

再現

  • 壊す用のデータベースクラスタ作る
initdb data2
pg_ctl -D data2 start -o "-p 5433"
pg_ctl -D data2 stop
  • WALセグメントの先頭256Bを0で埋めて壊してみる
dd conv=notrunc if=/dev/zero of=data2/pg_wal/000000010000000000000001 bs=256 count=1
1+0 records in
1+0 records out
256 bytes copied, 0.0001245 s, 2.1 MB/s
  • サーバが立ち上がらなくなる
pg_ctl -D data2 start -o "-p 5433"
waiting for server to start....2020-02-29 12:23:30.692 UTC [840] LOG:  starting PostgreSQL 12.1 (Debian 12.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
2020-02-29 12:23:30.692 UTC [840] LOG:  listening on IPv4 address "0.0.0.0", port 5433
2020-02-29 12:23:30.692 UTC [840] LOG:  listening on IPv6 address "::", port 5433
2020-02-29 12:23:30.696 UTC [840] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5433"
2020-02-29 12:23:30.711 UTC [841] LOG:  database system was shut down at 2020-02-29 12:21:37 UTC
2020-02-29 12:23:30.711 UTC [841] LOG:  invalid primary checkpoint record
2020-02-29 12:23:30.711 UTC [841] PANIC:  could not locate a valid checkpoint record
2020-02-29 12:23:30.711 UTC [840] LOG:  startup process (PID 841) was terminated by signal 6: Aborted
2020-02-29 12:23:30.711 UTC [840] LOG:  aborting startup due to startup process failure
2020-02-29 12:23:30.712 UTC [840] LOG:  database system is shut down
 stopped waiting
pg_ctl: could not start server
Examine the log output.

状況把握

  • ログ

主な原因

  • トランザクションログの破損/消失

対処案

  • pg_resetwalコマンドでトランザクションログを復旧する

    • 決して手で操作しようとか思わないこと
pg_resetwal -D data2
Write-ahead log reset
  • サーバ起動
pg_ctl -D data2 start -o "-p 5433"
  • 無事起動した
waiting for server to start....2020-02-29 12:27:25.357 UTC [851] LOG:  starting PostgreSQL 12.1 (Debian 12.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
2020-02-29 12:27:25.357 UTC [851] LOG:  listening on IPv4 address "0.0.0.0", port 5433
2020-02-29 12:27:25.357 UTC [851] LOG:  listening on IPv6 address "::", port 5433
2020-02-29 12:27:25.362 UTC [851] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5433"
2020-02-29 12:27:25.375 UTC [852] LOG:  database system was shut down at 2020-02-29 12:27:10 UTC
2020-02-29 12:27:25.381 UTC [851] LOG:  database system is ready to accept connections
 done
server started

pg_resetwal

pg_resetwal resets the PostgreSQL write-ahead log.

Usage:
  pg_resetwal [OPTION]... DATADIR

Options:
  -c, --commit-timestamp-ids=XID,XID
                                 set oldest and newest transactions bearing
                                 commit timestamp (zero means no change)
 [-D, --pgdata=]DATADIR          data directory
  -e, --epoch=XIDEPOCH           set next transaction ID epoch
  -f, --force                    force update to be done
  -l, --next-wal-file=WALFILE    set minimum starting location for new WAL
  -m, --multixact-ids=MXID,MXID  set next and oldest multitransaction ID
  -n, --dry-run                  no update, just show what would be done
  -o, --next-oid=OID             set next OID
  -O, --multixact-offset=OFFSET  set next multitransaction offset
  -V, --version                  output version information, then exit
  -x, --next-transaction-id=XID  set next transaction ID
      --wal-segsize=SIZE         size of WAL segments, in megabytes
  -?, --help                     show this help, then exit

Report bugs to <pgsql-bugs@lists.postgresql.org>.
  • データベースクラスタを指定して、各種制御情報を変更できる

    • 次のOID
    • 次のXID
    • WAL開始位置 等
  • $PGDATA環境変数は効かない。必ず処理対象を明示する

トランザクションIDを変更する例

  • pg_xact/ディレクトリを確認し、最も大きな数字をもつファイル名を調べる

    • PostgreSQL9.x系ではpg_clog/
ls -lA pg_xact/
total 8
-rw-------   1 postgres       postgres 8192 2020-02-29 21:32 0000
  • これに1を足して、1048576 = 2^20 = (2^4)^5 を掛ける

    • 16進数ならばゼロを5つつければよい
0x0000 -> 0x0001 -> 0x000100000
  • この値に設定する
pg_resetwal -x 0x100000 data2
Write-ahead log reset

システムテーブルのインデックス復旧

事象

  • システムテーブルのインデックスが破損し、PostgreSQLが正常に動作しない

再現

  • データベースのoid特定
oid2name -p 5433
All databases:
    Oid  Database Name  Tablespace
----------------------------------
  13408       postgres  pg_default
  13407      template0  pg_default
      1      template1  pg_default
  • データベースオブジェクトのoid特定
oid2name -p 5433 -d postgres -i -S
From database "postgres":
  Filenode                                     Table Name
---------------------------------------------------------
      2600                                   pg_aggregate
      2650                       pg_aggregate_fnoid_index
      2601                                          pg_am
      2651                               pg_am_name_index
      2652                                pg_am_oid_index
...
  • pg_class_oid_indexを壊してみる
      2662                             pg_class_oid_index
  • ページファイル確認
ls -lA base/13408/2662
-rw-------   1 postgres       postgres    32768 2020-02-29 21:21 base/13408/2662
  • 先頭256Bに0を書き込んで壊してみる
dd conv=notrunc if=/dev/zero of=data2/base/13408/2662 bs=256 count=1
  • サーバ停止・起動
pg_ctl -D data2 stop
pg_ctl -D data2 start -o "-p 5433"
  • 接続を試みる
psql -p 5433
  • 壊れていて接続できない

    • ので REINDEXも実行できない
2020-02-29 12:58:53.917 UTC [3494] FATAL:  invalid page in block 0 of relation base/13408/2662
psql: error: could not connect to server: FATAL:  invalid page in block 0 of relation base/13408/2662

状況把握

  • ログ
2020-02-29 12:58:53.917 UTC [3494] FATAL:  invalid page in block 0 of relation base/13408/2662
psql: error: could not connect to server: FATAL:  invalid page in block 0 of relation base/13408/2662

対処案

  • 下記のいずれかでPostgreSQLを起動する

    • シングルユーザ状態

      • -Pオプションつきでpostmaster起動
    • システムインデックスを無視する状態

      • ignore_system_indexesパラメータをON

        • 開発者向けオプションなのでpostgresql.confのコメントアウトには無い
  • 前者を実施してみる
pg_ctl -D data2 stop
pg_ctl -D data2 start -o "-p 5433 -P"
cat postmaster.opts
/usr/lib/postgresql/12/bin/postgres "-D" "data2" "-p" "5433" "-P"
psql -p 5433
  • 接続できた
psql (12.1 (Debian 12.1-1.pgdg100+1))
Type "help" for help.

postgres=# 
  • oid2nameで壊れたデータベースオブジェクトを特定
oid2name -p 5433 -o 2662
From database "postgres":
  Filenode          Table Name
------------------------------
      2662  pg_class_oid_index
  • pg_class_oid_indexREINDEXする
REINDEX (VERBOSE) INDEX pg_class_oid_index;
INFO:  index "pg_class_oid_index" was reindexed
DETAIL:  CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
REINDEX
  • 通常のオプションでPostgreSQL再起動
pg_ctl -D data2 stop
pg_ctl -D data2 start -o "-p 5433"
  • 接続確認
psql -p 5433
  • 接続できるようになった
psql (12.1 (Debian 12.1-1.pgdg100+1))
Type "help" for help.

postgres=# 

レプリケーション・ホットスタンバイ復旧

散々やったので略

障害対応向けオプション

  • 公式/開発者向けオプション
  • ignore_system_indexes

    • onにすると、システムテーブルを読むとき、インデックスを無視する

      • システムカタログのインデックスが破損して接続できなくなったときなどに
  • zero_damaged_pages

    • onにすると、破損したページを0埋めして、最低限のデータをサルベージできるようにする

      • 埋められた部分のデータは当然失われる
      • バックアップを取っていなかった・バックアップも失われた場合などに