首页 文章

使用mgo上传到分片的MongoDB错误“完整的分片键必须在更新对象中进行收集:...”

提问于
浏览
1

使用MongoDB的Labix mgo API,我试图对分片集合执行增量操作 . 我可以使用通常的mgo.Change结构在非整数集合上做到这一点,但是当我尝试在分片集合上执行此操作时,我收到错误: full shard key must be in update object for collection: db_name.collection_name

适用于非分片集合的原始代码如下所示:

change := mgo.Change{
                ReturnNew: true,
                Upsert: true,
                Update: bson.M{
                    "$setOnInsert": bson.M{
                        "ci": r.Ci,
                        "dt": r.Dt,
                        "zi": r.Zi,
                    },
                    "$inc": &data,
                },
            }

            _, err := collection.Upsert(bson.M{"_id": id, "ci": r.Ci, "dt": r.Dt, "zi": r.Zi}, change); if err != nil {
                log.Println("FAILURE", err)
            }

但是,当我切换到分片集合时,在键 {ci: 1, dt: 1, zi: 1} 上分片我得到上面提到的错误 .

在尝试调试时,我试图用mgo弄清楚幕后发生了什么,并尝试直接插入mongo终端 .

db.collection.update({ "_id" : "98364_2013-12-11", "ci" : "16326", "dt" : "2013-12-11", "zi" : "98364"}, {$setOnInsert: { "ci" : "16326", "dt" : "2013-12-11", "zi" : "98364"} , $inc: {test :1}}, { upsert: true });

然而,这给我一个单独的错误: Can't modify shard key's value. field: ci: "16326" collection: db.collection 这是我认为一旦我弄清楚我的初始错误就必须弄明白的东西,但是我觉得很奇怪它因为它不应该用$ setOnInsert命令抛出这个错误要修改该值,只需将其设置在初始插入上即可 . 当我删除命令的$ setOnInsert部分时,所有错误都消失了,但我需要一种方法来确保设置这些值,因为它们在我编写的查询中很重要,以便将数据恢复 .

回到我的主要问题:我确实发现当我在与MongoDB终端进行交互时重新排列更新和upsert文档的顺序时,我得到了当我通过mgo时得到的错误,所以我试图非常严格地控制通过切换到bson.D在mgo.Change结构中传递的文档的顺序:

change := bson.D{
                {
                     "Update",
                     bson.D{
                         {"$setOnInsert", bson.D{
                                {"_id", id},
                                {"ci", r.Ci},
                                {"dt", r.Dt},
                                {"zi", r.Zi},
                                },
                         },
                         {"$inc", &data},
                     },
                },
                {
                    "Upsert",
                    true,
                },
            }
            log.Println(change)
            err := collection.Update(bson.D{{"_id", id},{ "ci", r.Ci},{ "dt", r.Dt}, {"zi", r.Zi}},change); if err != nil {
                log.Println("FAILURE", err)
            }

此时,打印更改对象会产生: [{Update [{$setOnInsert [{_id 11635_2013-12-11} {ci 3599} {dt 2013-12-11} {zi 11635}]} {$inc 0xc21dd9d8d0}]} {Upsert true}] 我认为这正是我应该根据Mongo's documentation以完全正确的顺序传递的更改对象,但我仍然得到相同的 full shard key must be in update object for collection: db.collection 错误 .

我意识到使用 collection.Find({_id: ... }).Apply(change, ...) 是一种可能的替代方案,当我使用Upsert(或更新)函数看到更高的性能(比快20倍)时它可以正常工作,速度绝对是一个优先级因为我处理数十个每秒数千个事件 .

我已经到了这样的程度,我觉得我已经尝试过每一个我能想到的想法,并会欣赏一双新的眼睛试图帮助我弄清楚发生了什么,所以任何帮助都会受到赞赏 .

1 回答

  • 2

    mgo.Change类型特定于Query.Apply方法,该方法运行MongoDB findAndModify命令并立即执行任何支持的修改 . 另一方面,Upsert方法采用一个修改文档,该文档将直接提供给mgo / bson进行编组 . 这些修改文档具有相同的format,无论您是通过Query.Apply(在mgo.ChangeUpdate 字段中)还是通过Collection.UpsertCollection.Update方法提供它们 .

    因此,观察到的错误是由于它试图使用 mgo.Change 作为插入的普通结构(换句话说,带有键"returnnew"的文档等),这绝对不是你想要的 . 例如,您提供的shell命令等同于mgo的直接转换:

    type M map[string]interface{}
    err := collection.Upsert(
        M{
            "_id": "98364_2013-12-11",
            "ci":  "16326",
            "dt":  "2013-12-11",
            "zi":  "98364",
        },
        M{
            "$setOnInsert": M{"ci": "16326", "dt": "2013-12-11", "zi": "98364"},
            "$inc":         M{"test": 1},
        },
    )
    

    尽管如此,这仍然是打破了,但出于不同的原因 . 作为错误消息中提到的服务器,这是尝试第二次设置分片键 . 请注意,upsert操作将使用查询文档中提供的字段,merge both查询文档和修改文档以创建要插入的最终文档 . 这意味着 $setOnInsert 文档中的分片键字段与查询文档中的分片键字段是冗余的 .

    我将改进该领域的文档,以减少人们因使用_1456391而感到困惑的机会 . 抱歉,添麻烦了 .

相关问题