18

我正在阅读不同的事务隔离级别,并遇到了SERIALIZABLE隔离级别。我也知道 Postgres、Oracle 和 MySQL 等数据库支持这种SELECT .. FOR UPDATE语法。

然而,当我想锁定我希望对其执行更新的一行(或一系列行)时,我很困惑如何使用这些。

过去在使用JPA 的时候,我总是用@Transactional加上一个LockModeType.PESSIMISTIC_WRITE就查询。这转化为在 SQL中使用READ_COMMITTED隔离级别。SELECT .. FOR UPDATE

但是现在,在阅读了 aboutSERIALIZABLE之后,我想知道如果我使用@Transactional(isolation=SERIALIZABLE)一个普通的SELECT(例如em.findById来获取一个分离的实体),然后是一个UPDATE(实体的合并)会有什么不同。

行为会一样吗?

例如,我有一个银行系统,我希望在两个账户之间转账。我要求在转移过程中不要干预这些帐户。所以,假设我从一个账户中扣除 -100 并将其记入另一个账户。确保这些帐户仅对执行更新的交易可用的最佳方法是什么?

假设我正在操作 JPA 分离实体,因此在更新之前,我必须从数据库中读取它们,例如findById()

  • @Transactional(isolation=READ_COMMITTED), em.findByIdLockModeType.PESSIMISTIC_WRITE(ie SELECT .. FOR UPDATE) 一起使用,然后使用 em.merge (ie UPDATE) ?
  • 或使用em.findById@Transactional(isolation=SERIALIZABLE),然后使用em.merge(即)?UPDATE
4

4 回答 4

6

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

于 2013-06-06T13:44:01.470 回答
2

在我看来 SERIALIZABLE 不能在这个 BUSINESS 事务中工作,因为你需要在选择一个实体后检查一些条件(例如,如果一个帐户有足够的钱)。并发事务可以使用 SERIALIZABLE 级别获得错误的值,因为它持有 SELECT 的共享(读取)锁。

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

于 2015-06-03T10:54:39.813 回答
0

如果使用for update,则指定要锁定的行。如果您有多个读取操作,您可以锁定属于事务中特定读取操作的行。另一方面,可序列化总是锁定行不被并发读取

于 2022-01-19T01:50:56.083 回答
0

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

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

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

于 2021-05-31T08:16:36.480 回答