2011-07-09 50 views
121

我知道這裏有類似的問題,但他們要麼telling me到如果我需要交易切換回常規的RDBMS系統或使用atomic operationstwo-phase commit。第二種解決方案似乎是最佳選擇。第三個我不想跟隨,因爲看起來很多事情可能會出錯,我無法在各個方面進行測試。我很難重構我的項目來執行原子操作。我不知道這是否來自我有限的觀點(迄今爲止我只使用過SQL數據庫),或者它是否實際上無法完成。如何解決MongoDB中缺少事務的問題?

我們想在我們公司進行試點測試的MongoDB。我們選擇了一個相對簡單的項目 - 一個短信網關。它允許我們的軟件發送SMS消息到蜂窩網絡,網關完成骯髒的工作:實際上通過不同的通信協議與提供商進行通信。網關還管理消息的計費。每個申請該服務的客戶都必須購買一些信用。系統在發送消息時自動減少用戶的餘額,如果餘額不足,則拒絕訪問。同樣因爲我們是第三方短信服務提供商的客戶,我們也可能與他們有自己的餘額。我們也必須跟蹤這些。

我開始思考我如何可以將需要的數據存儲與MongoDB中,如果我砍倒了一些複雜性(外部計費,排隊短信發送)。從SQL世界來,我就會爲用戶創建一個單獨的表,另一個用於短信,以及一個用於存儲有關用戶的餘額交易。假設我爲MongoDB中的所有人創建了單獨的集合。

想象短信在這個簡化的系統下面的步驟發送任務:

  1. 檢查用戶是否有足夠的餘額;拒絕訪問,如果沒有足夠的信用

  2. 發送和存儲消息在SMS收集中的細節和成本(在現場系統中,消息將具有status屬性,並且任務將拾取它用於遞送並設置根據其當前狀態)

  3. 下降短信的價格用戶的平衡被髮送的消息

  4. 日誌交易收集

在交易成本

現在有什麼問題呢? MongoDB只能在一個文檔上進行原子更新。在之前的流程中,可能會發生某種錯誤,並且消息會存儲在數據庫中,但用戶的餘額不會更新和/或事務未記錄。

我想出了兩個想法:

  • 爲用戶創建一個單一的集合,並存儲餘額作爲一個字段,用戶關聯交易和信息爲用戶的文檔中的子文檔。因爲我們可以自動更新文件,所以這實際上解決了交易問題。缺點:如果用戶發送很多短信,文檔的大小可能會變大,並且可能會達到4MB的文檔限制。也許我可以在這種情況下創建歷史文檔,但我認爲這不是一個好主意。另外我不知道如果我將越來越多的數據推送到同一個大文檔,系統會有多快。

  • 爲用戶創造一個集合,一個用於交易。可以有兩種交易:信用卡購買與正結餘變化和信息發送負平衡變化。交易可能有一個子文件;例如在消息發送 SMS的細節可以嵌入到交易中。缺點:我不存儲當前的用戶餘額,所以每次用戶嘗試發送消息以告知消息是否可以通過時,我都必須計算它。恐怕這個計算會隨着存儲事務數量的增長而變慢。

我有點困惑要選擇哪種方法。還有其他解決方案嗎?我無法在網上找到任何關於如何解決這些問題的最佳實踐。我想很多試圖熟悉NoSQL世界的程序員在開始時都面臨類似的問題。

+52

原諒我,如果我錯了,但看起來好像這個項目將使用NoSQL數據存儲,無論它是否會從中受益。 NoSQL不是SQL的替代選擇,而是一種「時尚」選擇,但是當關系型RDBMS的技術不適合問題空間和非關係型數據存儲時。很多你的問題有「如果它是SQL,那麼......」並且對我發出警告。所有NoSQL都來自需要解決SQL無法解決的問題,然後將它們推廣到易於使用的階段,然後當然這一潮流開始滾動。 – PurplePilot

+3

我知道這個項目並不是嘗試使用NoSQL的最佳選擇。然而,如果我們開始將其用於其他項目(比如說圖書館館藏管理軟件,因爲我們正在進行館藏管理),並且突然出現某種請求需要交易(並且實際上存在這種請求,那麼想象一本書從一個集合轉移到另一個集合),我們需要知道我們如何克服這個問題。也許這只是我狹隘的思想,認爲總是需要交易。但它可能是有辦法克服這些不知何故。 – NagyI

+2

我同意PurplePilot,你應該選擇一種適合解決方案的技術,而不是嘗試移植一個不適合解決問題的解決方案。爲圖形數據庫建模數據是與RDBMS設計完全不同的範例,你必須忘記你所知道的一切,重新學習新的思維方式。 –

回答

19

