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