PHP MongoDB驱动程序在慢速查询中打开许多连接


PHP MongoDB driver opens many connections on slow query

我们在MongoDB PHP驱动程序(v1.3)中执行慢速查询时会遇到半奇怪的行为。当请求很慢时,驱动程序似乎一直在打开连接,我不完全理解为什么。也许你们有一些建议。

以下是一些事实:

  • 网站和数据库在单个Ubuntu 13.04服务器上运行
  • 服务器是高端8核16GB RAM服务器
  • MongoDB v2.2.4
  • 网站运行PHP 5.4
  • Apache 2作为web服务器
  • PHP Mongo驱动程序1.3.x(应该是最新的1.3)
  • 网站使用条令ODM
  • 网站在任何时候都有大约50到100个并发用户
  • ulimit打开的文件(ulimit nofile)=64000

Memcache记录每天过期一次,然后进行慢速查询。这导致PHP打开了多达800个到MongoDB的连接(根据日志,通常我们有10个打开的连接)。我们的网站几乎是完全Memcached的,所以我们的数据库没有任何其他重要的负载。800个打开的连接使网站一开始有30秒的加载时间,然后抛出几种类型的MongoException(太多的连接/套接字异常)。

这是一个带有group by的丑陋查询。非常清楚的是,我们知道这个查询速度慢且愚蠢,我们今天将删除这个查询。只是不清楚为什么它会把整个网站搞砸。我们使用条令作为抽象层,但根据日志,这是对200000文档数据库(每个文档3个字段:id/product/date)的实际查询:

{"group":true,"keys":{"product":1},"initial":{"count":0},"reduce":"function (obj, prev) { prev.count++; }","options":[],"db":"Orders","collection":"History"}

查询完成后,其结果将写入Memcache 24小时。所以所有的新请求都是从Memcache获得的,而不是从MongoDB获得的。但是,它仍然保持着大约800个连接,问题本身并没有解决,网站过了一段时间就不再响应了。打开这800个连接大约需要10分钟。

这感觉像是典型的比赛状态。查询只是感觉不够重,无法在具有此负载的服务器上实际导致竞争条件。我的意思是,感觉不应该。

好的,那么问题是:

  1. 为什么PHP不断打开这么多连接
  2. 为什么MongoDB不能处理这个问题(这应该不是什么大问题,对吧?)
  3. 关于我们应该做什么,还有其他建议吗
  4. 我应该在连接和查询上设置超时来解决这个问题还是其他问题

我之所以这么问,是因为我们的网站增长非常快,我们预计未来会有更多的流量和MongoDB负载。

提前感谢!

假设您调用的是group命令,而不是执行基本的读取查询,那么您也可能在与MongoDB 2.2中的JavaScript解释器作斗争。直到2.4版本,JavaScript解释器才得到增强,以支持并发执行。如果这些组操作中的每一个都需要JS评估(至少对于reduce函数),那么您将看到广泛的资源匮乏。

对于"连接太多"的例外情况,我没有任何解释。即使是800个并发连接也远低于MongoDB的20000个限制(注意:SERVER-8943中的2.6版本中删除了这个限制)。

重构应用程序并避免group竞争条件的一个想法是使用单个文档作为PHP进程的锁,以重新计算结果并重新填充缓存。使用findAndModify,您可以有一个带有一些字符串_id(例如"Order.History group")和另一个active字段的单个文档。当PHP进程缓存未命中并且需要重新计算结果时,它可以首先尝试执行findAndModify并找到适当的_id,其中activefalse,在相同的原子操作中将active更新为true。只有在取回此锁定文档之后,它才能继续执行group命令。其他找不到锁定文档的PHP进程(因为active将不是false)可能会被指示休眠一段时间、返回陈旧数据或中止web请求。