当前位置: 首页 > 知识库问答 >
问题:

乐观锁定绝对安全吗?

邢浩邈
2023-03-14

当使用乐观锁定策略时,它可以解决如下并发问题:

| the first transaction started        |
|                                      |  
| select a row                         |
|                                      |  the second transaction started
| update the row with version checking |
|                                      |  select the same row 
| commit txn                           |
|                                      |  update the row with version checking
|                                      |  
|                                      |  rolls back because version is dirty

但是,如果在极少数情况下,如果第二个事务中的更新是在第一个事务中的udpate之后但在事务提交之前,该怎么办?

| the first transaction started        |
|                                      |  the second transaction started
| select a row                         |
|                                      |  select the same row 
| update the row with version checking |
|                                      |  update the row with version checking
| commit txn                           |
|                                      |  rolls back because version is dirty // will it?
|                                      |  
|                                      |  

我做了一个实验,第二个事务中的更新无法读取“脏”版本,因为第一个事务尚未提交。在这种情况下,第二次交易会失败吗?

共有2个答案

裴兴言
2023-03-14

正如您已经发现的,乐观锁受制于TOCTOU竞争条件:在提交决定和实际提交之前,有一个短暂的时间窗口,在此期间另一个事务可以修改数据。

为了使乐观锁定100%安全,您必须确保第二个事务一直等到第一个事务提交,然后才进行版本检查:

您可以通过在更新语句之前获取行级(选择更新)锁来实现这一点。

jOOQ为您做到这一点。在Hibernate中,您必须手动锁定该行:

var pessimisticRead = new LockOptions(LockMode.PESSIMISTIC_READ);
session.buildLockRequest(pessimisticRead).lock(entity);

请注意,您无法在单个VM上重现HiberNate中恼人的TOCTOU竞争条件。由于共享的持久上下文,HiberNate将顺利解决此问题。当事务在不同的VM上运行时,HiberNate无法提供帮助,您必须添加额外的锁定。

单昊穹
2023-03-14

您在问题中没有说您实际使用的数据库系统,因此我不知道您的系统的详细信息。

但无论如何,在乐观锁定系统下,进程在执行更新语句时不能只检查行版本,因为这正是您担心的问题。

对于完全可序列化、隔离的事务,每个进程必须在提交时自动检查它检查和修改的所有行的行版本。因此,在您的第二个场景中,右手进程在尝试提交之前不会检测到冲突(您没有为右手进程包含这一步骤)。当它尝试提交时,它将检测到冲突并回滚。

 类似资料:
  • 我们有一个系统,我们偶尔会得到一个乐观的锁定异常。我们在代码中已经解决了这个问题,但现在我正在查看JPA 2,并看到它有一个用于处理这个问题的注释(@版本) 我们的问题是,一个表上有多个事务,如果表锁已满,则即使未对相同的记录进行更改,也会导致乐观锁定异常。 我们在JBoss 4.2服务器上使用hibernate,数据库可以是MySQL或SQL服务器。 如果改为使用@Version,这会在两个数据

  • 在多用户环境中,在同一时间可能会有多个用户更新相同的记录,会产生冲突,解决方案有两种:乐观锁、悲观锁。 悲观锁在这里不讲,自行Google。 乐观锁假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,不完整则更新失败。 乐观锁实现方式 使用整数表示数据版本号.更新时检查版本号是否一致,如果相等,则更新成功,且版本号+1.如果不等,则数据已经被修改过,更新失败。 使用时间戳来实现。 本质上也

  • 我使用SpringDataJPA和Hibernate作为PostgreSQL上的持久性提供者。我试图提供悲观锁定: 我尝试从两个线程中调用< code>findOneAndLock。我认为,如果< code >线程A锁定了对象,那么< code >线程B应该等到锁被释放。而是< code >线程B抛出< code > org . spring framework . ORM . objectopt

  • 问题内容: 我找不到有关MySQL中乐观锁定的任何详细信息。我读到开始事务使两个实体上的更新保持同步,但是,它不会停止两个用户同时更新数据而引起冲突。 显然乐观锁定会解决这个问题吗?这在MySQL中如何应用。是否有SQL语法/关键字呢?还是MySQL具有默认行为? 谢谢你们。 问题答案: 关键是,乐观锁定不是数据库功能,不适用于MySQL或其他功能:乐观锁定是一种使用带有标准指令的DB进行的实践。

  • Sequelize 内置支持通过模型实例版本计数进行乐观锁定. 乐观锁定默认情况下处于禁用状态,可以通过在特定模型定义或全局模型配置中将 version 属性设置为 true 来启用. 有关更多详细信息,请参见模型基础. 乐观锁定允许并发访问模型记录以进行编辑,并防止冲突覆盖数据. 它通过检查自从读取以来另一个进程是否对记录进行了更改,并在检测到冲突时抛出 OptimisticLockError

  • 我有一个带有和的微服务。我使用注释对中的表执行操作。我遇到的问题是保存操作花费了太多的时间,所以我希望读取操作不被它阻塞。注意,保存是通过持久化实体来执行的。 当前,在所有并发保存操作完成之前,所有读取操作都不会返回结果,这意味着表被锁定。相反,我希望实现乐观锁定。我怎样才能做到呢?