日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 互聯網 > 五個解決方案讓MongoDB擁有RDBMS的魯棒性事務

五個解決方案讓MongoDB擁有RDBMS的魯棒性事務

來源:程序員人生   發布時間:2014-09-15 04:25:55 閱讀次數:3253次

【編者按】在分布式存儲解決方案中談事務一直是件很痛苦的事情,而事務也成了大部分NoSQL解決方案短板所在。近日,MongoDB公司的Antoine Girbal在其個人博客上撰文,分享了在MongoDB文檔間實施魯棒可擴展事務的5個解決方案――同步字段、作業隊列、二階段提交、Log Reconciliation和版本控制。


免費訂閱“CSDN大數據”微信公眾號,實時了解最新的大數據進展!

CSDN大數據,專注大數據資訊、技術和經驗的分享和討論,提供Hadoop、Spark、Imapala、Storm、HBase、MongoDB、Solr、機器學習、智能算法等相關大數據觀點,大數據技術,大數據平臺,大數據實踐,大數據產業資訊等服務。


以下為譯文:

事務問題

數據庫支持數據塊間的事務是有原因的。典型的場景是應用需要修改幾個獨立的比特時,如果只有一些而不是全部改變存儲到了數據庫,那么這就會出現不一致問題。因此ACID的概念是:

  • 原子性:所有的改變要么都做了,要么都沒做
  • 一致性:數據保持一致性狀態
  • 隔離性:其它用戶看不到部分改變
  • 持久性:一旦向用戶確認了事務,數據就處于安全的狀態(通常存在硬盤上)

引入NoSQL數據庫后,文檔間ACID事務的支持通常就取消了。許多鍵/值存儲仍有ACID,但它只適用于單個條目,取消ACID的主要原因是其可擴展限制。如果文檔橫跨幾個服務器,事務將會很難實施以及性能。假設事務橫跨數十個服務器,一些數據庫是遠程的,一些是不可靠的,想象下這會變的多難,多慢!

在單個文檔等級上,MongoDB支持ACID。更準確的說,默認情況下是“ACI”,打開“j”WriteConcern選項后是ACID。Mongo有豐富的查詢語言,橫跨多個文檔,因此人們一直在尋找多文檔事務來使用他們的SQL代碼。一個常見的辦法是利用文檔的性質:不需要很多行、很多關系,你可以將所有的東西嵌入到一個大文檔中,Denormalization將帶你回歸事務。

這個技術解決了從一對一關系到一對多關系的很多事務問題。這也可能使應用更簡單,數據庫更快,所以這是雙贏。不過當數據庫必須分離時,該怎么辦?

減少ACID

其實大部分應用都可以歸結為:

  • 原子性:實際上你希望所有的改變都完成
  • 一致性:系統短時間不一致沒關系,只要最終一致就行
  • 隔離性:缺乏隔離性導致暫時的不一致,這并不理想,但是當今線上服務時代,很多用戶對此都習慣了(如用戶支持:“它要花幾秒傳輸”)。
  • 持久性:很重要,要支持。

這樣問題就簡化為魯棒性、可擴性、最終一致性。

解決方案 1:字段同步

這種解決方案的使用場景最簡單,最常見:文檔間有些字段需要保持“同步”。例如,你有一個用戶名為“John”的用戶文檔,文檔代表John發表過的評論。如果用戶可以更換用戶名,那么這個改變需要發送給所有文檔,即使進程中有應用錯誤或數據庫錯誤。

為了實現這一目標,一個簡單的辦法是在主文檔(這個情況下主文檔是用戶文檔)中使用一個新字段(如“syncing”)。給“syncing”設置一個日期時間戳,記錄用戶文檔的更新。

db.user.update({ _id: userId }, { $set:{ syncing: currentTime }, { rest of updates ... } })

然后應用會修改所有的評論文檔。結束后,需要移除標識:

db.user.update({ _id: userId }, {$unset: { syncing: 1 } })

現在假設進程中出現了問題:有些評論使用的是舊用戶名。不過這些地方仍然會保留標識,所以應用知道哪些進程需要重新進行。因此,你需要后臺進程在指定的時間(如1小時)檢查“syncing”文件是否有未完成的地方。索引應設為“sparse”,這樣只有實際設置的文檔需要被索引,索引量就會比較小。

db.user.ensureIndex({ syncing: 1 }, { sparse: true })

因此,系統通??梢员3质虑樵诙虝r間內同步,在系統故障的情況下,時間周期為一個小時。如果時間不重要,當探測到“syncing”標志時,應用可以輕易修復文檔。

解決方案2:作業隊列

以上原理良好工作的前提是應用不需要很多內容,只依賴于通用進程(如:復制一個值)。一些事務需要執行特定變化,這些變化稍后很難識別。例如,用戶文檔包括一個朋友列表:

{ _id: userId, friends: [ userId1,userId2, ... ]}

