从同一客户端取消先前的 MongoDB 操作

Posted

技术标签:

【中文标题】从同一客户端取消先前的 MongoDB 操作【英文标题】:Cancel previous MongoDB operation from the same client 【发布时间】:2014-02-23 16:05:16 【问题描述】:

我有一个包含 3257477 个城市的 MongoDB 集合,我在 NodeJS 上使用 Mongoose 来访问它。我反复向它发出请求(每 500 毫秒一次)。请求通常会很快得到答复。但是,当我输入错误时,查询需要很长时间,并且请求开始堆积,直到初始请求得到答复。以下是我收集的一些请求和响应日志:

21:48:50    started query for "new"
21:48:50    finished query for "new"
21:48:52    started query for "newj ljl"  // blockage
21:48:54    started query for "newj"
21:48:55    started query for "new"
21:48:57    started query for "new ye"
21:48:59    started query for "new york"
21:49:08    finished query for "newj ljl" // blockage removed, quick queries flood in
21:49:08    finished query for "new"
21:49:08    finished query for "new york"
21:49:08    finished query for "new ye"
21:49:23    finished query for "newj"

我可以取消客户提出的请求,因此我不必担心以错误顺序返回的查询。而且我现在对如何使查询更快不感兴趣,因为查询实际正确拼写的速度很快。

我想知道新请求如何取消同一客户端发出的旧请求。换句话说,"newj ljl""newj" 到达时被取消,"newj""new" 到达时被取消,依此类推。如果它只是要被扔掉,为什么要捆绑数据库?

有没有合适的方法来做到这一点?

更新:

我知道db.currentOp().inprog,我想我可以使用该数组中文档的client 属性来了解它是否是重复请求,但我不太清楚如何访问它来自猫鼬。我也不确定 何时 这样做,或者我如何知道哪个请求是从 this 客户端产生的(因此要取消哪个)。我想要一个使用 Mongoose 的实际代码示例,或者如果可能的话,使用本机 NodeJS MongoDB 驱动程序!

下面是一些示例代码:

models.City.find( ... )
    .exec(function (err, cities) 

    );

【问题讨论】:

How to run db.killOp() using the MongoDB native Node.js driver?的可能重复 【参考方案1】:

以下是我想出的解决问题的方法。

我可以轻松地从 Mongo shell 执行 db.currentOp().inprogdb.killOp(),但我真的需要在需要时从 Mongoose 自动执行此操作。由于您可以使用require('mongoose').connection.db 引用 MongoDB 驱动程序,因此您可以通过对以下集合执行“查询”来执行这些命令:

db.collection('$cmd.sys.inprog');
db.collection('$cmd.sys.killop');

完整的解决方案:

var db = require('mongoose').connection.db,
    // get the client IP address
    ip = request.headers['x-forwarded-for'] || 
         request.connection.remoteAddress || 
         request.socket.remoteAddress ||
         request.connection.socket.remoteAddress;

// same thing as db.currentOp().inprog
db.collection('$cmd.sys.inprog').findOne(function (err, data) 
    if (err) throw err;

    data.inprog.filter(function (op) 
        // get the operation's client IP address without the port
        return ip == op.client.split(':')[0];
    ).forEach(function(op)
        // same thing as db.killOp()
        db.collection('$cmd.sys.killop')
          .findOne( 'op': op.opid , function (err, data) 
              if (err) throw err;
          );
    );

    // start the new cities query
    models.City.find( ... )
        .exec(function (err, cities) 

        );
);
有用的网址: https://groups.google.com/forum/#!topic/mongodb-user/1wFp7AqWnM4 drop database with mongoose How to determine a user's IP address in node

【讨论】:

我收到MongoError: Invalid collection name: dbname-test.$cmd.sys.inprog @killianc 这从 MongoDB 3.2 开始不起作用,您需要这样做:db.command( killOp: 1, op: item.opid );【参考方案2】:

您可以尝试使用db.killOp()

http://docs.mongodb.org/manual/reference/method/db.killOp/#db.killOp

更新:您可以从db.currentOp()获取当前操作的列表,并通过匹配opqueryclient等字段来识别要取消的操作

http://docs.mongodb.org/manual/reference/method/db.currentOp/#db.currentOp

【讨论】:

感谢您的回答。我知道我可以做db.killOp(),但我想知道更多我怎么知道当前操作是由同一个客户端启动的,因此可以被杀死? 我真的在寻找一个例子,尤其是如何通过 Mongoose 做到这一点。抱歉,如果不清楚。我用更多细节更新了这个问题。【参考方案3】:

您绝对可以使用 killop 执行此操作,并且上述解决方案看起来可以解决上述问题。不过,我认为可能值得深入挖掘。

当您的查询不返回任何结果时,您的查询速度明显变慢,这一事实似乎很不寻常。这有点像完整的收藏扫描。要问的问题是,首先,您是否设置了索引,其次,您是否使用通用正则表达式进行查询? MongoDB 并不能很好地处理像 "name" : /.*new york.*/ 这样的正则表达式搜索。

另外,整个“用户每点击一个键就发送一个http请求”的方式简单优雅,但也会造成一些不必要的服务器负载。也许一个搜索按钮或一个客户端超时,如果用户在 1 秒内没有按键,您只发送一个请求,这可能有助于减轻对 killop 方法的需求。

【讨论】:

感谢您的意见!我已经将请求反跳到 500 毫秒(如最初的问题中所述),但我很好奇查询这样一个字符串的更好方法是什么。 尝试使用 .explain (docs.mongodb.org/manual/reference/method/cursor.explain) 通过 mongo shell 运行您的查询,它会告诉您 MongoDB 用于查询的索引。丢失的索引很糟糕。如果您必须通过正则表达式进行搜索,您应该使用以“^”开头的正则表达式,MongoDB 可以有效地对其进行索引,或者考虑文本搜索 (docs.mongodb.org/manual/core/index-text)

以上是关于从同一客户端取消先前的 MongoDB 操作的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB - 如何确保多次更新都成功?

GraphQL/Apollo 客户端:如果查询是先前查询的子集,则从缓存中获取数据

linux下MongoDB客户端shell基本操作

如何从 jQuery 中的“滚动”事件中取消绑定所有先前添加的事件? [复制]

MongoDB嵌入Java

如何使用c ++驱动程序将两个客户端IP连接到同一个mongodb