檢查this由Tokutek。他們爲Mongo開發了一個插件,不僅能夠保證交易,還能提高性能。

+0

@Giovanni Bitliner。從此以後,托克泰克被佩爾科納收購,並且在你給出的鏈接上,我看不到任何關於自該職位以來發生的任何信息。你知道他們的努力發生了什麼嗎?我通過電子郵件發送該頁面上的電子郵件地址以查明。 –

+0

你需要什麼具體?如果您需要將toku技術應用於Mongodb,請嘗試https://github.com/Tokutek/mongo,如果您需要mysql版本,可能會將它添加到他們通常提供的標準版本的Mysql中 –

+0

如何與nodejs集成tokutek 。 –

6

該項目是簡單的,但你必須支持交易付款,這使得整個事情的困難。因此,例如,一個擁有數百個藏品(論壇,聊天,廣告等)的複雜門戶系統在某種程度上更簡單一些,因爲如果您失去了論壇或聊天記錄,那麼沒有人真正關心。另一方面,如果你失去了一個嚴重問題的支付交易。

所以,如果你真的想MongoDB的一個試點項目,選擇其中之一是在方面簡單。

+0

謝謝你解釋。很遺憾聽到這一點。我喜歡NoSQL的簡單性和使用JSON。我們正在尋找ORM的替代方案,但看起來我們必須堅持一段時間。 – NagyI

+0

你能給出任何充分的理由,爲什麼MongoDB比SQL更適合這項任務?試點項目聽起來有點愚蠢。 –

+0

我沒有說MongoDB比SQL更好。我們只是想知道它是否比SQL + ORM更好。但現在越來越清楚,他們在這類項目中沒有競爭力。 – NagyI

10

它帶來的一點是:如果事務完整性是必須那麼就不要使用MongoDB的,但在系統中支持事務只使用組件。在組件之上構建某些東西非常困難,以便爲不符合ACID的組件提供類似ACID的功能。根據個人usecases它可能是有意義以某種方式單獨行動納入事務性和非事務性的行爲......

+1

我想你的意思是NoSQL可以用作經典RDBMS的搭檔數據庫。我不喜歡在同一個項目中混合使用NoSQL和SQL的想法。它增加了複雜性,並可能引入一些不重要的問題。 – NagyI

+1

NoSQL解決方案很少單獨使用。文件存儲(mongo和沙發)可能是這條規則唯一的例外。 –

6

交易不存在MongoDB中的正當理由。這是使MongoDB速度更快的其中一件事。

就你而言,如果交易是必須的,mongo似乎不太合適。

可能RDMBS + MongoDB的,但是這會增加複雜性和將使它更難管理和支持應用程序。

+1

現在有一個名爲TokuMX的MongoDB發行版,它使用分形技術來提供50倍的性能改進,並同時提供完整的ACID事務支持:http://www.tokutek.com/tokumx-for-mongodb/ – OCDev

+7

交易永遠不是一個「必須」。只要你需要一個簡單的案例,你需要更新2個桌子mongo突然不再是一個合適的?這並沒有留下很多用例。 –

+0

@Mr_E同意,這就是爲什麼MongoDB有點愚蠢:) –

7

現在有什麼用,這個問題? MongoDB只能在一個文檔上進行原子更新。在之前的流程中,可能會發生某種錯誤,並將消息存儲在數據庫中,但用戶的餘額不會減少和/或事務未被記錄。

這是不是一個真正的問題。您提到的錯誤是邏輯(錯誤)或IO錯誤(網絡,磁盤故障)。這種類型的錯誤可能會使交易存儲和事務存儲處於非一致狀態。例如,如果已經發送的短信,但同時出現存儲信息中的錯誤 - 它不能回滾短信發送,這意味着它不會被記錄,用戶平衡不會減少等

這裏真正的問題用戶可以利用競爭條件併發送比他的餘額允許的更多消息。這也適用於RDBMS,除非您使用平衡字段鎖定(這將是一個很大的瓶頸)在事務內發送SMS。作爲一種可能的解決方案,MongoDB首先會使用findAndModify來減少餘額並檢查它,如果它是否定的,則不允許發送並退還金額(原子增量)。如果是肯定的,繼續發送,並在未能退還金額的情況下。餘額歷史記錄集合也可以維護以幫助修復/驗證平衡字段。

+0

謝謝你這個偉大的答案!我知道,如果我使用交易功能的存儲數據可能會因爲SMS系統而無法控制的情況下損壞。然而,在Mongo中,數據錯誤也有可能發生在內部。假設代碼使用findAndModify更改用戶的餘額,餘額變爲負值,但是在我可以糾正錯誤發生並且應用程序需要重新啓動之前。我想你的意思是我應該實現類似於基於事務收集的兩階段提交的東西,並定期對數據庫進行更正檢查。 – NagyI

