我发现自己经常执行的编程逻辑的一个共同点是类似于以下伪代码:
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()
很好,除非该应用程序是分布式应用程序,并且在多台计算机上运行,并且所有这些计算机都与同一台后端数据库计算机通信,那该怎么办?在这种情况下,互斥锁是无用的,因为它仅将应用程序的单个实例与其他本地线程同步。为了使这项工作有效,我们需要某种“全局”分布式同步技术。 (假设仅禁止在
Database
中重复是不可行的策略。)通常,该问题有哪些实际解决方案?
我意识到这个问题非常笼统,但是我不想让这个问题成为特定于语言的问题,因为这是一个涉及多种语言和多种数据库技术的问题。
我故意避免指定是在谈论RDBMS还是SQL数据库,而不是NoSQL数据库之类的东西,因为再次-我正在寻找基于行业惯例的通用答案。例如,这种情况是否可以解决原子存储过程?还是原子交易?还是这需要像“分布式互斥对象”之类的东西?或更一般而言,数据库系统通常会解决此问题,还是应用程序本身应处理的问题?
如果事实证明,如果没有更多信息,根本无法回答这个问题,请告诉我,以便我进行修改。
最佳答案
防止数据踩踏的一种肯定方法是锁定数据行。许多数据库都允许您通过事务来做到这一点。有些不支持交易。
但是,对于大多数情况下争用通常较低的情况,这是过大的选择。您可能需要阅读Isolation levels以获得有关该主题的更多背景知识。
更好的通用方法通常是Optimistic Concurrency。其背后的思想是,每个数据行都包含一个签名,时间戳可以正常工作,但签名无需面向时间。例如,它可以是哈希值。这是一种通用的并发管理方法,不仅限于关系存储。
更改数据的应用程序首先读取该行,然后执行所需的任何计算,然后在某个时候将更新后的数据写回到数据存储中。通过Optimistic并发,该应用程序按照规定(如果是SQL数据库,则在SQL中表示)写入更新,只有在此期间未更改签名的情况下,才必须更新数据行。并且,每次更新数据行时,签名也必须更新。
结果是更新不会受到阻碍。但是,对于并发问题的更严格的解释,请参阅有关数据库隔离级别的文章。
所有分布式更新程序都必须遵循OCC约定(或更强的规则,例如事务锁定)才能起作用。