具有多进程的更新插入上的重复键错误(Mongo>=3.0.4 连线虎)



所有

我刚收到一个来自我们应用程序的奇怪错误:

当我用两个进程进行更新时,它抱怨在一个具有唯一索引的集合上出现重复键错误,但有问题的操作是一个错误。

案例代码:

import time
from bson import Binary
from pymongo import MongoClient, DESCENDING
bucket = MongoClient('127.0.0.1', 27017)['test']['foo']
bucket.drop()
bucket.update({'timestamp': 0}, {'$addToSet': {'_exists_caps': 'cap15'}}, upsert=True, safe=True, w=1, wtimeout=10)
bucket.create_index([('timestamp', DESCENDING)], unique=True)
while True:
    timestamp =  str(int(1000000 * time.time()))
    bucket.update({'timestamp': timestamp}, {'$addToSet': {'_exists_foos': 'fooxxxxx'}}, upsert=True, safe=True, w=1, wtimeout=10)

当我用两个进程运行脚本时,Pymongo异常:

Traceback (most recent call last):
  File "test_mongo_update.py", line 11, in <module>
    bucket.update({'timestamp': timestamp}, {'$addToSet': {'_exists_foos': 'fooxxxxx'}}, upsert=True, safe=True, w=1, wtimeout=10)
  File "build/bdist.linux-x86_64/egg/pymongo/collection.py", line 552, in update
  File "build/bdist.linux-x86_64/egg/pymongo/helpers.py", line 202, in _check_write_command_response
pymongo.errors.DuplicateKeyError: E11000 duplicate key error collection: test.foo index: timestamp_-1 dup key: { : "1439374020348044" }

环境:

  • mongodb 3.0.5,WiredTiger

  • 单个mongodb实例

  • pymongo 2.8.1

mongo.conf

systemLog:
   destination: file
   logAppend: true
   logRotate: reopen
   path: /opt/lib/log/mongod.log
# Where and how to store data.
storage:
   dbPath: /opt/lib/mongo
   journal:
     enabled: true
   engine: "wiredTiger"
   directoryPerDB: true
# how the process runs
processManagement:
   fork: true  # fork and run in background
   pidFilePath: /opt/lib/mongo/mongod.pid
# network interfaces
net:
   port: 27017
   bindIp: 0.0.0.0  # Listen to local interface only, comment to listen on all interfaces.
setParameter:
   enableLocalhostAuthBypass: false

有什么想法吗?

PS:

我在MMAPV1存储引擎中重试了同样的情况,它运行良好,为什么?

我在这里找到了一些相关的东西:https://jira.mongodb.org/browse/SERVER-18213

但是在这个错误修复之后,它会处理这个错误,所以看起来这个错误还没有完全修复。

干杯

我在以下位置发现了错误:https://jira.mongodb.org/browse/SERVER-14322

请随时投票支持它,并观看它的进一步更新。

upstart既检查要更新的现有文档,也插入新文档。

我的最佳猜测是你遇到了一个时间问题,其中:

  1. 流程2检查是否存在,但没有
  2. 流程1检查是否存在,但它没有
  3. 处理2个插入,有效
  4. 进程1插入,从而引发重复密钥

首先检查您的python库在下面发送的本机查询。确认这是你对本地mongo方面的期望。然后,如果你可以在wiredtiger上半定期地复制这种情况,但从不在mmap上复制,那么就向mongo提出一个错误,以确认他们的预期行为。有时很难选择他们保证的原子行为。

这是一个很好的例子,说明了为什么MongoObjectID结合了时间戳、机器id、pid和计数器以获得唯一性。

http://docs.mongodb.org/manual/core/storage/

使用WiredTiger,所有写操作都发生在文档级锁的上下文中。因此,多个客户端可以同时修改单个集合中的多个文档。

您的多个客户端可以同时更新集合。Wiredtiger将锁定您正在更新的文档,而不是集合。

我使用MongoDB来管理存储系统的目录树数据。我遇到了同样的问题:upsert=True的update命令有时会创建重复的文档。

但我使用了MMAPv1,我不知道使用WiredTiger引擎会发生什么。我认为这是MongoDB的一个种族条件问题。最后,我决定用python-redis锁编写一个锁定机制(文档锁定级别(。它运行得很好!

最新更新