什么情况下需要分布式锁_在没有分布式交易的情况下进行

本文探讨了在云环境中处理分布式事务的挑战与策略,包括最终一致性、补偿事务、乐观与悲观锁定,以及幂等性在确保跨多个资源一致性中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在本系列的第1部分中,您了解了事务处理和分布式事务。 事务属性有助于减少应用程序所需的错误处理工作。 但是,管理跨多个资源的事务的分布式事务不一定在基于云的运行时中可用。 在第2部分中,我们研究了云设置,以及即使没有分布式事务也能确保跨多个资源管理器的事务质量的方法。

在云设置中,与传统设置相比,分布式事务的皱眉甚至更多。 资源(例如数据库的资源管理器)和事务管理器都可能是易变的。 它们可以随时启动或停止以适应不同的负载情况。 重新启动资源管理器会中止其中当前活动的所有事务,但是处于分布式事务的表决阶段中间的事务管理器如何处理? 如果没有持久的事务管理器日志,事务将陷入混乱之中,为这些事务锁定的资源将永远不会被其资源管理器释放。

同样在云设置以及某些面向服务的体系结构(SOA)设置中,用于调用资源管理器的协议经常不支持诸如REST over HTTP调用之类的事务。 使用这样的协议,呼叫等效于自动提交的操作,如第1部分所述。

云使快速部署新版本或快速扩展应用程序变得更加容易,但是这会使应用程序程序员更加困难。 使用您在此处了解的有关事务管理的知识,可以帮助您创建在云中更好地运行的应用程序。

因此,尽管您可能可以在单独的存储基础结构上使用共享的持久性事务日志在云中设置应用程序服务器,以补偿不稳定的操作,但许多流行的通信协议并不支持事务的使用。

这意味着在云设置中,您必须没有分布式事务。 以下各节介绍了用于在非事务性设置中协调多个资源的多种技术。

在云中处理分布式事务的方法

事务处理的基本假设是,您希望多个资源彼此一致。 一个资源的更改应与另一个资源的更改一致。 如果没有分布式事务,则这些错误处理模式可用于多种资源:

  • 仅在一种资源中执行更改。 如果从功能的角度来看这种情况是可以接受的,则可以自动提交事务,并且可以记录和忽略其他资源中的错误情况。
  • 当更改第二个资源时发生错误时,将回退第一个资源中的更改。 这就要求在发生错误时,第一个资源上的事务仍处于打开状态,以便可以回滚。 或者,当两个资源上的事务都保持打开状态,并且在两个事务之一的第一次提交时发生错误时,外部看不到第一个资源的回滚更改,因此回滚时不会出现不一致的窗口出现。
  • 一个资源被更改,并且在稍后的时间点,第二个资源被更改为与第一个资源一致。 这可能是由于在更改第二资源时发生的可恢复错误情况,但也可能是一种架构模式,无论如何(例如在批处理中)都会推迟第二资源的更改。 这种模式创建了一个(更大)不一致的窗口,但最终最终实现了一致性。 因此,这种模式称为最终一致性 。 我将其称为前滚不一致,因为第二个更改已提交。
  • 当更改第二资源时发生错误时,将补偿第一资源中的更改。 对于此模式,第一个资源需要提供补偿事务以回滚单独事务中的更改。 这也会产生最终的一致性,但是错误模式与上面的前滚不一致有所不同。 我将其称为回滚不一致 ,因为第一个更改已回滚。

在下一部分中,我将介绍采用最终一致性属性的技术。 该系统不会立即保持一致,但是应该在提交事务后立即保持一致。

缓解技术

如何减少错误条件,以使错误很少发生或根本不发生?

忽略-稍后再修复

当然,最简单的方法是忽略任何错误情况,然后再通过批处理作业对其进行修复。 但是,这可能并不像看起来那样容易。 考虑一个在一个数据库中具有购物车和订单处理服务的商店系统。 如果订购服务不可用并且您将其忽略,则需要执行以下操作:

  • 在购物车上保持尚未成功发送订单的状态。
  • 考虑直到批处理解决该问题所需的时间。 如果仅在晚上运行,则可能会浪费一天的时间,直到后端进行订单处理,可能会导致客户不满意。

图1说明了最终一致性批处理解决不一致问题的示例。

图1.批处理作业修复不一致
该图显示批处理作业修复不一致

此主题的一种变体是将“作业”写入数据库,并使后台进程不断尝试交付订单直到成功。 在这种情况下,您实际上将在构建自己的事务日志和事务管理器。

基于时间的悲观锁定

悲观锁定是一种允许事务确保要更改的资源实际可供他们使用的技术。 悲观锁定可防止其他事务修改资源(如数据库行),有时甚至在短暂的技术事务中也是如此。

