mongoDB中的冪等性

Hoegh發表於2017-04-05
最近在學習MongoDB的過程中,接觸到一個新名詞——冪等性。

先來看一下度孃的解釋。

冪等


冪等(idempotent、idempotence)是一個數學與計算機學概念,常見於抽象代數中。

在程式設計中,一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。

冪等函式,或冪等方法,是指可以使用相同引數重複執行,並能獲得相同結果的函式。這些函式不會影響系統狀態,也不用擔心重複執行會對系統造成改變。例如,“setTrue()”函式就是一個冪等函式,無論多次執行,其結果都是一樣的.更復雜的操作冪等保證是利用唯一交易號(流水號)實現。

看懂了嗎?這個解釋比較抽象,不要著急。接下來看一下什麼是冪等性問題。

冪等性問題


所謂冪等,簡單地說,就是對介面的多次呼叫所產生的結果和呼叫一次是一致的。擴充套件一下,這裡的介面,可以理解為對外發布的HTTP介面或者其他介面,也可以是接收訊息的內部介面,甚至是一個內部方法或操作。

那麼我們為什麼需要介面具有冪等性呢?設想一下以下情形:
  • 在App中下訂單的時候,點選確認之後,沒反應,就又點選了幾次。在這種情況下,如果無法保證該介面的冪等性,那麼將會出現重複下單問題。
  • 在接收訊息的時候,訊息推送重複。如果處理訊息的介面無法保證冪等,那麼重複消費訊息產生的影響可能會非常大。

在分散式環境中,網路環境更加複雜,因前端操作抖動、網路故障、訊息重複、響應速度慢等原因,對介面的重複呼叫機率會比集中式環境下更大,尤其是重複訊息在分散式環境中很難避免。

分散式環境中,有些介面是天然保證冪等性的,如查詢操作。有些對資料的修改是一個常量,並且無其他記錄和操作,那也可以說是具有冪等性的。其他情況下,所有涉及對資料的修改、狀態的變更就都有必要防止重複性操作的發生。透過間接的實現介面的冪等性來防止重複操作所帶來的影響,成為了一種有效的解決方案。

看了上面App的例子好理解多了。那冪等性在併發系統中如何保證呢?

高併發系統中如何保證資料冪等性


在系統開發過程中,經常遇到資料重複插入、重複更新、訊息重發傳送等等問題,因為應用系統的複雜邏輯以及網路互動存在的不確定性,會導致這一重複現象,但是有些邏輯是需要有冪等特性的,否則造成的後果會比較嚴重,例如訂單重複建立,這時候帶來的問題可是非同一般啊。

一、系統的冪等性

     冪等是資料中得一個概念,表示N次變換和1次變換的結果相同。

二、高併發的系統如何保證冪等性

1、查詢

    查詢的API,可以說是天然的冪等性,因為你查詢一次和查詢兩次,對於系統來講,沒有任何資料的變更,所以,查詢一次和查詢多次一樣的;

2、MVCC方案

    多版本併發控制,update with condition更新帶條件,這也是在系統設計的時候,合理的選擇樂觀鎖,透過version或者其他條件,來做樂觀鎖,這樣保證更新及時在併發的情況下,也不會有太大的問題。

   例如update table_xxx set name=#name#,version=version+1 where version=#version# ,或者是 update table_xxx set quality=quality-#subQuality# where quality-#subQuality# >= 0

3、單獨的去重表

    如果涉及到的去重的地方特別多,例如ERP系統中有各種各樣的業務單據,每一種業務單據都需要去重,這時候,可以單獨搞一張去重表,在插入資料的時候,插入去重表,利用資料庫的唯一索引特性,保證唯一的邏輯;

4、分散式鎖

    還是拿插入資料的例子,如果是分佈是系統,構建唯一索引比較困難,例如唯一性的欄位沒法確定,這時候可以引入分散式鎖,透過第三方的系統,在業務系統插入資料或者更新資料,獲取分散式鎖,然後做操作,之後釋放鎖,這樣其實是把多執行緒併發的鎖的思路,引入多多個系統,也就是分散式系統中得解決思路;

