MongoDB 單文檔原生支持原子性,也具有事務的特性,可是咱們提及事務,一般是指在多文檔中的實現,所以,MongoDB 在 4.0 版本支持了多文檔事務,4.0 對應於複製集的多表、多行,後續又在 4.2 版本支持了分片集的多表、多行事務操做。mongodb
原子性(Atomicity):事務必須是原子工做單元,對於其數據修改,要麼全執行,要麼全不執行。相似於 Redis 中我一般使用 Lua 腳原本實現多條命令操做的原子性。shell
一致性(Consistency):事務在完成時,必須使全部的數據都保持一致狀態。bash
隔離性(Isolation):由併發事務所作的修改必須與任何其餘併發事務所做的修改隔離(簡而言之:一個事務執行過程當中不該受其它事務影響)。併發
持久性(Durability):事務完成以後,對於系統的影響是永久性的。異步
在事務操做中會分別使用到 readConcern、writeConcern、readPreference 這幾個選項,用於控制 Session 的行爲,下面分別予以介紹。性能
事務使用事務級別的 writeConcern 來提交寫操做,決定一個事務的寫入成功與否要看 writeConcern 選項設置了幾個節點,默認狀況下爲 1。測試
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
在一個事務操做中使用事務級別的 readPreference 來決定讀取時從哪一個節點讀取。可方便的實現讀寫分離、就近讀取策略。
mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/admin?replicaSet=myRepl&readPreference=secondary
複製代碼
db.collection.find({}).readPref( "secondary")
複製代碼
測試時可藉助 db.fsyncUnlock() 對從節點鎖定,僅主節點寫入數據。
MongoDB 3.2 引入了 readConcern 來決定讀取的策略,可是與 readPreference 不一樣,readPreference 決定從哪一個節點讀取,readConcern 決定該節點的哪些數據是可讀的。主要保證事務中的隔離性,避免髒讀。
在啓動 Mongod 實例時,指定 --enableMajorityReadConcern 選項或在配置文件中配置
enableMajorityReadConcern=true
複製代碼
從新啓動實例,Mongo shell 登錄實例,使用 db._adminCommand( {getCmdLineOpts: 1})
查看配置,能夠看到在複製集中會多出一個配置,說明配置成功。
{
...
"parsed" : {
"replication" : {
"enableMajorityReadConcern" : true, // 變化之處
"oplogSizeMB" : 1024,
"replSet" : "May"
},
},
}
複製代碼
db.user.find().readConcern("majority")
複製代碼
MongoDB 的 readConcern 默認狀況下是髒讀,例如,用戶在主節點讀取一條數據以後,該節點未將數據同步至其它從節點,就由於異常掛掉了,待主節點恢復以後,將未同步至其它節點的數據進行回滾,就出現了髒讀。
readConcern 級別的 majority 能夠保證讀到的數據已經落入到大多數節點。因此說保證了事務的隔離性,所謂隔離性也是指事務內的操做,事務外是看不見的。
一個典型的應用場景是用戶寫入訂單數據(數據寫入 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 進行實踐說明,歡迎關注!