寫操作事務
writeConcern - w
writeConcern決定寫操作落到多少節點上才算成功,其取值包括:
- 0:發起寫操作,不關心結果
- n 1 <= n <= 集群最大數據節點數:寫操作復制到n個節點才算成功。
- majority: 寫操作被復制到大多數節點才算成功
發起寫操作的線程將阻塞到寫操作到達指定節點數才算成功;三節點復制集,默認是1,即主節點寫好了就成了; 對於重要數據設置w:majority,對於普通數據設置w:1
語法:
db.test.insert({count:1}, {writeConcern:{w:3,wtimeout:3000}})
journal - j
journal決定寫操作到什么程度才算成功
- true: 寫操作落到journal文件中
- false: 寫到內存就算成功
讀操作事務
讀數據,主要有兩個問題:
- 從哪里讀?- readPreference
- 什么樣的數據可以讀?(隔離性) - readConcern
readPreference
- primary: 只從主節點讀
- primaryPreferred:優先主節點
🌰 用戶下單后轉到訂單詳情頁,可能從節點還沒有復制到新訂單 - secondary: 只從從節點
🌰 生成報表 - secondaryPreferred: 從節點優先
🌰 用戶查詢自己的歷史訂單 - nearest: 選擇最近的節點
🌰 用戶上傳的圖片分發到全世界
語法:
db.test.find({a:123}).readPref("secondary")
Tag
例如五節點復制集,三個硬件好一點的,打上tag"online",另外兩個打上tag"analyse",生成報表只選擇"analyse"節點,如圖所示:

readConcern
這個節點上哪些數據是可以讀的:
- available: 所有可用數據
- local:所有可用且屬於當前分片
local是默認值,在復制集上local和available無區別;只是在分片集上,當做chunk遷移時,只讀當前分片,否則把還沒遷移成功的另外個分片上遷移中的數據也讀出來 - majority:大多數節點提交完成的數據
實現方式:和MySQL的MVCC類似,節點上保存多個版本的數據,根據需求返回不同版本數據。使用majority可以有效避免“臟讀”。
首先,MongoDB對事務的支持更多指的是一個寫操作能否被持久化下來,所以“提交”的概念就是,寫到了多數節點(那么數據就不會丟失,永遠持久化了),在這個層面上,讀majority就不會“臟讀”,讀到的數據都是落到大多數節點的,而不會因為節點crash某個寫操作在讀之后丟失了(相當於回滾了)
- linearizable:可線性化讀取文檔,保證該“讀”能讀到上一個“寫”,保證操作的線性順序
- snapshot:讀取最近快照中的數據 (隔離級別最高,相當於serializable)
安全的讀寫分離
readConcern和writeConcern配合實現安全的讀寫分離
🌰用戶下單,並從從節點讀訂單數據:
不安全:
db.orders.insert({oid:100, sku:"kite", q:1})
db.orders.find({oid:100}).readPref("secondary")
安全:
db.orders.insert({oid:100, sku:"kite", q:1},{writeConcern:{w:"majority"}})
db.orders.find({oid:100}).readPref("secondary").readConcern("majority")
多文檔事務
mongoDB 4.2之后全面支持了多文檔事務,但是推薦還是能不用盡量不用,盡量通過合理的數據模型設計來規避事務的必要性。
語法:
try (ClientSession clientSession = client.startSession()) {
clientSession.startTransaction();
collection.insertOne(clientSession, docOne);
collection.insertOne(clientSession, docTwo);
clientSession.commitTransaction();
}
注意:
- 多文檔事務的讀必須從主節點讀
- 使用MongoDB4.2兼容的驅動
- readConcern只應該在事務級別設置,不能在每次讀寫操作上
