我正在阅读不同的事务隔离级别,并且跨越SERIALIZABLE
隔离级别。我也知道像Postgres,Oracle和MySQL这样的数据库支持SELECT .. FOR UPDATE
语法。
过去使用JPA时,我总是在查询中使用@Transactional
和LockModeType.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)
)?答案 0 :(得分:3)
SERIALIZABLE与使用SELECT FOR UPDATE之间的主要区别在于,SERIALIZABLE始终锁定所有内容。与SELECT FOR UPDATE一样,您可以选择锁定的内容和时间。
因此,如果您只想锁定某些数据,即BankAccount而不是其他数据,即Branch,AccountTypes,则SELECT FOR UPDATE会为您提供更好的控制,因为SERIALIZABLE会阻止整个系统,因为每个事务都是从ACCOUNT_TYPES表中选择的。此外,某些交易可能只是想检查余额,因此不需要锁定ACCOUNT表。
请参阅,
答案 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
更简单,而且锁定可能会产生大量等待事务,从而导致数据库负载增加。