2
0

SELECT文でデッドロックするパターン

Posted at

SELECT文でデッドロックになるパターン

PostgreSQL+Springを使用しているシステムにて、いくつかの特殊な状況が重なりデッドロックが発生した。
ざっくり言うと以下の順序で処理が発生した結果、デッドロックが発生。

  • トランザクションAがSELECT * FROM user_attr;
  • トランザクションBがTRUNCATE user_attr;
  • トランザクションAを発生させたJavaプログラムが別のトランザクションCを発生させ、SELECT * FROM user_attr;

PostgreSQLのTRUNCATE周りの仕様

PostgreSQLのTRUNCATEはOracle/MySQLと違いロールバックが可能。
ただし、TRUNCATEの実行によりAccessExclusiveLockを獲得し、TRUNCATE実行中のSELECT文をブロックする。

PostgreSQLのSELECT仕様

SELECT文を実行すると、AccessShareLockを獲得する。
これはAccessExclusiveLock(=TRUNCATE)をブロックし、ブロックされるもの。
AccessShareLockは(ほかのロックもだけど)トランザクション終了まで保持し続ける。

Springにより1リクエスト当たり複数のトランザクションを発行するケース

TransactionalアノテーションのオプションであるPropagationの設定により、1リクエストの中で複数のトランザクションを発行する場合がある。

例えば業務処理Aと、ログ記録処理Bがあった場合、Aの結果に関わらずBをコミットした場合等に利用される。

今回はPropagation.REQUIRES_NEWを使用し、ログ記録処理を行っていた。

起こったこと

TRUNCATE実行時に、TRUNCATEが完了せず待ち状態に。
上記で言うところの業務処理A実行後、ログ記録処理B実行前にTRUNCATEが差し込まれた状態となり、デッドロック。
ロックの一端をJavaが握っている関係で、PostgreSQLのdeadlock_timeoutの検知が効かず、全てのリクエストがログ記録待ちでストップ。

対策

  • そもそも運用中にTRUNCATEを使わない
  • Propagation.REQUIRES_NEWを濫用しない。使う場合は通常のデッドロックにも注意が必要
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0