我有一个名为'msgdb'的mongodb数据库,以及一个名为'roster'的集合。 “名册”中的文件如下:
{
"userId": "sn99933289",
"rosterGroups": [
{
"groupId": "242326",
"groupName": "buddy",
"rosterItems": [
]
}
],
"version": NumberInt(1)
}
我将字段“_id”设置为分片键,如下所示:
db.runCommand({shardcollection:“msgdb.roster”,key:{_ id:“hashed”}})
调用upsert方法的Java代码:
import com.mongodb.BasicDBObject;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
private void saveRoster(Roster roster) {
BasicDBObject dbObject = new BasicDBObject();
mongoTemplate.getConverter().write(roster, dbObject);
dbObject.remove("version");
dbObject.remove("_class");
Update update = new Update();
for (Map.Entry<String, Object> entry : dbObject.entrySet()) {
update.set(entry.getKey(), entry.getValue());
}
update.inc("version", 1);
Criteria criteria = Criteria.where("_id").is(roster.getUserId());
Query query = new Query(criteria);
mongoTemplate.upsert(query, update, "roster");
}
我从应用程序日志中找到了DuplicateKeyException。似乎upsert操作不是原子的。更具体的异常堆栈跟踪如下。如何避免这种异常?任何帮助/提示都表示赞赏。
org.springframework.dao.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: msgdb.roster index: _id_ dup key: { : "sn88332138" }'; nested exception is com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error collection: msgdb.roster index: _id_ dup key: { : "sn88332138" }'
at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:69) ~[spring-data-mongodb-1.7.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2011) ~[spring-data-mongodb-1.7.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:463) ~[spring-data-mongodb-1.7.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.doUpdate(MongoTemplate.java:1086) ~[spring-data-mongodb-1.7.1.RELEASE.jar:na]
at org.springframework.data.mongodb.core.MongoTemplate.upsert(MongoTemplate.java:1052) ~[spring-data-mongodb-1.7.1.RELEASE.jar:na]
at com.suning.im.server.center.roster.RosterServiceImpl.saveRoster(RosterServiceImpl.java:235) ~[classes:na]
at com.suning.im.server.center.roster.RosterServiceImpl.getRoster(RosterServiceImpl.java:68) ~[classes:na]
at com.suning.im.server.center.roster.RosterServiceImpl.getRosterPresencesWithVirtual(RosterServiceImpl.java:251) ~[classes:na]
at sun.reflect.GeneratedMethodAccessor54.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_25]
at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_25]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) ~[na:1.7.0_25]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) ~[na:1.7.0_25]
at java.lang.Thread.run(Thread.java:724) ~[na:1.7.0_25]
答案 0 :(得分:2)
Upsert不是原子的。这是由Mongo文档暗示的:https://docs.mongodb.org/v3.0/reference/method/db.collection.update/。
为避免多次插入同一文档,只使用upsert:如果查询字段是唯一索引的,则为true。
所以我建议仅在索引为唯一的字段上使用upsert。如果查询语句中有多个字段,则可以使用复合索引或将这些字段散列在一起(例如,对文档的id)。幸运的是,在使用_id字段时,您正在使用唯一索引。
将upsert包装在发出更新请求的try catch中可以解决您的问题,因为您确定曾经在catch块中存在该文档。