在第1部分中描述的长期运行的业务事务中,初始的短期运行的事务会更改资源的状态,以便其他事务无法再次锁定它。 在第1部分的信用卡示例中,您看到了这样的技术可以成功地在长期运行的业务交易中大规模使用。

该技术也可以在云设置中使用,但是有一个警告:您需要确保在不再需要该锁时将其释放。 您可以通过在对象上成功提交更改或回滚事务来做到这一点。 基本上,您需要在事务客户端或事务管理器中编写和处理事务日志,以确保即使发生其他可能的错误也可以释放锁定。

如果您不能保证一定会释放该锁,则可以使用基于时间的悲观锁(一种在给定时间间隔后释放的锁)作为最后的选择。 可以使用简单的时间戳在资源上实现这种锁定,因此不需要批处理作业。

锁会在事务客户端和资源管理器之间引入共享状态。 需要识别锁并将其与事务关联; 因此,您需要在系统之间传递锁标识符或事务标识符,以解决提交或回滚时的锁。

补偿金

与悲观锁定相反,乐观锁定不是避免错误的技术; 其目标是检测不一致之处。 尽管悲观锁定为事务保留了资源,但更乐观的方法是假定没有不一致,并在第一步中直接对事务执行本地更改。 但是,如果确实发生不一致,那么您就需要能够回滚更改。 这是对乐观执行的操作的补偿 。

从理论上讲,这是一种不错的方法,因为您只需要在回滚操作时调用补偿服务操作即可。 但是,例如,在JEE应用程序中,资源管理器可以决定在尝试commit()时回滚,而不是在执行更改时回滚,因为某些数据库约束只能在提交时检查。 结果是您需要编写自己的Java资源适配器,因为只有这样,您才能确定是否已回滚或提交了另一资源上的事务。 但这又引入了分布式事务,出于上述原因,您希望在云设置中避免这种事务。 另外,您需要确定由于某种原因而导致补偿失败的情况。

更好的方法是在某种事务日志中记录失败,例如,在单独的(自动提交事务)操作中将一行写入特定表。 然后,您可以让批处理作业在以后处理补偿。 但是,当第二个更改在被叫服务处被完全处理,但呼叫者没有收到响应时,会发生问题,如下一节所示。 如果然后补偿了第一个资源,则您生成了另一个不一致的地方。 如果应避免这种情况,则应在尝试补偿第一个更改之前检查第二个资源的状态。

正如您在此处看到的,讨论所有可能的错误模式是一项繁琐但必要的工作。 只有一种更具体的情况有待讨论:当操作实际成功时,但调用者未收到答复并认为有错误。

重复性(幂等)

再次考虑购物车和订购服务。 假设已经调用了订单服务,但是网络上缺少答复。 呼叫者不知道订单服务呼叫是否成功,然后重试。 当然,不应两次处理订单,那么该怎么办?

图2.服务运营必须管理“双重提交”问题
该图显示了双重提交问题的处理

图2说明了这种情况。 此问题类似于Web应用程序中的“双重提交”问题,其中不小心双击了一个按钮,并且两次将相同的请求发送到服务器。 如果服务器响应资源管理器检测到此重复呼叫怎么办? 在我们的示例中,订单服务必须将第二个呼叫标识为重复呼叫,并仅返回第一个呼叫结果的副本; 实际订单仅处理一次,订单服务是可重复的 。

服务的可重复性极大地帮助减少了没有分布式事务的设置中错误处理的复杂性。 这意味着该服务是幂等的 :您可以重复多次该服务调用,并且结果总是好像该服务仅被调用一次。 但是,当然,这应该只发生在相同的顺序上。 即使单独的订单具有相同的内容,也应将其作为新订单处理。

可以通过比较服务调用的所有属性来实现此解决方案。 例如,我的银行拒绝了第二笔交易,其金额,主题和收款人与第一笔相同,以防万一我偶然在网上银行应用程序中两次输入了一笔交易。

更强大的是,与请求一起发送的事务标识符可以帮助检测重复提交。 在这种情况下,被调用的服务操作将注册由调用方发送的事务ID,并且当再次使用相同的ID时,它仅返回原始结果。 但是,这可能会在群集中的服务实例之间引入共享状态(事务ID),因此对另一个群集成员的重复调用仍将仅处理一次,从而造成另一个一致性问题。

手动交易和错误处理

由于缺乏分布式事务,因此系统需要查看每个单独的操作并决定如何以确保一致性的方式来解决它。 调用多个资源管理器(每个可能在其自己的事务中)的客户端必须仔细确定首先提交哪个资源管理器,以及在发生错误时如何与其他资源管理器一起处理。 这由功能要求决定。 诸如Web服务之类的非事务性资源更加糟糕,因为它们无法回滚,只能得到补偿。 资源管理器过去在经典系统中为您做的事情现在必须为您自己做。

