【问题标题】:Rails: Global instance variables in RailsRails:Rails 中的全局实例变量
【发布时间】:2026-01-23 16:30:01
【问题描述】:

在我的应用中,用户可以通过表单提交食谱,该表单将发布在网站上。在食谱发布之前,它们会通过版主进行审核。

因此,我的应用会在导航栏中显示版主当前未发布的所有食谱的计数,如下所示:

为了实现这一点,我目前执行以下操作:

application.rb

before_action :count_unpublished

def count_unpublished
  @unpublished_count = Recipe.where(:published => false).count
end

_navbar.html.erb

<li>
  <%= link_to recipes_path do %>
     Recipes <span class="badge" style="background-color:#ff7373"><%= @unpublished_count %></span>
  <% end %>
</li>

它有效,但我现在想知道这是否是一个好习惯,因为我的应用程序现在每次操作都会访问食谱数据库(这可能不是很优雅)。

是否有更好的解决方案来实现这一目标?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 activerecord


    【解决方案1】:
    cache_key = "#{current_user.id}_#{unpublished_count}"
    @unpublished_count = Rails.cache.fetch(cache_key, expires_in: 12.hours) do 
      Recipe.where(:published => false).count
    end
    

    更多:http://guides.rubyonrails.org/caching_with_rails.html#low-level-caching

    【讨论】:

      【解决方案2】:

      为避免命中数据库,您可以引入缓存。它有多种形式:更快的存储(memcached、redis)、进程内缓存(全局/类变量)等等。它们都有一个相同的问题:您需要知道何时使缓存无效。

      查看本指南以获得一些想法:Caching with Rails

      如果我是你,我不会关心这个,直到我的分析器告诉我这是一个性能问题。相反,我会努力开发其余的功能。

      【讨论】:

      • 哈哈哈我只是想把它作为评论发布,但显然只是“缓存”不符合合法评论的条件
      • @engineersmnky:一样!不得不添加一些绒毛:)
      • 要扩展@SergioTulentsev 的答案,如果此计数成为性能问题,您可以设置缓存并确保每次不再计算配方记录时使缓存无效(例如:published 已更改( false => true 或相反),配方已删除)。这可以通过 ActiveRecord 回调(after_saveafter_destroy)来完成。
      【解决方案3】:

      您陷入过早优化的陷阱。在进行任何优化(大多数情况下会增加代码复杂性)之前,您必须对代码进行分析以找到瓶颈。改进只占总响应时间一小部分的 SQL 请求是没有用的。相反,如果 SQL 需要大量时间,那是一个很大的改进。

      为此,我可以推荐这 2 颗宝石:

      要回答您的问题,更好的方法是:

      # app/models/recipe.rb
      class Recipe < AR::base
        # A nice scope that you can reuse anywhere
        scope :unpublished, -> { where(published: false) }
      end
      

      然后在你的 navbar.html.erb 中:

      <li>
        <%= link_to recipes_path do %>
           Recipes <span class="badge" style="background-color:#ff7373"><%= Recipe.unpublished.count %></span>
        <% end %>
      </li>
      

      控制器中不再有这些丑陋的回调和实例变量。

      除非您有很多食谱(例如 100K 或更多),否则性能不会成为问题。在这种情况下,您可以添加索引:

      CREATE INDEX index_recipes_unpblished ON recipes(published) WHERE published='f' 
      

      请注意,索引仅在发布为 false 时适用。否则会适得其反。

      我认为在您的情况下缓存并不好,因为失效非常复杂,并且会导致糟糕且容易破解的代码。不用担心撞到数据库,我们永远不会写出比 PostgreSQL/MySQL 等更快的代码。

      【讨论】: