关于MongoDb出现com.mongodb.MongoWriteException: E11000异常解决最佳方案
mongo原生的_id生成策略是: 时间戳+主机+进程号+序列 组成
出现的原因
根本的原因:是重写了MongoDb生成_id的策略,这种情况在$group中很常见的错误,例如下面
$group
{
_id: {
status:"$status"
zone:"$zone",
messagetype:"$messagetype"
},
count: {
$sum: 1
}
}
这样的情况,我们实际是通过group重写了mongo原本生成_id唯一主键字段的生成策略,第一次执行程序也许不会出现任何的问题,但是第二次执行就有可能出现_id重复的情况,假如你的业务逻辑在定时任务中,那么就会出现很严重的问题.导致你第一天的数据统计没有问题,以后可能会说不准.那么如何解决这种问题呢?网上有很多说自己写一个生成策略,那么问题来了,假如你的数据量有几十亿级别的,那么你的生成策略一定不会重复吗?其实最简单的方法就是,在你插入结果到mongo的时候把上面的_id从Document中移除,使用mongo原生的生成策略.
解决方案
上面已经把问题讲清楚了,下面我们通过一个简单的实例解决下这个问题:其实很简单在你插入文档前把_id字段移除了,然后又使用mongo原生的生成策略即可
private void MessageStatistics() {
List<DBObject> conditionList = Arrays.asList(
new BasicDBObject().
append("$match", new BasicDBObject().
append("sessionid", new BasicDBObject().
append("$gte", DateUtils.getYesterday()).
append("$lt", DateUtils.getToday())
).
append("type", 0)
),
//四个维度: zone,messagetype,channelid,status
new BasicDBObject().
append("$group", new BasicDBObject().
append("_id", new BasicDBObject().
append("status", "$status").
append("zone", "$zone").
append("channelid", "$channelid").
append("messagetype", "$messagetype")).
append("count", new BasicDBObject().
append("$sum", 1)
)
),
new BasicDBObject().append("$project", new BasicDBObject().
append("messageType", "$_id.messagetype").
append("channelId", "$_id.channelid").
append("status", "$_id.status").
append("zone", "$_id.zone").
append("count", "$count")
)
);
AggregateIterable aggregateIterable = MessageMongo.getInstance().getCollection().withReadPreference(ReadPreference.secondary()).aggregate(conditionList).allowDiskUse(true).batchSize(50);
aggregateIterable.forEach((Consumer<? super Document>) document -> {
document.append("createTime", DateUtils.getCurrentTime());
document.append("statisticalMode", 1);
//解决_id重复的问题,使用mongo的自动生成策略
document.remove("_id");
MessageStatisticsMongo.getInstance().getCollection().insertOne(document);
});
}