考虑我的一个项目中的示例:我们使用Web服务将一些记录存储在单独的系统中,稍后需要参考。 由于本地事务有时会出错,因此我们必须认真考虑错误发生的时间以及在何处调用补偿操作。 最后,我们使通话重复进行,并完全取消了补偿。 如果事务被回滚,则下一次尝试将再次写入(并覆盖)相同的记录,并且我们拥有了引用所需要的指针,而以前的尝试没有任何剩余。

在另一个示例中,我们被迫使用递增/递减Web服务保留另一个系统中主数据记录用法的引用计数器。 每当我们创建一个引用主数据记录的对象(即存储其ID)时,就必须增加计数器,并在删除对象时将其递减。 这引起了两个问题。 首先,该调用是一个Web服务,因此我们必须使用Java资源适配器来处理回滚。 即使这样,即使在回滚期间也会发生错误(例如,由于网络问题)。 其次(更糟的是),递减并不是100%的补偿。 如果该值达到零(例如在回滚中),则存在删除主数据记录的危险(当然,之后我们才发现)。 最后,直到创建对象时,我们最多进行计数,并且每当对此主数据记录进行操作时,便会在单独的(事务)日志表中进行记录。 然后一个单独的夜间批次固定计数器值。

像这样的例子很常见。 甚至eBay都使用了基本无交易的设计。 eBay的大多数数据库语句都是自动提交的(类似于Web服务调用)。 仅在某些定义明确的情况下,语句才组合到单个事务中(在该单个数据库上)。 不允许进行分布式交易。 (请参阅“ 可伸缩性最佳实践:来自eBay的经验教训 。”)

相关工作

在分布式复制数据库的相关方案中, CAP定理指出,在一致性,可用性和分区容忍这三种功能中,任何一次都只能实现两项。 尽管这是为单个分布式数据库定义的,但如上所述,它也可以应用于跨多个资源的事务。

放宽强一致性并允许最终的一致性窗口在“ BASE”范式中形式化:

  • asically 一个 vailable
  • 小号经常状态
  • Ëventual一致性

可用性优先于强一致性,这会导致软状态可能不是最新值,但最终可能会更新。 该原则适用于数据库区域,但也适用于部署在云中的Web服务或微服务。 但是,在分布式数据库中,数据库供应商负责保持系统(最终)的一致性。 在使用多个资源的应用程序中,应用程序必须自己照顾自己,如上所示。

新的最终一致性范例甚至可以在某些功能方案中接受。 例如,请参阅Google在“ 提供一致的用户体验并利用最终的一致性模型以扩展到大型数据集 ”中的讨论。

更进一步,您可以尝试找到并使用通过最终一致性是安全的算法。 最近提出的CALM定理认为,某些程序(“单调”的程序)可以在最终一致的基础上安全运行。 例如,如果使用读取操作添加计数器,先添加1,然后再写回,则很容易出现一致性问题。 但是,如果通过调用“增量”操作来添加计数器,则不会。 有关更多信息,请参见“ 今天的最终一致性:限制,扩展和超越 ”和“ CALM:一致性作为逻辑单调性 ”。

结论

不必执行分布式事务所保证的强一致性模型,会使普通程序员在某些方面变得更加艰难。 以前由事务管理器完成的工作现在必须由程序员完成,特别是在错误处理方面。 另一方面,程序员的工作也变得更加轻松:从一开始就实现最终一致性的编程使该程序具有更大的可扩展性和弹性,可以应对比程序员自己的数据中心分布得多的世界中的网络延迟和问题。 当然,并不是每个人都从eBay规模创建应用程序,但是分布式事务会产生大量开销。 (但是,我承认我希望我可以在一个事务中使用至少一个数据库和一个消息存储。)

那么,如何在没有分布式交易的情况下相处呢? 这里有一些建议:

  • 在新系统中,尝试找到可以解决您的业务问题但可以在最终一致性下工作的算法(请参阅CALM定理)。
  • 在强大的一致性要求上挑战业务需求。
  • 通过可重复(幂等)或提供适当的补偿,确保您调用的服务适应此体系结构。
  • 在现有系统中,使用此处介绍的技术,修改算法以使其更简单,并且减少系统调用的服务中出现故障的可能性。

云使快速部署新版本或扩展应用程序变得更加容易,但是这也会使事情变得更具挑战性。 使用您在这里学到的有关事务管理的知识,应该可以帮助您创建在云中更好地运行的应用程序。

非常感谢我的同事SvenHöfgen,JensFäustl和Martin Kunz对本系列的评论和评论。


翻译自: https://www.ibm.com/developerworks/cloud/library/cl-manage-cloud-transactions_2/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值