我在F5负载均衡器(专业管理,它没有向> 1主机发送流量)后面有一个记录良好的多个Java服务器池,运行Tomcat并安装了我的应用程序,连接到分片的mongo集群。我使用主要自然键的base64编码SHA-1哈希作为_id。当要创建新记录时,我会做一个非常基本的记录:
BasicDBObject query = new BasicDBObject();
query.put("userId", userId);
query.put("_id", id);
DBObject user = getUsersCollection().findOne(query);
if (user == null) {
getUsersCollection().insert(new UserObject(userId));
}
这是简化的。实际上,对此用户的预先存在进行多次检查,包括应该抛出自定义异常的一个,并且不会触发任何一个。流量日志表示单个传入创建请求,以下是发生的事情的示例:
2014-01-19 20:03:45,167 [http-bio-7950-exec-827]:[...] : ERROR FATAL [...] - Internal server error
[...]: com.mongodb.MongoException$DuplicateKey: { "serverUsed" : "[...]" , "singleShard" : "replicaset_2/host1:27017,host2:27017,host3:27017" , "err" : "E11000 duplicate key error index: Users.$_id_ dup key: { : \"HASH\" }" , "code" : 11000 , "n" : 0 , "lastOp" : { "$ts" : 1390190614 , "$inc" : 1} , "connectionId" : 335764 , "ok" : 1.0}
然而,在我的Users集合中,已创建记录:
db.Users.findOne({_ ID: “HASH”}):
{
"_id" : "HASH",
"createDate" : ISODate("2014-01-20T04:03:45.161Z"),
...
}
由于时间戳的原因,我认为这很重要。我们有一个时区问题,但除此之外我将6ms差异解释为mongo集群和我的应用服务器之间的时钟偏差。此传入流量没有其他记录(并且它在服务器之间反弹时记录,甚至 - 没有别的!)所以我99.999%确信我的SINGLE LEGITIMATE插入调用是插入并抛出错误。
任何有关如何/为何如此发生的理论都将受到高度赞赏。如果需要回答有关更多信息的问题,我会运行跟踪器和示例。
答案 0 :(得分:0)
您正在搜索同时使用_id
和userId
字段的用户。尝试注释掉这一行:query.put("_id", id);
。
在您的代码中不清楚Java变量userId
来自何处。还不清楚UserObject
如何设置_id
。
总体而言,它看起来像您搜索用户的方式以及您创建他的方式不匹配,即在该用户上定义唯一键的内容。
一个修复可以替换这些行:
query.put("userId", userId);
query.put("_id", id);
使用:
query.put("_id", userId);
要使_id
字段成为您的userId
。