現在A和B決定成為朋友:你需要把B添加到A的列表,也需要把A添加到B的列表。如果兩者沒有同時發生也沒有關系(只要沒有引發困擾)。針對這種情況和大多數事務問題的解決方案是使用作業隊列,作業隊列也存儲在MongoDB。一個作業文檔就像這樣:

{ _id: jobId, ts: timeStamp, state: "TODO", type: "ADD_FRIEND", details: { users: [ userA, userB ]} }

或者是原始線程可以插入作業轉發改變,或者是“worker”線程可以撿起工作。worker使用findAndModify()獲取最原始的未加工的工作,findAndModify()是完全原子性的。操作中findAndModify()將工作標注為將被處理,同時也會表明worker name、當前時間以便于追蹤。{ state: 1, ts: 1 } 上的索引使這些調用很迅速。

db.job.findAndModify({ query: { state: "TODO" }, sort: { ts: 1 }, update: { $set: { state: "PROCESSING", worker: { name: "worker1", ts: startTime } } } })

之后worker以一種冪等的方式對雙方用戶文檔進行修改,這些改變能應用很多次,并且有同樣的效果――這很重要!為了這個目的,我們只需要使用一個$addToSet。一種更通用的替代方式是在查詢端添加一個測試,檢測修改是否執行了。

db.user.update({ _id: userA }, {$addToSet: { friends: userB } })

最后一步是刪除作業或標注作業完成。再保留一段時間作業是一種安全的方式,唯一的缺點是隨著時間的流逝,先前的索引會變得越來越大,盡管你可以在指定域{ undone: 1 } 上使用稀疏索引,并且根據實際情況修改查詢。

db.job.update({ _id: jobId }, { $set: { state: "DONE" } })

如果進程在某一時刻故障了,作業仍然會在隊列中,并標注為處理中。后臺進程停止一段時間后會將作業標注為需要再次處理,然后作業會重新從頭開始。

解決方案3 :二階段提交

二階段提交是一個眾所周知的解決方案,很多分布式系統都采用了這種解決方案。MongoDB簡化了這種解決方案的實施,因為靈活的框架,我們可以將所有需要執行的數據全都放入文檔中。我幾年前就寫過關于這種方法的文章,你可以去MongoDB Cookbook中查閱《 執行二階段提交》(Perform Two Phase Commits)或者到MonoBD Manual中查閱《 執行二階段提交》(Perform Two Phase Commits)。

解決方案4: Log Reconciliation

很多財務系統常用的解決方案是 log reconciliation。這種解決方案將事務寫作簡單的日志,這避免了復雜性和潛在的故障。然后從上次良好狀態以來所有的變化推測當前賬戶的狀態。在極端情況下,你可以清空賬戶,然后通過實施從第一天以來所有的變化重建賬戶……這聽起來很恐怖,但是可行。賬戶文件需要一個“緩存”來提高速度,還需要一個seqId,seqId計算如下:

{ _id: accountId, cache: { balance:10000, seqId: 115 } }

執行事務時,一個典型的財務系統會給事務寫一個條目,會給與事務有關的賬戶寫一個“賬戶變化”條目。這個方法需要進一步的寫保證,“作業隊列”解決方案可以實現寫保證,事務中所有的作業在所有賬戶更改寫入前都會保持不變。不過有了MongoDB,我們可以寫一個包括事務和賬戶更改的文檔。這個文檔應該嵌入tx集合,如下:

{ _id: ObjectId, ts: timestamp , proc: "UNCOMMITTED", state: "VALID", changes: [ { account: 1234, type: "withdraw", value: -100, seqId: 801, cachedBal: null }, { account: 2345, type: "deposit", value: 100, seqId: 203, cachedBal: null } ] }

幾個重點:

  • 步驟:事務從“UNCOMMITTED” 狀態開始,變為“COMMITTED”,此時涉及這些賬戶的所有先前事務也會變為 “COMMITTED” ,這表明這個事務也可以用作“anchor”來進行平衡計算。
  • 狀態:狀態可能是 “VALID”、“CANCELLED等。如果不是VALID,即使是“COMMITTED”,平衡計算也會忽略事務。
  • seqId:這是賬戶的獨有的seqId,這個seqId給賬戶更改一個確定的順序。
  • cachedBal:賬戶的緩存平衡。如果事務時“COMMITTED”狀態,那么緩存平衡(如果設置了)是一個有效值。
  • 注意我們在 { changes.account: 1, changes.seqId: 1 }上使用一個獨特的索引。reconciliation需要這個索引來提速,一個賬戶也不會有seqId副本。

關鍵是確保即使事務沒有按順序發生,緩存平衡也可以安全的計算/取消,還有就是事務狀態可能改變。因此我們每個賬戶使用一個seqId,這確保了賬戶更改按確定的順序發生,可以避免復雜的鎖。在寫事務前,應用首先通過簡單地查詢推斷每個賬戶的下一個sqlId:

db.tx.find({ "changes.account": 1234 }, { "changes.$.seqId": 1 }).sort({ "changes.seqId": -1 }).limit(1)

