ActiveRecords选择(:id).collect与pluck(:id)方法:为什么纯AR"采摘"慢点?

时间:2015-09-02 11:45:19

标签: ruby benchmarking

我正试图从我的文章模型中获取所有ID。我可以用两种方式做到:

 Article.select(:id).collect{|a| a.id}
  Article Load (2.6ms)  SELECT "articles"."id" FROM "articles" 

OR

2.2.1 :006 > Article.pluck(:id)
   (4.3ms)  SELECT "articles"."id" FROM "articles"

是什么给出的?为什么AR比Ruby版本慢?

即使我对Ruby方法进行基准测试,它似乎也更快:

Benchmark.measure{Article.select(:id).collect{|a| a.id}}
  Article Load (1.9ms)  SELECT "articles"."id" FROM "articles"
 => #<Benchmark::Tms:0x007feb12060658 @label="", @real=0.026455502957105637, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.020000000000000018, @total=0.020000000000000018>

3 个答案:

答案 0 :(得分:5)

您的基准测试不准确。首先,正如您所看到的,数据库端的两个执行都会触发相同的查询

<input onclick="select()">

因此,数据库时间应该被视为无关紧要。显然,这两个查询具有不同的执行时间,如控制台所示,但这是正常的,就好像您运行相同的查询100次执行时间每次都可能不同,因为它取决于各种变量,如机器负载,数据库状态等

由于数据库执行时间可以被认为是等效的,因此它与基准测试无关。

因此,您需要比较的是Ruby执行时间和分配。与SELECT "articles"."id" FROM "articles" 相比,Pluck应该更快,更轻量,它不分配ActiveRecord对象,而只返回选定的值。

如果你真的想要对方法进行基准测试,你应该模拟数据库时间(这显然是可变的,但与此基准无关),只有基准分配和两种不同的Ruby方法。

长话短说,collect通常效率更高。

答案 1 :(得分:2)

select用于获取具有特定属性的记录。它返回一个ActiveRecord::Relation对象。

pluck可以与select使用相同的方式使用,但是它会返回一个选定属性的数组。

您可以浏览this article

答案 2 :(得分:1)

您不应该注意控制台中记录的时间,而应该执行正确的benchmark。这是在嵌套查询中使用select时可以快多少倍(快30倍!)

[25] pry(main)> ActiveRecord::Base.logger = nil
[25] pry(main)> Benchmark.bmbm do |bm|
[25] pry(main)*   bm.report('select') do
[25] pry(main)*     2000.times do
[25] pry(main)*       ActiveRecord::Base.uncached do
[25] pry(main)*         Agenda.where(organization_id: Organization.limit(2).select(:id))
[25] pry(main)*       end
[25] pry(main)*     end
[25] pry(main)*   end
[25] pry(main)*   bm.report('pluck') do
[25] pry(main)*     2000.times do
[25] pry(main)*       ActiveRecord::Base.uncached do
[25] pry(main)*         Agenda.where(organization_id: Organization.limit(2).pluck(:id))
[25] pry(main)*       end
[25] pry(main)*     end
[25] pry(main)*   end
[25] pry(main)* end
Rehearsal ------------------------------------------
select   0.147064   0.001408   0.148472 (  0.149976)
pluck    1.494075   0.077501   1.571576 (  4.175919)
--------------------------------- total: 1.720048sec

             user     system      total        real
select   0.140494   0.000301   0.140795 (  0.140956)
pluck    1.200006   0.070174   1.270180 (  3.958814)

正如已经解释的那样,这是因为在第一种情况下,该查询作为嵌套选择合并到主查询中,而使用pluck执行第一个查询以获取ID,然后将其用于运行主查询。

请记住,此类基准始终取决于您的数据库,索引,要处理的行数等。如有疑问,请始终运行快速基准以确认这种假设!

作为记录,这是针对您的“简单”查询的类似基准,它确实显示pluck速度更快,尽管差距不大:

[27] pry(main)> Benchmark.bmbm do |bm|
[27] pry(main)*   bm.report('select') do
[27] pry(main)*     2000.times do
[27] pry(main)*       ActiveRecord::Base.uncached do
[27] pry(main)*         Organization.select(:id).collect(&:id)
[27] pry(main)*       end
[27] pry(main)*     end
[27] pry(main)*   end
[27] pry(main)*   bm.report('pluck') do
[27] pry(main)*     2000.times do
[27] pry(main)*       ActiveRecord::Base.uncached do
[27] pry(main)*         Organization.pluck(:id)
[27] pry(main)*       end
[27] pry(main)*     end
[27] pry(main)*   end
[27] pry(main)* end
Rehearsal ------------------------------------------
select   1.669422   0.080951   1.750373 (  4.318759)
pluck    1.081312   0.064770   1.146082 (  3.797270)
--------------------------------- total: 2.896455sec

             user     system      total        real
select   1.601772   0.069176   1.670948 (  4.298829)
pluck    1.094950   0.064165   1.159115 (  3.811094)