我发现自己经常实现的一些常见的编程逻辑类似于以下伪代码:
Let X = some value
Let Database = some external Database handle
if !Database.contains(X):
SomeCalculation()
Database.insert(X)
但是,在多线程程序中,我们在这里遇到竞争条件。线程A可能会检查X
是否在Database
中,发现它不是,然后继续致电SomeCalculation()
。同时,线程B还将检查X
是否在Database
中,发现它不是,并插入重复的条目。
当然,这需要像:
同步Let X = some value
Let Database = some external Database handle
LockMutex()
if !Database.contains(X):
SomeCalculation()
Database.insert(X)
UnlockMutex()
这很好,除非应用程序是分布式应用程序,运行在多台计算机上,所有这些计算机都与同一个后端数据库计算机进行通信?在这种情况下,Mutex是无用的,因为它只将应用程序的单个实例与其他本地线程同步。为了实现这一目标,我们需要某种“全局”分布式同步技术。 (假设在Database
中简单地禁止重复是不可行的策略。)
一般来说,这个问题的一些实际解决方案是什么?
我意识到这个问题非常泛型,但我不想让这个问题成为特定于语言的问题,因为这是跨多种语言和多种数据库技术的问题。
我故意避免指定我是在谈论RDBMS还是SQL数据库,而不是像NoSQL数据库那样,因为再次 - 我正在寻找基于行业惯例的通用答案。例如,这种情况是原子存储过程可能解决的吗?还是原子交易?或者这是否需要像“分布式互斥体”这样的东西?或者更一般地说,这个问题通常是由数据库系统解决的,还是应用程序本身应该处理的?
如果事实证明这个问题不可能完全没有回复,请告诉我,我可以修改它。
答案 0 :(得分:2)
确保数据踩踏的一种可靠方法是锁定数据行。许多数据库允许您通过事务执行此操作。有些人不支持交易。
然而,对于大多数争用率较低的案例而言,这是过度的。您可能希望阅读Isolation levels以获得有关该主题的更多背景信息。
更好的一般方法通常是Optimistic Concurrency。其背后的想法是每个数据行包括签名,时间戳工作正常,但签名不需要时间导向。例如,它可以是哈希值。这是一种通用的并发管理方法,并不仅限于关系存储。
更改数据的应用程序首先读取行,然后执行所需的任何计算,然后在某个时刻将更新的数据写回数据存储。通过乐观并发,应用程序使用规定(在SQL中表示,如果它是SQL数据库)写入更新,只有在临时中未更改签名时才必须更新数据行。并且,每次更新数据行时,也必须更新签名。
结果是更新不会被踩踏。但是有关并发问题的更严格的解释,请参阅有关数据库隔离级别的文章。
所有分布式更新程序必须遵循OCC约定(或更强大的内容,如事务锁定)才能使其正常工作。
答案 1 :(得分:0)
显然,您可以使用特定资源上的独占锁将“synch”部分移动到数据库层本身。
这有点极端(在大多数情况下,当你真正发现有人已插入行时尝试插入和管理异常)会更加充分,我认为。
答案 2 :(得分:0)
好吧,既然你问了一个普遍的问题,我会尝试提供另一种选择。它不是非常正统,但可能有用:您可以“定义”机器或负责这样做的过程。例如:
Let X = some value
Let Database = some external Database handle
xResposible = Definer.defineResponsibleFor(X);
if(xResposible == me)
if !Database.contains(X):
SomeCalculation()
Database.insert(X)
这里的技巧是使defineResponsibleFor始终返回与调用谁无关的相同值。所以,如果你有一个公平的X分布范围和一个公平的Definer,所有的机器都有工作要做。您可以使用简单的线程互斥来避免竞争条件。当然,现在您必须注意容错(如果机器或流程已经停业,您的Definer必须知道并且没有为它定义任何工作)。但是你应该这样做......:)