并发的最佳实践

时间:2014-03-10 21:49:48

标签: breeze

我正在使用breeze在我的SPA和网络服务器之间进行交互,并且可以使用一些有关并发“问题”的帮助。

只要更新是一个简单的对象,并发工作正常。但是,在处理分层实体时,问题就开始了。

例如,产品具有一些简单的属性,如代码,名称,描述以及一些导航属性。为简洁起见,我将仅讨论其中一个:产品包(具有id,代码,名称和productId的实体,这是产品的外键)。保存产品时,只有具有更改的实体才会发送到服务器,因此,如果我们将产品包添加到产品中,那么这只是此产品包实体。

当此产品由2个人同时编辑时,可能会导致问题,例如由于产品包装上发生了一些业务验证。

产品本身具有“版本”属性,因此一个选项可能是在产品包列表(或其他导航属性之一)发生更新时手动将产品实体状态设置为已修改,但是这将需要为每个动作发送产品实体(不是它需要那么多带宽,但感觉就像一个黑客)。

这样的事情的最佳做法是什么?

  1. 发送根实体及其所有子项(即使没有更改)。
  2. 将根实体(包括或不包含更改)与具有更改的实体一起发送。
  3. 只需发送更新的实体并使用其他机制进行并发检查。
  4. 完全避免并发检查并使用最新/最后更改。
  5. 别的什么?

1 个答案:

答案 0 :(得分:1)

我的感觉是你的问题不只是关于锁定而且(或者更确切地说)关于合作编辑,这不是一个可以通过单一答案解决的问题。合作编辑非常复杂,人们可以编写有关它的书籍。根据您的coop编辑会话的要求以及数据的结构如何以及数据的不同节点之间的松散/严格约束/规则/关系,可以有许多解决方案,这些解决方案都不是完美的但是自己的利弊。

我最近一直在合作编辑工具上工作(有时还在工作),所以我对问题的复杂程度有了一定的线索,但不要期待对你的模糊问题有明确的答案,因为你赢了& #39;得到一个。要给你一个好的答案是不可能的,因为正如我之前提到的那样,即使是明确指定的合作编辑问题也常常无法提供一个好的答案,因为每个解决方案都有某种权衡,你必须决定哪个解决方案有适合您的优点和可接受的利弊。例如,您的客户可能更喜欢使用" fluid"会话就好像他单独使用一个桌面应用程序一样,有时会因为隐藏网络延迟而发现冲突而导致其工作回滚,但另一个客户可能更喜欢另一种方式:始终每次更改后提交/验证数据,即使花费时间并以较差的用户体验/较慢的工作为此付费。

分层数据?如何通过锁定并发访问来保护您的数据始终是一个大问题。这里最大的问题是你必须定义什么"一致性"表示您的数据。让我们说有人想写一个小单元,一个"节点"在您的数据中,您必须在实际提交之前验证数据。您不仅要锁定即将更改的节点,还要锁定所需的所有其他节点,以便根据您的规则检查新写入的数据是否与其他节点的内容一致。由于这个非常简单的原因,如果节点之间的关系较少,一致性规则限制较少,则编辑会话变得更有效,锁定更少。尽量使规则尽可能宽松。

以下是一些如何让您的生活更轻松的随机提示:

  • 较少的一致性规则,更松散耦合的数据更容易合作编辑。尝试仅在彼此接近的节点之间创建关系,但最好是限制一致性规则才能在单个节点内工作。您也可以定义什么是"节点",将数据组织到正确的数据结构中。
  • 为所有节点提供唯一ID,或者在互联网编辑数据的情况下,为全局唯一ID。这有很多好处......
  • 有时树不是树。 :-)至少在记忆中。如果所有树节点都具有唯一ID,则可以将树处理/处理为具有唯一ID的彼此引用的节点列表。通常,当涉及到编辑/差异/合并/冲突处理时,处理节点列表比分层数据结构更容易。更不用说如果您的用户删除了一个节点并且有其他节点引用了已删除的节点,您仍然可以在其他节点中保留无效引用,并且如果用户"取消删除" /撤消他的删除操作,然后你没有删除的引用再次生效,并且很容易处理带有id的撤销/重做功能。
  • 协同编辑与撤消/重做缓冲区有很大关系。撤消/重做缓冲区基本上包含以下3个操作:
    1. 使用具有默认属性值的新ID创建新节点。
    2. 修改现有节点的属性。 (请注意,对其他节点的引用实际上可以视为具有唯一ID类型的属性,因此您可以将节点间连接创建/删除视为属性更改。)
    3. 删除节点。
  • 请注意,客户端与服务器之间(或对等端之间的对等体)之间的网络通信通常包含您保存到撤消/重做缓冲区中的相同操作。

