シグナルを読み、まず何に手を付けるか決める
目標
データベース障害で最も高くつく失敗は、被害者を原因と取り違えることです。 ブロックされているセッション4つを終了させても、またブロックされ、肝心のブロックしている1つはそのまま残ります。
ここでは、実際の形のスナップショットを読んで、何から手を付けるかを決めます。
用意するもの
/opt/lab/dbsignals/activity.tsv pg_stat_activity 스냅숏 30줄
/opt/lab/dbsignals/locks.tsv 누가 누구를 막고 있는가
/opt/lab/dbsignals/statements.tsv pg_stat_statements 상위 질의
mkdir -p /root/dbsignals && cp /opt/lab/dbsignals/* /root/dbsignals/ && cd /root/dbsignals
column -t -s $'\t' activity.tsv | head -5
残すもの
01-states.txt 상태별 개수
02-waits.txt 무엇을 기다리는가
03-root.txt 사슬의 뿌리
04-statements.txt 총 시간과 평균 시간
05-decide.md 지금 할 것과 나중에 할 것
06-prevent.md 다시 안 생기게
07-notes.md 왜 그런지
今、何が動いているか
activity.tsvから状態別の件数を数え、idle in transactionのうち最も古いものが何秒かを01-states.txtに書いてください。その状態がなぜ危険なのかも1行書きます。
awk -F'\t' 'NR>1 {print $3}' activity.tsv | sort | uniq -c
idleは単に何もしていない接続なので、たいてい問題ではありません。idle in transactionは違います。トランザクションを開いたまま何もしていないため、その間に握っているロックが解放されず、クリーンアップ(vacuum)もその地点を越えられません。
何を待っているか
activeのものだけを選んでwait_event_type別に数えて02-waits.txtに書き、Lock待ちとIO待ちで対処法がどう違うかを書いてください。
awk -F'\t' 'NR>1 && $3=="active" {print ($4=="" ? "(없음)" : $4)}' activity.tsv | sort | uniq -c
待機タイプが空のものは、実際にCPUを使って動いているものです。
Lockは別のセッションが解放してくれないと解決しないので、その別のセッションを見る必要があります。IOはストレージが遅いか、キャッシュにないためなので、クエリやハードウェアを見る必要があります。同じ「遅い」でも、見る場所がまったく違います。
チェーンの根元を探す
locks.tsvを見て、誰がブロックされていて、誰がブロックしているかを03-root.txtに書いてください。根元のセッションが何をしているかも一緒に書きます。
ブロックされているものたちは被害者です。それらを終了させても、またブロックされます。
根元が見つかったら、activity.tsvでそのpidがどんな状態で、最後にどんなクエリを投げたかを見てください。前のステップで見たものとつながります。
合計時間と平均時間は別の問題
statements.tsvから、合計時間が最も大きいクエリと呼び出しが非常に多くて合計が大きいクエリをそれぞれ探して04-statements.txtに書き、2つの対処法がどう違うかを書いてください。
平均が3秒のクエリと、平均が0.2msのクエリがあり、後者が480万回呼ばれると、合計は同じくらいになります。
平均が大きいものは、そのクエリ1つを直します(インデックス、集計の事前計算)。呼び出しが多いものは、クエリではなく呼び出す側を直します(N+1、キャッシュ、バッチ)。
今すること、あとですること
今この瞬間に何をするかを05-decide.mdに書いてください。今手を付けるもの、近いうちに見るもの、あとで直すものの3つに分け、それぞれの理由を書きます。
今やることは1つです。そのセッションをキャンセルするか切断するかも決めてください。
pg_cancel_backend(pid): 今実行中のクエリだけをキャンセルします。接続は残りますpg_terminate_backend(pid): 接続を切断します。開いているトランザクションもロールバックされます
idle in transactionは実行中のクエリがないため、キャンセルでは解決しません。
再発を防ぐ
同じ事故が再び起きないようにする設定とアラートを06-prevent.mdに書いてください。値は数字で決めます。
idle_in_transaction_session_timeoutが、この事故を丸ごと防ぎます。lock_timeoutとstatement_timeoutも一緒に決めてください。
「有効にしてください」という言葉だけでは、誰も有効にできません。値を決めて渡して初めて、実行に移せます。
設定だけでは次の事故を見つけられません。何を監視するかも書いてください。最も古いトランザクションの経過時間のようなものです。
次に読む人へ
ここで見たもののうち4つ以上を選び、07-notes.mdにまとめてください。何をしたかではなく、なぜそうなのかを書きます。
明け方にページを受けた自分が読むと考えてください。「pg_stat_activityを見た」は役に立ちませんが、「ブロックされているものは被害者です。終了させてもまたブロックされます」は役に立ちます。