維護(hù)原子性的推薦方法是保留所有相關(guān)信息,這些信息經(jīng)常使用嵌入的文檔一起更新到一個(gè)文檔中。這將確保單個(gè)文檔的所有更新都是原子的。
假設(shè)我們已經(jīng)創(chuàng)建了一個(gè)名稱為productDetails的集合,并在其中插入了一個(gè)文檔,如下所示-
>db.createCollection("products") { "ok" : 1 } > db.productDetails.insert( { "_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" } ] } ) WriteResult({ "nInserted" : 1 }) >
在本文檔中,我們將購(gòu)買(mǎi)產(chǎn)品的客戶的信息嵌入到 product_bought_by 字段中?,F(xiàn)在,每當(dāng)新客戶購(gòu)買(mǎi)產(chǎn)品時(shí),我們將首先使用 product_available 字段檢查產(chǎn)品是否仍然可用。如果可用,我們將減少product_available字段的值,并在product_bought_by字段中插入新客戶的嵌入文檔。我們將為此功能使用 findAndModify 命令,因?yàn)樗阉骱透挛臋n的過(guò)程是相同的。
>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"}} } })
我們采用嵌入文檔和使用 findAndModify 查詢的方法,確保只有在產(chǎn)品可用時(shí)才更新產(chǎn)品購(gòu)買(mǎi)信息。整個(gè)事務(wù)都在同一個(gè)查詢中,是原子的。
與此相反,請(qǐng)考慮以下情況:我們可能分別保留了產(chǎn)品的可用性和有關(guān)誰(shuí)購(gòu)買(mǎi)了該產(chǎn)品的信息。在這種情況下,我們將使用第一個(gè)查詢首先檢查產(chǎn)品是否可用。然后在第二個(gè)查詢中,我們將更新購(gòu)買(mǎi)信息。但是,有可能在執(zhí)行這兩個(gè)查詢之間,其他用戶已經(jīng)購(gòu)買(mǎi)了該產(chǎn)品,而該產(chǎn)品不再可用。在不知道這一點(diǎn)的情況下,我們的第二個(gè)查詢將基于我們第一個(gè)查詢的結(jié)果來(lái)更新購(gòu)買(mǎi)信息。這將使數(shù)據(jù)庫(kù)不一致,因?yàn)槲覀円呀?jīng)出售了不可用的產(chǎn)品。