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

乐观锁定和重试

丁高峯
2023-03-14

我不确定方法的设计是否恰当。

我们使用乐观锁定,将long增量版本放置在每个实体上。这种实体的每次更新都是通过比较和交换算法执行的,该算法的成功与否取决于其他客户端是否同时更新实体。经典的乐观锁定,如hibernate。

我们还需要采取重新尝试的方法。我们使用基于< code>http的存储(etcd ),某些更新请求可能会超时。

这就是问题所在。如何结合乐观锁定和重试?这是我面临的具体问题。

假设我有一个具有version=1的实体,我正在尝试更新它。下一个版本显然是2。我的客户端执行条件更新。只有当持久性版本为1并且它被原子更新为version=2时,它才被成功执行。到目前为止,一切都很好。

现在,假设更新请求的响应没有到达。现在还说不准成功与否。我现在唯一能做的就是再次尝试更新。在内存中,实体仍然包含< code>version=1,打算将值更新为< code>2。

真正的问题现在出现了。如果第二次更新失败,因为持久性版本是2而不是1,该怎么办?

有两个可能的原因:

  1. 第一个请求确实导致了更新 - 操作成功,但响应丢失或我的客户端超时等等。它只是没有到达,但它通过了
  2. 其他客户端在后台同时执行更新

现在我不能说什么是真的。我的客户端是否更新了实体或其他客户端?操作是通过还是失败?

当前我们使用的方法只是比较持久化的实体和主内存中的实体。要么是 java 相等,要么是 json 内容相等。如果它们相等,则更新方法声明为成功。我对算法不满意,因为它对我来说既便宜又不合理。

另一种可能的方法是不使用版本,而是使用时间戳。每个客户端都在更新操作中生成自己的时间戳,这意味着潜在的并发客户端将很有可能生成其他客户端。对我来说,问题是概率,尤其是当两个并发更新来自同一台机器时。

还有其他解决办法

共有2个答案

东门彬
2023-03-14

恕我直言,由于etcd是基于HTTP构建的,而HTTP本质上是一种不安全的协议,因此很难有一个防弹的解决方案。

传统的SQL数据库使用连接的协议、事务和日志记录来允许用户确保事务作为一个整体将被完全提交或完全回滚,即使在操作过程中断电的最坏情况下也是如此。

因此,如果两个操作相互依赖(资金从一个银行帐户转移到另一个),您可以确保两个操作都可以或都没有,并且您可以简单地在数据库中实现一个“操作”日志,其中包含它们的状态,以便以后能够通过查看日志来查看是否通过了一个特定的操作,即使您在提交过程中断开了连接。

但是我无法想象< code>etcd会有这样的解决方案。所以除非有人找到更好的方法,否则你只有两个选择

  • 在后端使用经典的SQL数据库,使用etcd(或等效的)作为简单的缓存
  • 接受协议的弱点

顺便说一句,我不认为时间戳代替长版本号会增强系统,因为在高负载下,两个客户端事务使用相同时间戳的概率会增加。也许你可以尝试在你的字段中添加一个唯一的id(客户端id或者仅仅是技术uuid),当版本为n1时,只需比较增加了它的uuid:如果是你的,那么如果不是id,那么事务就通过了。

但是,如果目前您可以阅读版本,那么真正更糟糕的问题会出现,它不是在 n 1 处,而是已经在 n 2 处。如果UUID是你的,你确定你的交易通过了,但如果不是,没有人可以说。

归鹤龄
2023-03-14

您可以使用两步协议在etcd中伪造交易。

更新算法:

第一阶段:将更新记录到etcd

  • 添加一个TTL相当小的“update-lock”节点。如果存在,请等到它消失后重试。
  • 将看门狗添加到您的代码中。如果执行下一步所需的时间超过锁的TTL(或者如果您未能刷新它),则必须中止。
  • 添加一个带有[old, new]值的“update-plan”节点。它的结构取决于您,但您需要确保在持有锁时复制旧值。
  • 添加一个“提交更新”节点。此时您已经“原子地”更新了数据。

第二阶段:执行实际更新

  • 读取“计划更新”节点并应用它描述的更改。
    • 如果更改失败,请验证是否存在新值。
    • 如果不是,你就有大问题了。保释。

    如果要读取一致的数据:

    >

  • 虽然没有提交更新节点,但您的数据正常。
  • 否则,请等待它被删除。

    • 每当提交更新存在但更新锁定不存在时,启动恢复。

    事务恢复,如果您发现没有锁的更新计划节点:

      < li >获取更新锁。 < li >如果没有提交的更新节点,请删除该计划并释放锁定。 < li >否则,继续上面的“第二阶段”。

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

  • 当我试图理解基于版本的乐观锁定如何防止“最后提交获胜”问题和适当的覆盖时,我遇到了困难。 为了使问题更具体,让我们考虑以下使用 JDBC 的伪代码: 这里,如果其他事务会在更新和提交之间提交其更改呢?如果事务是并发的,那么第一个事务所做的更新尚未提交,因此第二个事务不可见(具有适当的隔离级别),因此第一个事务提交将覆盖第二个交易的更改,而不会出现任何通知或错误。 乐观锁定只是降低了问题发生的概率,

  • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁与悲观锁的具体区别: http://www.cnblogs.com/Bob-FD/p/3352216.html

  • 我在Oracle的博客上读到一篇关于JPA和锁定模式的文章。 我不完全理解和锁定模式类型之间的区别。 模式: 当用户选择这种模式时,他必须将EntityManager的状态flush()放到数据库中,以手动增加版本字段。因此,所有其他乐观事务都将失效(回滚)。在事务结束时还会对版本进行检查,以提交或回滚事务。 这似乎很清楚,但什么时候应该使用模式与模式?我看到的唯一标准是,当我希望事务优先于其他事

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

  • 本文向大家介绍说一下乐观锁和悲观锁?相关面试题,主要包含被问及说一下乐观锁和悲观锁?时的应答技巧和注意事项,需要的朋友参考一下 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。 数据库的乐观锁需要自