mysql - Rails + MySQL - 从 3 个具有特定格式的表加载数据(非常慢 "has_many"关系)

标签 mysql ruby-on-rails ruby has-many model-associations

我有这样的表结构:

用户

id | name
1  | John
2  | Peter
3  | Claire

服务

id | name
1  | home
2  | garden
3  | music

用户服务

id | user_id | services_id
1  | 1       | 3
2  | 2       | 3
3  | 1       | 2

型号: 用户.rb

class User < ActiveRecord::Base
  has_many :user_services, dependent: :destroy
  has_many :services, through: :user_services
end

user_service.rb

class UserService < ActiveRecord::Base
  belongs_to :user
  belongs_to :service
  validates_uniqueness_of :service_id, scope: :user_id
end

service.rb

class Service < ActiveRecord::Base
  has_many :user_services
  has_many :users, through: :user_services
end

这就是我所做的( Controller ):

def do_search
  @results = search_users
  @users_a = []
  @users_b = []

  @results[:users].each do |result|
    is_user_b = 0
    result.user_services.each do |service|
      if service.service_id == 28
        @users_a << "a string..."
        is_user_b = 1
        break
      end
    end
    if is_user_b == 0
      @users_b << "string B"
    end
  end
  render "results_users"
end

def search_users
  @users = User.within(distance, origin: city).where('gender = ?', 0).order("distance ASC")
end

在 View 中:

<% @results[:users].each do |result| %>
  <li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">
    <div><%= result.facility_name %></div>
    <ul class='service-icons cf'>
      <% result.services.each do |service| %>
        <li class='service-icon'>
          <span class="streamline" aria-hidden="true" data-icon="<%= raw service.icon %>" title="<%= service.label %>"></span>
        </li>
      <% end %>
    </ul>
  </li>
<% end %>

问题:这个流程非常慢 - SQL 查询非常快(0.4s),但是处理这 3 个循环(1 个在 Controller 中,2 个在 View 中)非常慢,导致显示结果持续大约 <强>60 秒。

这让我很惊讶,因为我有几乎相同的型号,但是带有 cars (cars, services, car_services),几乎与 users 中相同数量的属性(cars),在 cars 中是 600k 条记录,在 users 90K 只需 2 秒即可完成。我花了一整天的时间试图找出原因,但还是不明白怎么可能。

有人可以帮我吗,为什么循环遍历 has_many 关联需要这么多时间? (如果我评论所有 3 个循环,则页面将在 2 秒内加载)

提前感谢您抽出时间。

最佳答案

实际上 Active Record 在处理大型数据集方面确实很糟糕。

但是,您应该做的第一件事是通过预先加载关联记录来避免代码在这些循环中执行大量 n+1 查询,这些记录将在迭代中访问

 User.includes(:user_services, :services).within(...).where(...)

一次获取所有记录比每次循环迭代发出单独的 +1 查询要快得多。

您还应该考虑为用户及其服务引入分页或其他一些用户发起的额外获取(例如“显示更多”)。无论如何,人类将很难处理大量数据。

如果这种优化还不够,并且一次获取所有数据的要求是不可协商的,那么就使用原始 SQL。为每一行实例化 ActiveRecord 对象图的影响是巨大的。由此,我们已经能够将计费报告的优化时间从几分钟缩短到几秒。

我还推荐使用“rack-mini-profiler”gem 来分析性能问题。

关于mysql - Rails + MySQL - 从 3 个具有特定格式的表加载数据(非常慢 "has_many"关系),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27216956/

相关文章:

java - 从 Java Spring Boot 映射 mysql 数据库中的tinyint(3)

ruby-on-rails - 尽管手动请求工作正常,但无法使用 yt gem 读取标题

ruby-on-rails - Rails 管理命名空间和管理模型

ruby - 查找并返回嵌套数组中最长的数组及其大小

ruby-on-rails - Ruby 中的互斥锁不适用于 Redis?

mysql - 如何在MySQL中获取特定日期每3小时的聚合结果?

mysql - 数据库设计 : Where to store account balance?

php - 如何确保 sql 表未锁定/停止并发 cron

ruby-on-rails - rails : Is there away to get the Date object that is the closest Monday to today?

ruby-on-rails - 对象未加载