MongoDB 并不支持多文檔原子事務(wù)(multi-document atomic transactions)。但它提供了針對(duì)單個(gè)文檔的原子操作。假如一個(gè)文檔包含數(shù)百個(gè)字段,則 update 語(yǔ)句將更新所有的字段,或者一個(gè)也不更新,從而維持了文檔級(jí)的原子性。
維持原子性的建議方法是利用內(nèi)嵌文檔(embedded document)將所有經(jīng)常更新的相關(guān)信息都保存在一個(gè)文檔中。這能確保所有針對(duì)單一文檔的更新具有原子性。
考慮下列關(guān)于產(chǎn)品的文檔:
{
"_id":1,
"product_name": "Samsung S3",
"category": "mobiles",
"product_total": 5,
"product_available": 3,
"product_bought_by": [
{
"customer": "john",
"date": "7-Jan-2014"
},
{
"customer": "mark",
"date": "8-Jan-2014"
}
]
}
在這個(gè)文檔中,將購(gòu)買(mǎi)產(chǎn)品的顧客的信息內(nèi)嵌在 product_bought_by 字段中。無(wú)論何時(shí),只要新顧客購(gòu)買(mǎi)產(chǎn)品,我們就能使用 product_available 字段查看產(chǎn)品是否還有足夠的數(shù)量。如果產(chǎn)品還有,就減少 product_available 字段值,并且將新顧客的內(nèi)嵌文檔插入到 product_bought_by 字段中。使用 findAndModify 命令來(lái)實(shí)現(xiàn)該功能,因?yàn)樗芡瑫r(shí)搜索并更新文檔。
>db.products.findAndModify({
query:{_id:2,product_available:{$gt:0}},
update:{
$inc:{product_available:-1},
$push:{product_bought_by:{customer:"rob",date:"9-Jan-2014"}}
}
})
上述內(nèi)嵌文檔并使用 findAndModify 查詢的方法確保了,只有當(dāng)產(chǎn)品還有足夠數(shù)量時(shí),才更新產(chǎn)品購(gòu)買(mǎi)信息。在整個(gè)過(guò)程中,同一查詢中的事務(wù)是原子性的。
與之相反的情況是,我們可能會(huì)想分別保持產(chǎn)品可用性與購(gòu)買(mǎi)產(chǎn)品的顧客信息。在這種情況下,我們會(huì)首先使用第一個(gè)查詢來(lái)檢查產(chǎn)品是否夠用。然后在第二個(gè)查詢中更新購(gòu)買(mǎi)信息。但在這兩個(gè)查詢的執(zhí)行過(guò)程之間,其他一些顧客也可能會(huì)購(gòu)買(mǎi)了產(chǎn)品,從而使產(chǎn)品變得不夠用了。由于沒(méi)有了解到這種情況,我們的第二個(gè)查詢根據(jù)第一個(gè)查詢的結(jié)果進(jìn)行了更新。這將造成數(shù)據(jù)庫(kù)的不一致性。