然后每個sqlId都本地增長,然后寫作事務的一部分。如果另一個線程也可能同時包括同樣的seqId,獨特的索引會確保寫失敗,線程會進行重試直到順利完成任務。另一種方法是在賬戶集中保存一個當前seqId,然后用 findAndModify()獲得下一個seqId,這通常會比較慢,除非你對賬戶有很多爭用。注意如果因為某種原因事務沒有寫時,seqId可能會被跳過去,不過只有沒有副本情況下才會成為。

下面我們談談reconciliation的基礎。后臺進程確保所有未提交的事務都會繼續進行。只有所有賬戶的低seqId的事務都提交后一個事務才會被標注為提交。事務被標記為提交后就會變成不可變的。下面來談談好的方面:獲得賬戶平衡。首先我們獲得好的平衡,我們可以通過索引進行查詢:

db.tx.find({ "changes.account": 1234, proc: "COMMITTED" }, { "changes.$": 1 }).sort({ "changes.seqId": -1 }).limit(1)

我們通過較大seqId的事務獲得所有將發生的更改:

db.tx.find({ "changes.account": 1234, "changes.seqId": { $gt: lastGoodSeqId } }, { "changes.$": 1 }).sort({ "changes.seqId": 1 })

我們可以使用這些解決展示即將發生的損耗。如果我們只想簡單的了解將來的平衡點在哪,我們可以讓MongoDB收集所有變更展示總數:

db.tx.aggregate([{ $match: { "changes.account": 1234, "changes.seqId": { $gt: lastGoodSeqId }, state: "VALID" }}, 
{ $unwind: "changes" }, 
{ $match: { "account": 1234 }}, 
{ $group: { _id: "total", total: { $sum: "$value" } }}])

為了確保系統快速、計算量小,后臺工作者要確保所有的事務都達到提交狀態,平衡得到緩存。理想情況下一個事務是不可逆的,取而代之的是提交一個逆向事務來實施事務。不過只要所有的進一步事務狀態和緩存都是正確設置的,取消是可行的。

解決方案5:版本控制

有時變得很復雜,以至于不能再JSON中表示,這些變更可能涉及很多有著復雜關系的文件(如樹結構)。如果僅是部分變化(如破壞樹)將會很混亂,這種情況下我們需要隔離。獲取隔離性的一種方式是插入有著高版本號的新文檔,取代對現有文檔的更新??梢酝ㄟ^同日志和解同樣的技術很容易、很安全的獲得新版本號。通常{ itemId: 1, version: 1}上有一個獨特的索引。

嵌入文檔的應用從子文檔開始,到主文檔結束(如根節點)。當獲取數據時,應用檢查主文檔的版本號,忽略高于版本號高于此版本號的文檔。未完成的事務可以保持原狀,可以忽略,可以清楚。

總結

綜上所述,我們提供了在文檔間實施魯棒可擴展事物的五種解決方案:

  • 同步標志:最適用于僅從主文檔復制數據的情況
  • 作業隊列:比較通用,適用于95%的情況,大部分系統至少需要一個作業隊列
  • 二階段提交:這種技術確保每個實體都有為保持一致性狀態所需的所有信息
  • Log Reconciliation:最魯棒的技術,最適用于財務系統
  • 版本控制:提供了隔離性,適用于復雜的結構

此外,我們還提到了很多次MongoDB最終將支持真正的原子性和文檔間的隔離事務。這已經作為分區的一部分了,但目前還只是內部的……只有文檔在同一分區時這一特性才可能實現,否則我們將回到不可擴展的SQL世界。

原文鏈接: How to Implement Robust and Scalable Transactions Across Documents with MongoDB(編譯/蔡仁君 責編/仲浩)

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久成人一区二区 | 亚洲区一区二区三区 | a级毛片观看 | 国产特黄大片aaaaa毛片 | 欧美在线视频a | 99r | 日本在线中文 | 黄色片一级黄色片 | 国产一区二区久久 | 亚洲欧美日韩中文在线 | 91精品国产综合久久久久久丝袜 | 成人在线视频一区 | 精精国产 | 久久高清精品 | 成人精品鲁一区一区二区 | 1000部精品久久久久久久久 | 国产成人精品免费视频大全 | 亚洲国产区 | 国产精品免费一区 | 欧美视频在线看 | 亚洲综合一区二区三区 | 性免费视频| 四季av一区二区三区免费观看 | 精品一区久久 | 国产在线播放网址 | 国产成人午夜 | 一级片久久 | 欧美日韩亚洲视频 | 欧美国产高清 | 99精品视频在线免费观看 | 91精品久久久久久久久 | www久 | 亚洲欧美另类在线观看 | 亚洲成av人影片在线观看 | 久久极品| av资源在线 | 久久久亚洲 | 麻豆精品视频在线 | 国产一区久久 | 国产成人深夜视频51 | 日韩精品久久久久久 |