+9

如果你沒有做最後的提交,事務性存儲將會回滾。 –

+8

此外,你不發送短信,然後登錄到數據庫,這是錯誤的。首先將所有內容都存儲在數據庫中並進行最終提交,然後您可以發送消息。此時某些東西可能仍會失敗,因此您需要執行cron作業來檢查消息是否實際發送,如果不嘗試發送。也許專門的消息隊列會更好。但整個事情歸結爲是否可以以事務處理的方式發送短信... –

68

生活無交易

事務的支持ACID性質,但儘管在MongoDB沒有交易,我們確實有原子操作。那麼,原子操作就意味着當你處理單個文檔時,在其他任何人看到文檔之前完成該工作。他們會看到我們所做的所有更改或沒有任何更改。使用原子操作,通常可以實現我們在關係數據庫中使用事務完成的相同事情。原因在於,在關係數據庫中,我們需要對多個表進行更改。通常需要連接的表格,所以我們希望一次完成。要做到這一點,由於有多個表,我們必須開始一個事務並完成所有這些更新,然後結束事務。但與MongoDB,我們要嵌入數據,因爲我們要預加入它在文件中,他們是這些豐富的文件,有層次結構。我們經常可以完成同樣的事情。例如,在博客示例中,如果我們想確保我們以原子方式更新博客文章,我們可以這樣做,因爲我們可以一次更新整個博客文章。就好像它是一堆關係表一樣,我們可能必須打開一個事務,以便我們可以更新後期集合和評論集合。

那麼我們可以在MongoDB中採取哪些方法來克服交易缺失?

  • 重組 - 重構代碼,讓我們在單個文檔中工作,並採取我們認爲文件中提供的原子操作的優勢。如果我們這樣做,那麼通常我們都會定下來。
  • 在軟件中實現 - 我們可以通過創建臨界區域來實現軟件鎖定。我們可以使用查找和修改來構建測試,測試和設置。如果需要,我們可以構建信號燈。從某種意義上說,無論如何,這是更大的世界的工作方式。如果我們考慮一下,如果一家銀行需要將錢轉移到另一家銀行,他們不會生活在同一個關係系統中。他們每個人都有自己的關係數據庫。即使我們無法在這些數據庫系統之間開始事務和結束事務,他們也必須能夠協調該操作,而只能在一個銀行內的一個系統內進行。所以在軟件方面肯定有辦法解決這個問題。
  • 容忍 - 最後的方法,它經常在現代Web應用程序和其他接收大量數據的應用程序中工作,只是容忍一點不一致。一個例子是,如果我們在Facebook上談論一個好友Feed,那麼每個人都同時看到你的牆更新並不重要。如果奧奇,如果一個人的幾個在後面跳了幾秒鐘,他們趕上。在許多系統設計中,一切都保持完全一致,並且每個人都擁有完全一致且相同的數據庫視圖,這通常並不重要。所以我們可以簡單地容忍一點點的不一致,這有點臨時性。

UpdatefindAndModify$addToSet & $push操作在單個文檔中原子操作(更新內)(更新內)。

+1

我喜歡這個答案的方式,而不是繼續質疑我們是否應該回到關係數據庫。謝謝@xameeramir! – DonnyTian

+1

如果您有超過1臺服務器,代碼的關鍵部分將不起作用,必須使用外部分佈式鎖定服務 –

+0

@AlexanderMills請您詳細說明一下嗎? – xameeramir

2

這可能是我發現的關於爲mongodb實現交易功能的最佳博客。

正在同步標誌:最好從主控文檔

作業隊列只是將數據複製過來:非常通用,解決了95%的病例。無論如何,大多數系統至少需要有一個工作隊列!

兩個階段提交:該技術確保每個實體總是得到一個一致的狀態

登錄和解所需的所有信息:最強大的技術,非常適用於金融系統

版本:提供隔離和支持複雜的結構

閱讀更多信息:https://dzone.com/articles/how-implement-robust-and

+0

請在答案中包含回答問題所需的鏈接資源的相關部分。原來,你的答案很容易受到鏈接腐爛的影響(例如,如果鏈接的網站崩潰或者更改你的答案可能是無用的)。 – mech

+0

謝謝@mech的建議 – Vaibhav

2

由於4.0,MongoDB的將多文檔ACID事務。計劃是首先啓用副本集部署中的部署,然後是分區集羣。 MongoDB中的事務就像開發人員從關係數據庫中熟悉的事務 - 它們將是多語句,具有類似的語義和語法(如start_transactioncommit_transaction)。重要的是,啓用事務的MongoDB更改不會影響不需要它們的工作負載的性能。

詳情請參閱here

相關問題