PHP
MySQL
cron
DeadLock
retry

cron+retry+DBで地獄を見た件。

More than 1 year has passed since last update.

概要

アプリケーションにおいてそこで実装された機能の処理とcronにおいてデータベースの更新をすると、処理がかぶり、デッドロックが起こる場合がある。そうして負けた方のロールバックに巻き込まれてデータに不整合が起こるといった件で地獄を見たので共有。

起こった要因

ロックしている時間が長い

大量のINSERTをする+実行計画がindexである場合など様々スロークエリの状況が揃った場合に起こりやすくなっている。

Insertをした時にretryの処理を入れている(特にcron側)

あまり良くないとわかっていてもシステムを落とさないでしっかり動作させるためにこの処理入れているところもあるのではなかろうか。これが地獄を見た主たる原因。

改善策

  • SQLの実行計画の改善 => 処理の時間を短くすることでかぶる確率を下げる
  • cronの頻度を減らす => 上記に同じ。かぶる確率を下げる
  • retryをしない => 処理を落とさないためにその他施策を打つ必要はある

データがどういうことになってしまったか

アプリ側でINSERTなどをテーブルA, B, Cをロックしているとする。
その後、cron側がテーブルAとテーブルCをJOINしてSELECT->INSERTしに行こうとする。
しかしこの時、アプリ側でDBをロックしているのでROLL BACKが走る。
この時にリトライなどの処理を入れてしまうと、テーブルBのみINSERTされるといった現象が起きてしまう。ただ、INSERTはされているので、アプリの処理の書き方によっては、正常終了とみなされる。

処理 テーブルA テーブルB テーブルC
cronの処理 アクセス不可 アクセス不可
アプリの処理 ロック ロック ロック