where()和find()存在奇怪的性能问题

4

Mongoid 3.1.6 Rails 3.2.21 MongoDB 2.4.9

我们发现使用 find()where().first 存在奇怪的性能问题:

$ rails c
2.1.5 :001 > Benchmark.ms { User.find('5091e4beccbce30200000006') }
 => 7.95 
2.1.5 :002 > Benchmark.ms { User.find('5091e4beccbce30200000006') }
 => 0.27599999999999997 
2.1.5 :003 > Benchmark.ms { User.find('5091e4beccbce30200000006') }
 => 0.215 
2.1.5 :004 > exit

$ rails c
2.1.5 :001 > Benchmark.ms { User.where(id: '5091e4beccbce30200000006').first }
 => 7.779999999999999 
2.1.5 :002 > Benchmark.ms { User.where(id: '5091e4beccbce30200000006').first }
 => 4.84 
2.1.5 :003 > Benchmark.ms { User.where(id: '5091e4beccbce30200000006').first }
 => 5.297 
2.1.5 :004 > exit

这两个似乎都会触发相同的查询。有人可以解释一下为什么我们在性能上看到如此巨大的差异吗?
配置:
production:
  sessions:
    default:
      uri: <%= REDACTED %>
      options:
        consistency: :strong
        safe: true
        max_retries: 1
        retry_interval: 0
  options:
    identity_map_enabled: true

1
@SalvadorDali: 在Mongoid中,id将成为MongoDB内部的_id。Mongoid通常会为您将字符串转换为ObjectId。 - mu is too short
当您将identity_map_enabled更改为false时会发生什么?我认为.where不使用identity_map_enabled。 - kuadrosx
1
@mu太短:也许是,但它在发布的配置中已启用。 - kuadrosx
@kuadrosx:是的,就是这样。我认为值得注意的是,我发现使用没有身份映射的Mongoid 4时,我看到类似的结果(或者至少where(...).firstfind花费更长时间)。 - mu is too short
1
@mu 太短了:是的,我不知道为什么mongoid在这里缩短了:_id。这里有关于这个问题的讨论 https://github.com/mongoid/mongoid/issues/3768 - kuadrosx
显示剩余3条评论
2个回答

0

以下是我的假设,为什么第一个查询要慢几个数量级(我从mongo的角度来写,对ruby一无所知)。

第一次发出查询时,它不在工作集中,这导致性能较慢。连续几次后,它已经在那里了,因此性能更好。如果您只有很少的文档,我会觉得这种行为很奇怪(因为我预期所有文档都在工作集中)。

$where 的第二部分让我感到惊讶,因为我期望所有数字都比find()大(第一个事件并非如此),因为:

$where 提供了更大的灵活性,但需要数据库处理集合中每个文档的 JavaScript 表达式或函数。


1
Mongoid的where方法并不总是映射到MongoDB的$where操作符。where方法只是为MongoDB构建一个选择器,因此M.where(:a => 'b')就像MongoDB内部的db.ms.find({ a: 'b' })。该名称用于匹配通常的ActiveRecord/SQL名称。 - mu is too short
@muistooshort,感谢你的两个解释。我不理解这部分内容:“where doesn't (always) map”。它怎么可能呢?在我看来,它要么映射到$where,要么不映射。而且很可能我的答案在Ruby中并没有实际意义,所以我倾向于将其删除。 - Salvador Dali
1
我的意思是,如果你使用 M.where(:$where=> '...')where 可以使用 $where 操作符,但除非你明确使用它,否则它不会使用 $where。是的,这并不是我用英语表达得最清晰的方式。 - mu is too short

0

看起来find使用标识映射,而where则不使用。如果我将identity_map_enabled设置为false,那么findwhere的性能相同。

故事的寓意:尽可能使用find而不是where

我听说在Mongoid 4.x中删除了标识映射。所以这个问题只影响旧版本的人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接