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 方法 是直接批量插入