简体   繁体   English

Rails:请求代码优化/重组

[英]Rails: Code optimization/restructuring requested

I have the following code snippet that works perfectly and as intended:我有以下代码片段,可以完美地按预期工作:

    # Prepares the object design categories and connects them via bit mapping with the objects.design_category_flag
    def prepare_bit_flag_positions
      # Updates the bit_flag_position and the corresponding data in the object table with one transaction
      ActiveRecord::Base.transaction do
        # Sets the bit flag for object design category
        ObjectDesignCategory.where('0 = (@rownum:=0)').update_all('bit_flag_position = 1 << (@rownum := 1 + @rownum)')

        # Resets the object design category flag
        Object.update_all(design_category_flag: 0)
        # Sets the new object design category bit flag
        object_group_relation = Object.joins(:object_design_categories).select('BIT_OR(bit_flag_position) AS flag, objects.id AS object_id').group(:id)
        join_str = "JOIN (#{object_group_relation.to_sql}) sub ON sub.object_id = objects.id"
        Object.joins(join_str).update_all('design_category_flag = sub.flag')
      end

But in my opinion it is quite difficult to read.但在我看来,这很难读。 So I tried to rewrite this code without raw SQL. What I created was this:所以我尝试在没有原始 SQL 的情况下重写这段代码。我创建的是这样的:

  def prepare_bit_flag_positions
      # Updates the bit_flag_position and the corresponding data in the object table with via transaction
      ActiveRecord::Base.transaction do
        # Sets the bit flag for the object color group
        ObjectColorGroup.find_each.with_index do |group, index|
          group.update(bit_flag_position: 1 << index)
        end

        # Resets the object color group flag
        Object.update_all(color_group_flag: 0)
        # Sets the new object color group bit flag
        Object.find_each do |object|
          object.update(color_group_flag: object.object_color_groups.sum(:bit_flag_position))
        end
      end
    end

This also works fine, but when I run a benchmark for about 2000+ records, the second option is about a factor of 65 slower than the first.这也很好用,但是当我为大约 2000 条记录运行基准测试时,第二个选项比第一个慢 65 倍。 So my question is:所以我的问题是:

Does anyone have an idea how to redesign this code so that it doesn't require raw SQL and is still fast?有谁知道如何重新设计此代码,使其不需要原始 SQL 并且仍然很快?

I can see 2 sources of slowing:我可以看到 2 个减速源:

  1. N+1 problem N+1问题
  2. Instantiating objects实例化对象
  3. Calls to DB调用数据库

This code has the N+1 Problem.此代码存在 N+1 问题。 I think this may be the major cause of the slowing.我认为这可能是放缓的主要原因。

Object.find_each do |object|
  object.update(color_group_flag: object.object_color_groups.sum(:bit_flag_position))
end

Change to改成

Object.includes(:object_color_groups).find_each do |object|
  ...
end

You can also use Object#update class method on this code (see below).您还可以在此代码上使用Object#update class 方法(见下文)。

I don't think you can get around #2 without using raw SQL. But, you will need many objects (10K or 100K or more) to see a big difference.我认为不使用原始 SQL 就无法绕过 #2。但是,您将需要许多对象(10K 或 100K 或更多)才能看到很大的不同。

To limit the calls to the DB, you can use Object#update class method to update many at once.要限制对数据库的调用,您可以使用Object#update class 方法一次更新多个。

ObjectColorGroup.find_each.with_index do |group, index|
  group.update(bit_flag_position: 1 << index)
end

to

color_groups = ObjectColorGroup.with_index.map do |group, index|
  [group.id, { bit_flag_position: group.bit_flag_position: 1 << index }]
end.to_h
ObjectColorGroup.update(color_groups.keys, color_groups.values)

The following is a single query, so no need to change.以下是单个查询,因此无需更改。

Object.update_all(color_group_flag: 0)

Reference:参考:

  1. ActiveRecord#update class method API ActiveRecord#update class 方法 API
  2. ActiveRecord#update class method blog post ActiveRecord#update class 方法博文
  3. Rails Eager Loading Rails 预加载

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM