Ruby on rails 使用实例变量进行缓存会降低性能

Ruby on rails 使用实例变量进行缓存会降低性能,ruby-on-rails,performance,Ruby On Rails,Performance,下面的所有示例都会调用pending\u通知?和reviewable\u通知 对于用户,我有以下一组实例方法: def pending_notifications? return true if reviewable_notifications.size > 0 end def reviewable_notifications @reviewable_notifications ||= self.employee.notifications.where(read: [nil, f

下面的所有示例都会调用
pending\u通知?
reviewable\u通知

对于
用户
,我有以下一组实例方法:

def pending_notifications?
  return true if reviewable_notifications.size > 0
end

def reviewable_notifications
  @reviewable_notifications ||= self.employee.notifications.where(read: [nil, false])
end
视图按以下方式使用它们:

<% if current_user.pending_notifications? %>
  <li><%= link_to fa_icon("envelope") + " #{current_user.reviewable_notifications.count} Notification(s)", user_notifications_path(id: current_user.id) %></li>
<% else %>
   <li><%= link_to fa_icon("inbox") + " Notification Center", user_notifications_path(id: current_user.id) %></li>
<% end %>
这很好,但在重构之前,我没有使用推荐的缓存实例变量的技术,但在分析中得到了完全相同的查询。此外,它始终以20毫秒以下的速度运行。下面是代码最初是如何编写的。为什么Rails不调用同一个查询两次?为什么用这种方式编写的代码性能更好

def pending_notifications?
  return true if self.employee.notifications.where(read: [nil, false]).size > 0
end

def reviewable_notifications
  self.employee.notifications.where(read: [nil, false])
end

差异不基于正在生成的查询…
如果你使用

`@reviewable_notifications ||= self.employee.notifications.where(read: [nil, false])`
只要
@reviewable\u notifications
为零,您将只点击数据库

当它获取一个值时,它将被使用

作为一个简单的示例,您可以在控制台中编写:

2.1.2 :001 > 5.times { User.first } # no caching, hit 5 times your DB
  User Load (0.2ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
 => 5 
2.1.2 :002 > 5.times { @user ||=  User.first } # caching, only 1 hit
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
 => 5 

当然,mysql有自己的查询缓存,因此如果同一个查询访问数据库,可能会从数据库查询缓存返回结果(在上面的示例中,您可以看到最后一个查询比第一个查询花费的时间更少,这可能是因为mysq从其缓存提供结果)

只有在Ruby中进行了昂贵的计算,并且您在多个地方使用了该值时,才需要记住这样的值

这种特殊的计算是在SQL中进行的,Rails在默认情况下已经缓存了DB查询,所以您没有看到任何更改

2.1.2 :001 > 5.times { User.first } # no caching, hit 5 times your DB
  User Load (0.2ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
 => 5 
2.1.2 :002 > 5.times { @user ||=  User.first } # caching, only 1 hit
  User Load (0.1ms)  SELECT  `users`.* FROM `users`   ORDER BY `users`.`id` ASC LIMIT 1
 => 5