我目前正在参与 Ruby on Rails 的一个项目,但我对此并没有任何经验。现在,我们遇到了一些性能问题,我认为这些问题与执行太多查询有关。
Service我们在控制器中设置了一个模型,如下所示:
@services = Service.includes(:points).with_points
Run Code Online (Sandbox Code Playgroud)
范围with_points在服务中定义如下:
scope :with_points, -> { joins(:points).where.not(points: []).distinct }
Run Code Online (Sandbox Code Playgroud)
(我认为这里不需要 where 子句,但这可能与问题无关。)
然后在视图中,我们使用获取的services和链接点,如下所示:
<% @services.each do |s| %>
<div class="col-xs-12 col-sm-6 col-lg-4 serviceDiv" data-rating="<%= s.service_ratings %>" >
<% if s.service_ratings == "A" %>
<% grade = "rating-a" %>
<!-- etc. -->
Run Code Online (Sandbox Code Playgroud)
现在,据我在研究时所见,在尝试列出表中的所有行时,这是一种相对正常的模式。但是,当我查看日志时,看起来好像为每个服务执行了单独的查询?
# This query is what I'd expect:
SQL (1.6ms) SELECT DISTINCT "services"."id" # etc, etc
# But then we also get one of these for every service
Service Exists (1.5ms) SELECT 1 AS one FROM "services" WHERE "services"."name" = $1 AND ("services"."id" != $2) LIMIT $3
# And quite a few of these for every service:
CACHE Service Exists (0.0ms) SELECT 1 AS one FROM "services" WHERE "services"."name" = $1 AND ("services"."id" != $2) LIMIT $3
Run Code Online (Sandbox Code Playgroud)
现在,我的预感是那些“服务存在”行是坏消息。它们是什么,它们来自哪里?我在这里还缺少其他相关的内容吗?
在@MarekLipka 在这个问题的评论中指出之后,我设法找到了问题的根源。不确定将来有多少人会遇到这种设置,但为了以防万一,我会分享它。
线索就在我们的访问中s.service_ratings,它实际上不是数据库中的列。ActiveRecord 查询跟踪s.service_ratings指出了视图中引用的所有这些查询的来源,这是一个危险信号。
service_ratings实际上是模型上的一种方法(假设我在这里使用了正确的术语)Service,除了根据模型的几个属性的计算返回一个值之外,还调用self.update_attributes该方法来将该值实际存储在数据库中。这意味着每次我们检索该模型的数据以显示它时,我们还会运行另一个查询来将该值保存到数据库中 - 通常是冗余的。
换句话说,我们现在的解决方案是要么在其他时间点运行计算并将其存储到数据库中一次,要么每次需要时重新计算并且根本不存储它。