视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Mongdb的upsert出现E11000duplicatekeyerrors的错误分析
2020-11-09 16:22:10 责编:小采
文档


Mongdb的upsert出现E11000 duplicate key errors的错误分析 昨日上线的系统,今天查日志时发现有不少E11000 duplicate key errors的报错日志,当时 十分费解,因为用的upsert,这个是原子操作,避免了线程并发带来的问题,但为什么会报 重复主键的错误呢? w


Mongdb的upsert出现E11000 duplicate key errors的错误分析

昨日上线的系统,今天查日志时发现有不少E11000 duplicate key errors的报错日志,当时
十分费解,因为用的upsert,这个是原子操作,避免了线程并发带来的问题,但为什么会报
重复主键的错误呢?

www.2cto.com

Java代码

update( DBObject q , DBObject o , boolean upsert , boolean multi )

第一个参数是查询条件,第一个参数是要做的操作。

我的处理逻辑是这样的,集合中有3列联合唯一索引,此外还有6列属性值,4列要增加的列。

我的查询条件q是这么写的

Java代码

QueryBuilder.start("mb").is(bsc.getMb()).and("sb").is(bsc.getSb()).and("fd").is(bsc.getFd())

.and("mft").is(bsc.getMft()).and("mst").is(bsc.getMst()).and("mtt").is(bsc.getMtt())

.and("sft").is(bsc.getSft()).and("sst").is(bsc.getSst()).and("stt").is(bsc.getStt())

.get()

mb,sb,fd是联合唯一索引。

要做的操作是

Java代码

QueryBuilder.start("$inc").is(QueryBuilder.start("mc0").is(bsc.getMc0()).and("mc1").is
(bsc.getMc1())

.and("sc0").is(bsc.getSc0()).and("sc1").is(bsc.getSc1()).get())

.get();

在测试时是一点问题都没有的

但为什么会有duplicate key errors呢?

upsert的原理是先根据q去查询,若没有结果,则先insert,若有结果则根据o进行update

所以唯一可能的问题是那6个属性列,因为它们不是唯一索引列,但仍然出现在查询条件中,
这样就会出现索引列的值一样,但是属性列的值不一样,这样Mongodb进行insert,由于库中
已经有了和当前值唯一索引相同的记录,故出现duplicate key errors。属性列的值是与索引列的值
相关联的,但可能在计算的时候出错,导致属性列的值得到错误的结果。

www.2cto.com

同时,在Mongodb的官网jira中也有这个问题的讨论https://jira.mongodb.org/browse/SERVER-5928

Java代码

// Insert test document

> db.test.insert({_id:1, version:2, data:3})

// Get current document

> var doc = db.test.findOne({_id:1});

> printjson(doc);

{

"_id" : 1,

"version" : 2,

"data" : 3

}

// Perform some updates on doc.data

...

// Update

> db.test.update({_id:1, version:doc.version},{$set:{data:doc.data}, $inc:{version:1}}, true);
db.getLastError();

// Update succeeded

null

// Try once more

> db.test.update({_id:1, version:doc.version},{$set:{data:doc.data}, $inc:{version:1}}, true);

// Failed with "non-unique key" since "version" field changed

E11000 duplicate key error index: d2_feed0.feed.$_id_ dup key: { : 1 }

注意倒数第二行的

Java代码

Failed with "non-unique key" since "version" field changed

和我得出的结论是一致的。

以后在使用upsert时,在查询条件中尽量只有唯一索引的列。

修改之后的代码

www.2cto.com

Java代码

DBObject q = QueryBuilder.start("mb").is(bsc.getMb()).and("sb").is(bsc.getSb()).and("fd").
is(bsc.getFd()).get();

DBObject o = QueryBuilder.start("$set")

.is(QueryBuilder.start("mft").is(bsc.getMft()).and("mst").is(bsc.getMst()).and("mtt").
is(bsc.getMtt())

.and("sft").is(bsc.getSft()).and("sst").is(bsc.getSst()).and("stt").is(bsc.getStt())

.get())

.and("$inc").is(QueryBuilder.start("mc0").is(bsc.getMc0()).and("mc1").is(bsc.getMc1())

.and("sc0").is(bsc.getSc0()).and("sc1").is(bsc.getSc1()).get())

.get();

使用set来解决可能出现的不一致问题。

下载本文
显示全文
专题