5、刪除資料

  刪除資料,僅僅第一次刪除是真正的運算元據,第二次甚至第三次刪除,直接返回成功,這樣保證了冪等;

6、插入資料的唯一索引

   插入資料的唯一性,可以透過業務主鍵來進行約束,例如一個特定的業務場景,三個欄位肯定確定唯一性,那麼,可以在資料庫表新增唯一索引來進行標示。

   這裡有一個場景,API層面的冪等,例如提交資料,如何控制重複提交,這裡可以在提交資料的form表單或者客戶端軟體,增加一個唯一標示,然後服務端,根據這個UUID來進行去重,這樣就能比較好的做到API層面的唯一標示

看到這裡,你也許對冪等性已經有了深入的理解。最後,我們看一下MongoDB中的冪等性。

MongoDB中的冪等性


MongoDB是由C++語言所編寫的一種面向文件的非關係型資料庫(是一種NoSql資料庫實現),也是介於關係型資料庫和非關係型資料庫之間的資料儲存產品,其提供了高效能、高可用、高可擴充及基於分散式儲存的資料庫,是非關係型資料庫中功能最豐富,最類似關係型資料庫的一種集合、文件格式的資料庫。

在MongoDB中,文件的操作不支援事務,但是其對文件的儲存,修改及刪除等都是原子的,也就是要麼操作完成,要麼操作不完成,不存在中間不確定狀態,因此可以保證資料的完整性。
MongoDB官方提供了一種做法,就是分兩階段提交,基本原理就是利用寫操作的冪等性,但是有個前提條件:業務實現過程中,可以忽略中間態的不一致性,但最終結果是一致的,實際上絕大多數需求,只要結果一致就可,也希望MongoDB在這方面做更好的擴充和最佳化。

1、資料模型
舉個例子,不過我們需要為文件新增一個計數器欄位,它沒有實際的業務作用,只是用來作為操作原子性的標誌位avaliable,具體程式碼如下:
{
     "_id" :ObjectId("57e89964b316d2e13cc0ba9b"),
     "username" :"marky@123.com",
     "nickname" : "marky",
     "address" : "雲端路1024號,柯南私募基金大廈",
     "contact" :"13141250012",
     "created" : "2012-07-07",
     "orders" : [
         ObjectId("57e89b3ab316d2e13cc0ba9c"),
         ObjectId("57e89bcfb316d2e13cc0ba9d")
     ],
     "available": 1
}

注意:
此種新增一個計數器標誌位的方式雖然可以實現原子性,但對於業務的緊密度及可理解方面不友好,所以實際上,我們只在比較敏感的文件操作時才使用,比如:金錢交易等。
 
2、如何操作
如何操作?其實就是使用findAndModify()方法實現即可,如果找到的文件的計數器available值不為-1為大於0時,則代表有新的操作狀態,然後在更新新的操作,同時同步修改available為預設值,具體如下:
>db.user.findAndModify({query:{_id:ObjectId("57e89964b316d2e13cc0ba9b"), available:{$gt:0}},update:{$inc:{ available:-1},$set:{created:'2012-07-08'}}})
 
返回的結果:
{
     "_id" : ObjectId("57e89964b316d2e13cc0ba9b"),
     "username" :"marky@123.com",
     "nickname" : "marky",
     "address" : "雲端路1024號,柯南私募基金大廈",
     "contact" :"13141250012",
     "created" :"2012-07-08",
     "orders" : [
         ObjectId("57e89b3ab316d2e13cc0ba9c"),
         ObjectId("57e89bcfb316d2e13cc0ba9d")
     ],
     " available" : 0
}


~~~~~~~ the end~~~~~~~~~
hoegh
2017.04.05

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30162081/viewspace-2136668/,如需轉載,請註明出處,否則將追究法律責任。

相關文章