从同一客户端取消先前的 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().inprog
和 db.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()
获取当前操作的列表,并通过匹配op
、query
和client
等字段来识别要取消的操作
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 操作的主要内容,如果未能解决你的问题,请参考以下文章
GraphQL/Apollo 客户端:如果查询是先前查询的子集,则从缓存中获取数据