浏览 5798 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-10-25
昨日上线的系统,今天查日志时发现有不少E11000 duplicate key errors的报错日志,当时十分费解,因为用的upsert,这个是原子操作,避免了线程并发带来的问题,但为什么会报重复主键的错误呢?
update( DBObject q , DBObject o , boolean upsert , boolean multi ) 第一个参数是查询条件,第一个参数是要做的操作。
我的处理逻辑是这样的,集合中有3列联合唯一索引,此外还有6列属性值,4列要增加的列。
我的查询条件q是这么写的 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是联合唯一索引。 要做的操作是 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。属性列的值是与索引列的值相关联的,但可能在计算的时候出错,导致属性列的值得到错误的结果。
同时,在Mongodb的官网jira中也有这个问题的讨论https://jira.mongodb.org/browse/SERVER-5928 // 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 } 注意倒数第二行的 Failed with "non-unique key" since "version" field changed
和我得出的结论是一致的。 以后在使用upsert时,在查询条件中尽量只有唯一索引的列。 修改之后的代码 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来解决可能出现的不一致问题。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-10-29
mysql 多连接+隔离级别造成的吧,把隔离级别搞到最高估计就没事了。就是会影响性能。
|
|
返回顶楼 | |