可序列化事务与SELECT FOR UPDATE

时间:2013-06-06 08:50:43

标签: database jpa

我正在阅读不同的事务隔离级别,并且跨越SERIALIZABLE隔离级别。我也知道像Postgres,Oracle和MySQL这样的数据库支持SELECT .. FOR UPDATE语法。

然而,当我想要锁定我想要执行更新的行(或一系列行)时,我感到很困惑。

过去使用JPA时,我总是在查询中使用@TransactionalLockModeType.PESSIMISTIC_WRITE。这转换为在SQL中使用READ_COMMITTED隔离级别SELECT .. FOR UPDATE

但现在,在阅读SERIALIZABLE之后,我想知道如果我将@Transactional(isolation=SERIALIZABLE)与正常SELECT一起使用会有什么不同(例如 em.findById < / em>获取一个分离的实体),然后是UPDATE(实体的合并)。

行为是否相同?

比如说,我有一个银行系统,我希望在两个账户之间转账。转移正在进行中,我要求不要干涉这些帐户。因此,假设我用-100借记​​一个账户并将其存入另一个账户。确保这些帐户仅可用于执行更新的交易的最佳方法是什么?

假设我正在操作JPA分离的实体,所以在更新之前,我将不得不从数据库中读取它们,例如 findById()

  • 使用@Transactional(isolation=READ_COMMITTED) em.findById LockModeType.PESSIMISTIC_WRITE(即SELECT .. FOR UPDATE),然后 em.merge (即{ {1}})?
  • 或使用UPDATE em.findById ,然后 em.merge (即@Transactional(isolation=SERIALIZABLE))?

3 个答案:

答案 0 :(得分:3)

SERIALIZABLE与使用SELECT FOR UPDATE之间的主要区别在于,SERIALIZABLE始终锁定所有内容。与SELECT FOR UPDATE一样,您可以选择锁定的内容和时间。

因此,如果您只想锁定某些数据,即BankAccount而不是其他数据,即Branch,AccountTypes,则SELECT FOR UPDATE会为您提供更好的控制,因为SERIALIZABLE会阻止整个系统,因为每个事务都是从ACCOUNT_TYPES表中选择的。此外,某些交易可能只是想检查余额,因此不需要锁定ACCOUNT表。

请参阅,

http://en.wikibooks.org/wiki/Java_Persistence/Locking

答案 1 :(得分:1)

在我看来,SERIALIZABLE无法在此商业交易中工作,因为您需要在选择实体后检查一些条件(例如,如果帐户有足够的钱)。并发事务可以使用SERIALIZABLE级别获取错误的值,因为它为SELECT保存了共享(读取)锁。

但SELECT ... FOR UPDATE将正常工作,因为它将持有一个独占锁,直到事务结束,并将强制其他事务等待。

答案 2 :(得分:0)

select ... for update 用于行锁定。这将导致尝试对已锁定的行(以及每个引用的外键列)执行 select ... for update 的其他事务在继续之前等待锁定事务完成。锁定并发更新的资源应该适用于银行余额更新场景,在更新时锁定它,其他使用 select ... for update 修改相同资源的业务逻辑事务将不得不等待并轮流进行。

Serializable 另一方面防止 lost updates 通过处理并发事务提交有效性,就好像它们是按顺序提交的,因此验证冲突的更改。对于银行转账扣除的情况,使用 PostgreSQL,第一个提交的事务将获胜,而其他在同一余额上更新的提交事务在提交时将失败。处理失败的提交将由客户端代码负责。

我个人更喜欢在冲突更改失败时使用 Serializable 结合幂等自动重试,然后检查更新计数以了解更新是否成功,然后进行适当处理。这比必须记住在多个地方使用 for lock 更简单,而且锁定可能会产生大量等待事务,从而导致数据库负载增加。