3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Java @Transactionalを付けたのに不整合が起きるのはなぜ?

3
Posted at

@Transactionalとは

@Transactionalは、複数のDB操作を1つのトランザクションとして実行するためのアノテーションです。

処理が正常に終了すればコミットされ、途中で例外が発生するとまとめてロールバックされます。

つまり、複数の更新処理を「全部成功」または「全部失敗」にするための仕組みです。

@Transactionalが守るもの

@Transactionalは、複数のDB操作の整合性を守ります。

例えば、2つの更新処理がある場合、

出金

入金

の途中で例外が発生しても、出金だけが反映されることはありません。

○ 両方成功
× 両方失敗(ロールバック)

のどちらかになるため、データの不整合を防ぐことができます。

つまり@Transactionalが守るのは、処理全体の一貫性(整合性)です。

@Transactionalが守らないもの

@Transactionalは、同時実行による競合を防ぐものではありません。

例えば、口座残高が100円の状態で、2つの出金処理(80円)が同時に実行されたとします。

残高:100

A
残高取得 → 100

B
残高取得 → 100

A
80円出金

B
80円出金

AもBも「残高は100円ある」と判断して処理を進めるため、意図しない結果になることがあります。

@Transactionalを付けていても、複数のトランザクションが同じデータを読み取り、それぞれ更新を行うことは防げません。

つまり@Transactionalは、処理の整合性は守りますが、並行実行時の競合までは防ぎません。

並行実行の対策方法

並行実行による競合を防ぐには、@Transactionalとは別の対策が必要です。

代表的な方法として、以下があります。

  • 更新条件をSQLに含める(Atomic Update)
  • 楽観ロック(@Version
  • 悲観ロック(PESSIMISTIC_WRITE

例えば残高更新であれば、

UPDATE users
SET balance = balance - 80
WHERE id = 1
  AND balance >= 80

のように、チェックと更新を1回のSQLで行う方法がよく利用されます。

どの方法を選ぶかは要件によって異なりますが、重要なのは @Transactionalだけでは並行実行時の競合は防げない という点です。

まとめ

@Transactionalは複数のDB操作を1つの処理として扱い、途中で失敗した場合にロールバックする仕組みです。一方で、並行実行による競合は防げないため、必要に応じてロックやAtomic Updateなどの対策を組み合わせる必要があります。

3
5
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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?