如何在长时间运行的mapreduce操作中避免游标超时?

Zai*_*sud 5 mapreduce mongodb

我正在针对分片mongos群集上的实例对大型集合运行重复检测mapreduce操作,我希望操作花费的时间超过10分钟:

m = function () {
    emit(this.fieldForDupCheck, 1);
}
r = function (k, vals) {
    return Array.sum(vals);
}
res = db.Collection.mapReduce(m, r, { out : "dups" });
Run Code Online (Sandbox Code Playgroud)

运行此操作会在处理大约10分钟后出现以下错误:

uncaught exception: map reduce failed:{
"ok" : 0,
"errmsg" : "MR post processing failed: { result: "dups", errmsg: "exception: getMore: cursor didn't exist on server, possible restart or timeout?", code: 13127, ok: 0.0 }"
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用mapReduce调用添加noTimeout选项,.addOption(DBQuery.Option.noTimeout)但这会导致shell中出现JS错误Object [object Object] has no method 'addOption'

如何在长时间运行的mapreduce操作中避免游标超时?

Gus*_*yer 6

您还没有提到您正在使用的MongoDB版本,但解决方案与此处介绍的内容类似.我将在2.2.4之上进行演示,这是Ubuntu 13.04附带的内容.

这样做的问题确实是将选项注入光标.这就是addOption生活的地方:

> var cursor = db.test.find()
> cursor.addOption
function (option) {
    this._options |= option;
    return this;
}
Run Code Online (Sandbox Code Playgroud)

我们来看看如何mapReduce定义:

> db.test.mapReduce
function (map, reduce, optionsOrOutString) {
    var c = {mapreduce:this._shortName, map:map, reduce:reduce};
    ...
    var raw = this._db.runCommand(c);
    ...
    return new MapReduceResult(this._db, raw);
}
Run Code Online (Sandbox Code Playgroud)

因此它构建了一个文档来运行命令runCommand.让我们进一步研究它:

> db.runCommand
function (obj) {
    if (typeof obj == "string") {
        var n = {};
        n[obj] = 1;
        obj = n;
    }
    return this.getCollection("$cmd").findOne(obj);
}
Run Code Online (Sandbox Code Playgroud)

所以命令是通过运行的findOne.我们来看看它:

> db.test.findOne
function (query, fields, options) {
    var cursor = this._mongo.find(this._fullName, this._massageObject(query) || {}, fields, -1, 0, 0, options || this.getQueryOptions());
    if (!cursor.hasNext()) {
        return null;
    }
    var ret = cursor.next();
    ...
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

啊,这里有一些有趣的东西.光标正在使用来自参数的标志进行初始化options,遗憾的是,它不会对你的情况有所帮助,因为runCommand它保留了未设置,但它与getQueryOptions()来自集合的OR值.我们来看看它:

> db.collection.getQueryOptions
function () {
    var options = 0;
    if (this.getSlaveOk()) {
        options |= 4;
    }
    return options;
}
Run Code Online (Sandbox Code Playgroud)

糟糕..这不好.因此,我们无法访问游标,也无法通过非hackish手段将查询选项注入执行的命令.

好吧,但我们已经学到了很多关于map reduce命令实际上是如何通过该过程传递给服务器的.它只是一个针对数据库中特定集合查询的文档.这意味着我们可以构建相同的查询,并自己运行它,但提供任何必要的标志.

我不会解决构建整个MongoDB命令并为您设置结果的麻烦,但我只是通过isMaster命令来向您展示它实际上是有效的.

这是没有任何标志的命令:

> db.getCollection("$cmd").findOne({isMaster: 1}).ismaster
true
Run Code Online (Sandbox Code Playgroud)

为了看到效果的差异,我们将tcpdump与数据库的通信.我们可以在有线协议文档中看到相关的标志位于集合名称之前,为32位整数,因此很容易发现转储的相关部分:

    .                  vvvvvvvvv
    0x0040:  d407 0000 0000 0000 7465 7374 2e24 636d  ........test.$cm
    0x0050:  6400 0000 0000 ffff ffff 1700 0000 0169  d..............i
Run Code Online (Sandbox Code Playgroud)

好.我们可以在集合名称之前看到四个字节归零.

现在,让我们在提供一些标志的同时做同样的事情.我们从上面的调试部分了解到,查询标志可以作为第三个选项提供findOne,所以让我们这样做:

> db.getCollection("$cmd").findOne({isMaster: 1}, undefined, 0xBEEF).ismaster
true
Run Code Online (Sandbox Code Playgroud)

并看到转储:

    .                  vvvvvvvvv
    0x0040:  d407 0000 efbe 0000 7465 7374 2e24 636d  ........test.$cm
    0x0050:  6400 0000 0000 ffff ffff 1700 0000 0169  d..............i
Run Code Online (Sandbox Code Playgroud)

嘿,我们的旗帜交付到了应有的位置,我们也可以看到它被反转,这意味着字节被编码为little-endian,与文档匹配.

因此,这意味着您可以提供标志DBQuery.Option.noTimeout作为第三个选项findOne,并按照与我们相同的方式手动编写map-reduce命令,如文档中所述isMaster,您将获得所需的内容.