Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading MongoDB:实现读/写锁(互斥锁)_Multithreading_Mongodb_Locking_Mutex_Mongodb Query - Fatal编程技术网

Multithreading MongoDB:实现读/写锁(互斥锁)

Multithreading MongoDB:实现读/写锁(互斥锁),multithreading,mongodb,locking,mutex,mongodb-query,Multithreading,Mongodb,Locking,Mutex,Mongodb Query,我需要使用MongoDB实现一些锁定机制,以防止数据不一致,但允许脏读 条件: 只有在没有读取锁和无写入锁的情况下,才能获取写入锁 只有在没有写入锁的情况下,才能获取读取锁 单个文档上可以有多个并行的读取锁 必须有某种超时机制:如果(无论出于何种原因)某个进程没有释放其锁,应用程序必须能够恢复 只需忽略查询中的所有锁,即可实现脏读 (缺少写入进程不是本主题的一部分) 为什么读取和写入锁定/为什么不只使用写入锁定: 假设我们有两个集合:联系人和类别。这是一个n-m关系,其中每个联系人都有一个类

我需要使用MongoDB实现一些锁定机制,以防止数据不一致,但允许脏读

条件:
  • 只有在没有
    读取
    写入
    锁的情况下,才能获取
    写入

  • 只有在没有
    写入
    锁的情况下,才能获取
    读取

  • 单个文档上可以有多个并行的
    读取

  • 必须有某种超时机制:如果(无论出于何种原因)某个进程没有释放其锁,应用程序必须能够恢复

只需忽略查询中的所有锁,即可实现脏读

(缺少
写入
进程不是本主题的一部分)

为什么
读取
写入
锁定/为什么不只使用
写入
锁定: 假设我们有两个集合:
联系人
类别
。这是一个n-m关系,其中每个联系人都有一个类别ID数组

读取
锁定:
在向联系人添加类别时,我们必须确保该类别当前未被删除(这需要
写入
锁定,请参见下文)。而且,由于同一文档上可能存在多个
读取
锁,因此多个进程可以将此单一类别添加到多个联系人中

写入
锁定:
删除类别时,必须首先从所有联系人中删除类别ID。在运行此操作时,我们必须确保无法将此类别添加到任何联系人(此操作需要
读取
锁定)。之后,我们可以安全地删除类别文档

这样,将始终存在一致的状态

超时时间: 这是最难的部分。我已经尝试过两次了,但总是发现一些问题,似乎很难解决

基本思想:每个获取的锁都有一个时间戳,直到该锁有效。如果这个时间戳是过去的,我们可以忽略这个锁。当一个进程完成它的任务时,它应该移除它的锁

最大的挑战是,拥有多个
READ
锁,其中每个
READ
锁都有自己的超时,但多个
READ
锁可以具有相同的超时值。当释放
READ
锁时,它必须只释放自身,所有其他
READ
锁必须保留

我最后一次实施:
lock.read
可以包含元素,或者可以设置
lock.write
。一定不可能两个都设置好

查询: 对此的查询还可以,有些查询可能会更简单一些(特别是“释放读锁”)。但向你们展示它们的主要原因是我仍然不确定我是否错过了什么

前言:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
  • ISODate(“现在”)
    是当前时间。它用于忽略所有过期的锁。它还用于删除所有过期的读锁
  • ISODate(“锁过期”)
    用于指示此锁何时过期,可以忽略/删除。(例如,
    现在+5秒
  • 这在获取新锁时使用
  • 当释放读锁时也会用到它
获取
读取
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
如果没有有效的写锁,则插入读锁

update(
  {
    _id: 1234,
    $or: [
      { 'lock.write': null },
      { 'lock.write': { $lt: ISODate("now") } }
    ]
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
获取
写入
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
如果没有有效的读锁和写锁,则设置写锁

update(
  {
    _id: 1234,
    $and: [
      $or: [
        { 'lock.read':{ $size: 0 } },
        { 'lock.read':{ $not: { $gte: ISODate("now") } } }
      ],
      $or: [
        { 'lock.write': null },
        { 'lock.write': { $lt: ISODate("now") } }
      ]
    ]
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
释放
读取
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
使用获取的读锁的过期时间戳移除读锁

update(
  {
    _id: 1234,
    'lock.read': ISODate("lock expiration")
  },
  {
    $unset: { 'lock.read.$': null }
  }
)

update(
  {
    _id: 1234,
  },
  {
    $pull: { 'lock.read': { $lt: ISODate("now") } }
  }
)

update(
  {
    _id: 1234
  },
  {
    $pull: { 'lock.read': null }
  }
)
(如果多个进程获取了一个
read
锁,则
lock.read
数组可能包含多个相同的时间戳。尽管我们只需要删除一个时间戳,这不适用于
$pull
,但可以使用位置操作符
$
。 此外,我还通过附加更新删除了所有过期的锁。我尝试了一些方法,但无法将其减少到2个甚至1个更新。)

释放
写入
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
删除写日志。这里应该没有什么要检查的

update(
  {
    _id: 1234
  },
  {
    $set: { 'lock.write': null }
  }
)
编辑1:简化的获取
读取
写入
查询
{$not:{$gte:ISODate(“now”)}
仅在字段不包含任何内容时匹配
$gte:ISODate(“now”)
。尽管它将匹配
null
和不存在的字段以及空数组

获取
读取
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
获取
写入
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
但是仍然不知道关于“Release
READ
lock”查询

我考虑过某种具有超时时间戳和锁计数的元组。但是问题出现在Acquire
READ
lock查询中


编辑2:不同的数据结构便于发布
READ
lock 这是可行的,因为
ObjectId
由时间戳、机器id、进程id和计数器组成。这样就不可能创建多个相等的
对象。长话短说:

在获取
读取
锁时,我们插入一个由超时时间戳和唯一的
ObjectId
组成的文档。当释放它时,我们使用这个组合将它从阵列中移除。因此,唯一有趣的查询是:

获取
写入
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
释放
读取
锁定:

update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: { 'lock.write': null },
    $push: { 'lock.read': ISODate("lock expiration") }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: ISODate("now") } },
    'lock.read': { $not: { $gte: ISODate("now") } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
    'lock.write': { $not: { $gte: 4 } },
    'lock.read.timeout': { $not: { $gte: 4 } }
  },
  {
    $set: {
      'lock.read': [],
      'lock.write': ISODate("lock expiration")
    }
  }
)
update(
  {
    _id: 1234,
  },
  {
    $pull: {
      'lock.read': {
        $or: [
          { 'timeout': ISODate("lock expiration"), process: ObjectId("...") },
          { 'timeout': { $lt: ISODate("now") } }
        ]
      }
    }
  }
)
正如您所看到的,我们现在只需要一个查询就可以在清理所有超时锁时移除锁

唯一的进程标识符非常重要,因为