【问题标题】:Detecting concurrent data modification of document between read and write检测文档在读写之间的并发数据修改
【发布时间】:2017-07-01 21:01:46
【问题描述】:

我对从数据库中获取文档、根据一些外部条件运行一些计算、更新文档的一个字段然后保存文档的场景感兴趣,所有这些都在一个系统中可能有并发线程访问数据库。

为了更容易理解,这里有一个非常简单的例子。假设我有以下文件:

{
   ...
   items_average: 1234,
   last_10_items: [10,2187,2133, ...]
   ...
}

假设有一个新项目(X)进来,需要做五件事:

  1. 从数据库中读取文档
  2. 删除last_10_items 中的第一个(最旧的)项目
  3. 将 X 添加到数组的末尾
  4. 重新计算平均值*并将其保存在items_average
  5. 将文档写入数据库

* 注意:选择平均计算作为一个非常简单的例子,但问题应该考虑到基于文档中现有数据和新数据的更复杂的操作(即@987654325 无法解决的问题@运算符)

这在单线程系统中当然很容易实现,但在并发系统中,如果有 2 个线程想要遵循上述步骤,则可能会出现不一致,因为两者都会更新 last_10_itemsitems_average 值不考虑和/或覆盖并发更改。

那么,我的问题是如何处理这种情况?有没有办法检查或响应基础文档在步骤 1 和 5 之间发生更改的事实?是否存在诸如来自 redis 的 WATCH 或来自关系数据库的“并发修改错误”之类的东西?

谢谢

【问题讨论】:

  • 这不会发生,因为单个文档的写入和读取不会同时发生
  • 我认为,在阅读了您的整个问题后,您的意思是来自客户端的另一个连接的修改,这需要在查找和更新之间进行复杂的计算周期。在这种情况下,您将使用版本字段来防止过时的数据过滤到数据库中
  • 是的,是后一种情况。您的意思是在文档上有一个version 字段或updated_at 字段,然后使用db.foo.update({version=last_known_version}, {...stuff to update...}) 之类的东西并检查结果?我认为这可以工作,虽然感觉有点 hacky。我希望 MongoDB 内部机制能够更好地检测并发修改。
  • 最好用作版本字段,这里有一个很好的教程:askasya.com/post/trackversions
  • 请记住,这也是 SQL 技术人员所做的事情,但在幕后,他们会跟踪版本,这就是他们检测过时数据的方式,在 MongoDB 的情况下,您只需要自己做

标签: mongodb database-design concurrency nosql


【解决方案1】:

在数据库系统中,它使用类似于事务内存的内存检查和回滚方案。

简而言之,它只是监视您指定的共享内存部分,并执行比较和交换或加载和链接或测试和设置等操作。 因此,如果在事务过程中任何内存内容发生变化,它将中止并重试,直到没有对该共享内存的冲突操作。

例如,GCC 实现如下:

https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

type __sync_lock_test_and_set (type *ptr, type value, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

有关事务性内存的更多信息, http://en.wikipedia.org/wiki/Software_transactional_memory

【讨论】:

  • 我不确定MongoDB是否利用了这些信息,记住MongoDB不直接访问内存资源。操作系统只是使用 LRU 来缓存 MongoDB 索引计划和数据文件
猜你喜欢
  • 2023-03-16
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多