MongoDB 事務 —— 基礎入門篇

MongoDB 單文檔原生支持原子性,也具有事務的特性,可是咱們提及事務,一般是指在多文檔中的實現,所以,MongoDB 在 4.0 版本支持了多文檔事務,4.0 對應於複製集的多表、多行,後續又在 4.2 版本支持了分片集的多表、多行事務操做。mongodb

事務四大特性

  • 原子性(Atomicity):事務必須是原子工做單元,對於其數據修改,要麼全執行,要麼全不執行。相似於 Redis 中我一般使用 Lua 腳原本實現多條命令操做的原子性。shell

  • 一致性(Consistency):事務在完成時,必須使全部的數據都保持一致狀態。bash

  • 隔離性(Isolation):由併發事務所作的修改必須與任何其餘併發事務所做的修改隔離(簡而言之:一個事務執行過程當中不該受其它事務影響)。併發

  • 持久性(Durability):事務完成以後,對於系統的影響是永久性的。異步

Read Concern/Write Concern/Read Preference

在事務操做中會分別使用到 readConcern、writeConcern、readPreference 這幾個選項,用於控制 Session 的行爲,下面分別予以介紹。性能

事務和 Write Concern

事務使用事務級別的 writeConcern 來提交寫操做,決定一個事務的寫入成功與否要看 writeConcern 選項設置了幾個節點,默認狀況下爲 1。測試

幾個選項值:

  • w:0 設置爲 0 無需關注寫入成功與否。
  • w:1 ~ 任意節點數 自定義節點數設置,複製集中不得大於最大節點數。默認狀況下爲 1,表示寫入到 Primary 節點就開始往客戶端發送確認寫入成功。
  • w:"majority" 大多數節點成功原則,例如一個複製集 3 個節點,2 個節點成功就認爲本次寫入成功。
  • w:"all" 因此節點都成功,才認爲寫入成功。
  • j:true 默認狀況 j:false,寫操做到達內存算做完成。若是設置爲 j:true,寫操做只有到達 journal 文件纔算成功。
  • wtimeout: 寫入超時實踐

設置示例:

writeConcern: {
    w:"majority" // 大多數原則
    j:true,
    wtimeout: 5000,
}
複製代碼

JavaScript 使用示例:ui

db.user.insert({name: "Jack"}, {writeConcern: {w: "majority"}})
複製代碼

建議

對於重要數據能夠應用 w:"majority" 設置,普通數據 w:1 設置則能夠保證性能最佳,w 設置的節點數越多,等待的延遲也就越大,若是 w 等於總節點數,一旦其中某個節點出現故障就會致使整個寫入失敗,也是有風險的。spa

docs.mongodb.com/manual/refe…rest

事務和 Read Preference

在一個事務操做中使用事務級別的 readPreference 來決定讀取時從哪一個節點讀取。可方便的實現讀寫分離、就近讀取策略。

可選值如下 5 個:

  • primary 只從主節點讀取,默認值。
  • primaryPreferred 優先選擇主節點,不可用時選擇從節點
  • secondary 只在從節點讀取
  • secondaryPreferred 優先在從節點讀取,從節點不可用時選擇主節點。
  • nearest 選擇附近節點

場景選擇

  • primary/primaryPreferred:適合於數據實時性要求較高的場景,例如,訂單建立完畢直接跳轉到訂單詳情,若是選擇從節點讀取,可能會形成主節點數據寫入以後,從節點還未複製的狀況,由於複製過程是一個異步的操做。
  • secondary/secondaryPreferred:適應用於數據實時性要求不高的場景,例如,報表數據、歷史訂單。還能夠減輕對主節點的壓力。

使用示例

  • 連接字符串配置:
mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/admin?replicaSet=myRepl&readPreference=secondary
複製代碼
  • Mongo Shell:
db.collection.find({}).readPref( "secondary")
複製代碼

測試時可藉助 db.fsyncUnlock() 對從節點鎖定,僅主節點寫入數據。

docs.mongodb.com/manual/core…

事務和 Read Concern

MongoDB 3.2 引入了 readConcern 來決定讀取的策略,可是與 readPreference 不一樣,readPreference 決定從哪一個節點讀取,readConcern 決定該節點的哪些數據是可讀的。主要保證事務中的隔離性,避免髒讀。

可選值

  • available:讀取全部可用的數據。
  • local:僅讀取當前分片的數據。
  • majority:讀取在大多數節點上提交完成的數據。
  • snapshot:讀取最近快照中的數據。

更新配置項

在啓動 Mongod 實例時,指定 --enableMajorityReadConcern 選項或在配置文件中配置

enableMajorityReadConcern=true
複製代碼

從新啓動實例,Mongo shell 登錄實例,使用 db._adminCommand( {getCmdLineOpts: 1}) 查看配置,能夠看到在複製集中會多出一個配置,說明配置成功。

{
...
"parsed" : {
    "replication" : {
        "enableMajorityReadConcern" : true, // 變化之處
        "oplogSizeMB" : 1024,
        "replSet" : "May"
    },
},
}
複製代碼

使用示例

db.user.find().readConcern("majority")
複製代碼

readConcern 總結

MongoDB 的 readConcern 默認狀況下是髒讀,例如,用戶在主節點讀取一條數據以後,該節點未將數據同步至其它從節點,就由於異常掛掉了,待主節點恢復以後,將未同步至其它節點的數據進行回滾,就出現了髒讀。

readConcern 級別的 majority 能夠保證讀到的數據已經落入到大多數節點。因此說保證了事務的隔離性,所謂隔離性也是指事務內的操做,事務外是看不見的。

readConcern 參考

讀寫分離實踐

一個典型的應用場景是用戶寫入訂單數據(數據寫入 Primary),當即調用查詢接口,因爲採用讀寫分離模式,連接字符串設置 readPreference=secondaryPreferred 訂單寫入主節點以後並不能保證數據當即同步到從節點,若此時直接由從節點讀取數據, 偶爾會出現訂單數據沒法找到,用戶就會感受很奇怪,明明下了訂單,卻又查找不到,形成一些異常訂單。

圖片描述

一種致使下單以後再次查找丟失訂單的的寫法以下:

db.order.insert({"id": "123456789"})
db.order.find({"id": "123456789"}).readPref("secondaryPreferred")
複製代碼

解決方法一:

設置 readPreference=primary,將複製集的節點讀取由從節點轉換爲主節點。

這種方式一個缺點是數據量大了以後會增長主節點的壓力,也就沒有了主從分離的模式。

解決方法二:

使用 writeConcern、readConcern 組合來解決,即保證讀寫分離模式,也保證了數據的一致性

// 寫入時保證大多數節點寫入成功
db.order.insert({"id": "123456789"}, {writeConern: {w: "majority"}})

// 讀取時保證這條數據已在大多數節點存在
db.order.find({"id": "123456789"}).readPref("secondaryPreferred").readConcern("majority")
複製代碼

本文是對 MogoDB 事務的一個初步瞭解,Read Concern/Write Concern/Read Preference 這些在後續事務實踐中都會應用,但願你們能夠事先進行一個瞭解,在接下來的一篇文章中,我會介紹 MongoDB 的事務應該如何應用,同時結合 Node.js 進行實踐說明,歡迎關注!

Reference

相關文章
相關標籤/搜索