鍍金池/ 問答/數(shù)據(jù)庫/ mongodb findOneAndUpdate 線程安全嗎?

mongodb findOneAndUpdate 線程安全嗎?

需求:

類似消息隊(duì)列 多個(gè)用戶同時(shí)從一個(gè)表中取數(shù)進(jìn)行處理 為了保證每個(gè)用戶取得數(shù)都不同 取完數(shù)后立即加上user_id

db.foo.findOneAndUpdate({"user_id":{$exists:false}, {$set:{user_id:"123456"}},{sort:{_id:1}}})

官方文檔中并沒有說明該命令是否線程安全 如有如下的記錄

{"_id":1}
{"_id":2}
{"_id":3}

5個(gè)用戶同時(shí)來請求 有沒有可能都是取得_id為1的記錄?

使用Python開發(fā)了一個(gè)腳本 驗(yàn)證了一下 似乎是線程安全的 但是又不確定 為了保險(xiǎn)起見 應(yīng)用代碼還是顯式加了鎖 Python代碼如下:

def query_and_set_user_id(user_id):
    result = db.test.find_one_and_update({"user_id":{"$exists":False}},{'$set': {'user_id': user_id}}, sort=[("id", 1)])
    print(threading.current_thread(), result)
    return result

max_workers = 5
pool = ThreadPoolExecutor(max_workers)
futures = []
for i in range(max_workers):
    f = pool.submit(query_and_set_user_id, "user_{}".format(i+1))
    futures.append(f)

wait(futures)
回答
編輯回答
笨小蛋

應(yīng)用加鎖解決不了程序分布式部署的問題。

這有點(diǎn)類似隊(duì)列里的出隊(duì)列。A取的了一條消息, 該消息就不能被B取到。這種并發(fā)控制用mongo的話,我一般醬紫:

  1. 記錄加上一個(gè)處理狀態(tài)字段,如processStatus, 默值認(rèn)為0未處理 1處理中 2處理成功 3處理失敗 4重試。
  2. 利用findAndModify具有原子性操作的特性。
db.foo.findAndModify({
    query:{processStatus:[0,4]},
    update: { $set: { processStatus: 1} },
});
// 再根據(jù)處理結(jié)果 置processStatus為成功、失敗或者重試。

參考文檔:mongo的原則性和事務(wù)性

2018年5月22日 06:08
編輯回答
淺時(shí)光

是線程安全的。
實(shí)際上findAndModify的行為跟update是一樣的,這兩個(gè)函數(shù)在查詢和更新之間是不會(huì)被打斷的。不同的是findAndModify在操作之后還可以返回更新前或更新后的文檔。更多的差異可以看文檔:Comparisons with the update Method

2017年7月6日 08:22