MongoDB 默認寫入關注可能保存數據丟失問題分析
問題描述:
EDI服務進行優化,將原有MQ發送成功並且DB寫入成功,兩個條件都達成,響應接收訂單數據成功,修改為只有有一個條件成功就響應接收數據成功。只要發送MQ成功,就代表數據已經給下游客單系統,保存DB數據失敗可以接受,優先保證數據不阻斷。發送MQ失敗,保存DB數據成功,代表我們已經接受到訂單數據,可以通過容錯服務進行后續處理。這樣就可以保證MQ與DB只要有任何一個是沒問題就不會影響客戶訂單數據的正常下發。
近期發生MQ服務端忙碌,拒收生成消息。理論上數據應該已經保存到DB。通過容錯服務就可以處理。但是客單系統確實沒有收到,並且數據庫中也並未查詢到。ELK確實記錄了客戶是已經發送訂單下發請求並且我們也返回成功了。
經過ELK日志記錄排查,可以得到MQ發送失敗,DB保存沒有拋異常,應該算DB已經保存成功。但是實際卻沒成功。
經過一番排查,應該是默認的保存數據的寫入關注策略問題。
默認寫入關注設置為:WriteConcern.NORMAL
WriteConcern概述:
WriteConcern.NONE: 沒有異常拋出
WriteConcern.NORMAL: 僅拋出網絡錯誤異常,沒有服務器錯誤異常
WriteConcern.SAFE: 拋出網絡錯誤異常、服務器錯誤異常;並等待服務器完成寫操作。
WriteConcern.MAJORITY: 拋出網絡錯誤異常、服務器錯誤異常;並等待一個主服務器完成寫操作。
WriteConcern.FSYNC_SAFE: 拋出網絡錯誤異常、服務器錯誤異常;寫操作等待服務器將數據刷新到磁盤。
WriteConcern.JOURNAL_SAFE: 拋出網絡錯誤異常、服務器錯誤異常;寫操作等待服務器提交到磁盤的日志文件。
WriteConcern.REPLICAS_SAFE: 拋出網絡錯誤異常、服務器錯誤異常;等待至少2台服務器完成寫操作
Spring MongoDB 設置
<mongo:client-options write-concern="SAFE " />
Spring data MongoDB
@Autowired
MongoTemplate mongoTemplate;
mongoTemplate.setWriteConcern(WriteConcern.ACKNOWLEDGED);
mongoTemplate.save(data,"ediData");
說明:
@Deprecated
public static final WriteConcern SAFE = ACKNOWLEDGED;
SAFE 已經被棄用,源碼可以看到直接設置為了 ACKNOWLEDGED
關於Spring data jap MongoDB MongoRepository 接口的說明與源碼分析:
MongoRepository提供了簡單直接的幾個方法,其中就有 save 方法。
單條保存源碼流程。
批量保存
可以看到如果是新增數據(沒有設置ID),用save 其實也是走的 insert方法。
Mongodb insert 與 save 的區別說明
insert:當主鍵"_id"在集合中存在時,不做任何處理。 拋異常
save:當主鍵"_id"在集合中存在時,進行更新。 數據整體都會更新 ,新數據會替換掉原數據 ID 以外的所有數據。如ID 不存在就新增一條數據
save 方法 需要遍歷列表,一個個插入, 而 insert 方法 是直接批量插入