Rails pluck 与 ActiveRecord 关系的性能
Rails performance of pluck vs ActiveRecord Relation
当我只需要一个或两个字段时,我一直默认使用 pluck
,但我最近对性能差异进行了基准测试,并拔掉了:
ActiveRecord::Base.logger.level = 1
n = 5000
Benchmark.bm do |x|
x.report('Country.all: ') { n.times { Country.all } }
x.report('Country.unscoped: ') { n.times { Country.unscoped } }
x.report('base priority_order: ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name) } }
x.report('.includes(:translations): ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name).includes(:translations) } }
x.report('.pluck(:name): ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name).includes(:translations).pluck(:name) } }
x.report('.pluck(:name) only: ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name).pluck(:name) } }
end
# Results
=begin
user system total real
Country.all: 0.990000 0.020000 1.010000 ( 1.023518)
Country.unscoped: 0.050000 0.000000 0.050000 ( 0.050440)
base priority_order: 1.350000 0.010000 1.360000 ( 1.356728)
.includes(:translations): 1.460000 0.000000 1.460000 ( 1.462635)
.pluck(:name): 8.230000 0.150000 8.380000 ( 11.168291)
.pluck(:name) only: 6.980000 0.150000 7.130000 ( 9.903130)
=end
如您所见,pluck 的实时性要慢得多。我选择了第 4 个 .includes(:translations)
以避免 N+1 查询(前两个实际上只是基线,没有提供所需的数据)。
这个基准测试是否准确,而且 pluck 真的很慢,还是这不是故事的全部?由于没有创建 ActiveRecord 对象,应该可以节省大量内存和一些时间。
您的测试不准确。 Country.all
实际上并没有获取行,它只是 return 一个活动记录关系。与您的其他范围相同。只有 pluck 才会真正查询数据。
我有一个国家 table,有 243 行。列数不多(id、name、iso2、iso3)。我将您的查询调整为 map(&:name)
前两个,因此结果与 pluck 相同。普拉克大胜。
ActiveRecord::Base.logger.level = 1
n = 1000
Benchmark.bm do |x|
x.report('Country.all.map(&:name): ') { n.times { Country.all.map(&:name) } }
x.report('Country.unscoped.map(&:name): ') { n.times { Country.unscoped.map(&:name) } }
x.report('Coutry.pluck(:name): ') { n.times { Country.unscoped.pluck(:name) } }
end
输出:
user system total real
Country.all.map(&:name): 3.830000 0.140000 3.970000 ( 4.328655)
Country.unscoped.map(&:name): 3.780000 0.030000 3.810000 ( 4.159162)
Coutry.pluck(:name): 1.550000 0.040000 1.590000 ( 1.879490)
当我只需要一个或两个字段时,我一直默认使用 pluck
,但我最近对性能差异进行了基准测试,并拔掉了:
ActiveRecord::Base.logger.level = 1
n = 5000
Benchmark.bm do |x|
x.report('Country.all: ') { n.times { Country.all } }
x.report('Country.unscoped: ') { n.times { Country.unscoped } }
x.report('base priority_order: ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name) } }
x.report('.includes(:translations): ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name).includes(:translations) } }
x.report('.pluck(:name): ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name).includes(:translations).pluck(:name) } }
x.report('.pluck(:name) only: ') { n.times { Country.unscoped.with_translations(I18n.locale).order(list_at_top: :desc).order(:name).pluck(:name) } }
end
# Results
=begin
user system total real
Country.all: 0.990000 0.020000 1.010000 ( 1.023518)
Country.unscoped: 0.050000 0.000000 0.050000 ( 0.050440)
base priority_order: 1.350000 0.010000 1.360000 ( 1.356728)
.includes(:translations): 1.460000 0.000000 1.460000 ( 1.462635)
.pluck(:name): 8.230000 0.150000 8.380000 ( 11.168291)
.pluck(:name) only: 6.980000 0.150000 7.130000 ( 9.903130)
=end
如您所见,pluck 的实时性要慢得多。我选择了第 4 个 .includes(:translations)
以避免 N+1 查询(前两个实际上只是基线,没有提供所需的数据)。
这个基准测试是否准确,而且 pluck 真的很慢,还是这不是故事的全部?由于没有创建 ActiveRecord 对象,应该可以节省大量内存和一些时间。
您的测试不准确。 Country.all
实际上并没有获取行,它只是 return 一个活动记录关系。与您的其他范围相同。只有 pluck 才会真正查询数据。
我有一个国家 table,有 243 行。列数不多(id、name、iso2、iso3)。我将您的查询调整为 map(&:name)
前两个,因此结果与 pluck 相同。普拉克大胜。
ActiveRecord::Base.logger.level = 1
n = 1000
Benchmark.bm do |x|
x.report('Country.all.map(&:name): ') { n.times { Country.all.map(&:name) } }
x.report('Country.unscoped.map(&:name): ') { n.times { Country.unscoped.map(&:name) } }
x.report('Coutry.pluck(:name): ') { n.times { Country.unscoped.pluck(:name) } }
end
输出:
user system total real
Country.all.map(&:name): 3.830000 0.140000 3.970000 ( 4.328655)
Country.unscoped.map(&:name): 3.780000 0.030000 3.810000 ( 4.159162)
Coutry.pluck(:name): 1.550000 0.040000 1.590000 ( 1.879490)