锁定:

  • 正如我所说,如果你的一致性规则不太严格,你可以使用更少的锁。最好将一致性规则限制在节点网络的较小区域,因为通常每个一致性规则都有一个附带的锁。如果你的一致性规则扩展到整个树,那么你要么有太严格的规则,要么阻止你从多个线程中同时有效地编辑它,要么你的数据结构组织不好,或两者兼而有之。
  • 如果在大型数据结构上有多个锁(所有数据库软件中的常见方案),您必须很好地定义数据结构和一致性规则,以便在编辑时客户端之间的冲突次数最少但除此之外还会在您开始使用数据库的不同部分以执行操作之前,操作必须获取许多锁的情况。如果你有几个线程需要很多锁,那么它很容易变成死锁。它足以只有2个锁A和B以及2个需要这两个锁的线程:T1和T2。如果T1尝试获取锁A然后B尝试获取锁int而他反向顺序:B和A然后它很容易变成死锁,因为如果T1成功获取A并且T2同时成功获取B那么两个线程都不能获得第二个锁。这个问题有一个非常简单的解决方案:将锁放入组中,每个组应包含可由任何线程同时一起获取的锁。如果T1需要锁定A和B而T2需要B和C,那么A,B和C将转到同一组。完成后,在所有组中定义锁定顺序,并始终按此顺序获取锁定。这有助于避免死锁。

如果您有一个树,如果您有严格的一致性规则,那么您可能想要锁定整个子树,有时只需要更小的子树。这是一个天真的解决方案:你锁定每个节点。如果要锁定子树,则必须在开始处理数据之前锁定该子树中的每个节点。定义节点/锁的顺序,例如,作为树的inorder遍历。当你想要锁定一个子树时,你只需要通过inorder遍历来遍历它,然后逐个获取每个锁。

正如我告诉你的那样,合作编辑有很多算法/方法,它是一个热门话题,你可以使用谷歌找到它们,但我会提到你一个我喜欢的算法。它是一种非常简单但非常有效的算法。当您的数据松散耦合时(当您在编辑时仅锁定单个节点或小单位数据)时,它会产生奇迹:https://neil.fraser.name/writing/sync/

编辑:我忘记了一些事情:在合作编辑中你必须处理冲突,当"玩家" (:-))修改相同的对象"同一"时间。最基本的解决方案属于这两个类别,这些黑白解决方案之间有几种灰色" /中间解决方案:

  1. 当用户A开始编辑一组节点时,用户A将锁定所有这些对象,而其他人则无法修改它们,而用户A持有锁。
  2. 当用户A和用户B都修改相同的节点/属性时,修改其中一个用户"胜出"因为服务器或您的网络引擎将按顺序执行这些更改。如果用户的更改无法提交,如何处理冲突?好问题,你可以提供一个文本/视觉合并工具,你可以简单地自动删除更改(我链接的算法就是这样)。
  3. 我认为您已经更好地掌握了我所谈论的内容,并且您可以创建无数种组合作为编辑问题的解决方案,即使是我在此处所描述的内容。在编辑程序的情况下,为用户提供彼此通信/协作的方式也是非常重要的。这种沟通可以聊天,但我更喜欢视觉反馈。例如,在合作文档编辑器的情况下,您可以直观地显示其他人正在查看/编辑的位置,在3d世界编辑器的情况下,您可以显示其他用户的选择/锁定区域以及他们的相机的位置。视觉反馈更容易被大脑处理,并且使用非常短的学习曲线更直观。