【问题标题】:Rails counter_cache for Model.count without any association, in order to make SELECT COUNT (*) fasterRails counter_cache for Model.count 没有任何关联,以使 SELECT COUNT (*) 更快
【发布时间】:2011-05-12 01:04:47
【问题描述】:

我正在使用 Model.count 对我的一个模型中的行进行总计,并且有点担心性能,因为最终,这个模型会变得非常大,因此 SELECT COUNT (*) 非常慢。

有没有办法在没有:belongs_to 关系的情况下使用counter_cache?或者另一种计算行数的性能友好的方式?我考虑过制作另一个模型,只是我存储这样的计算但不确定这是最好的方法。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-5


    【解决方案1】:

    比制作Cache 模型更简单的是只使用Rails.cache

    Rails.cache.read("elephant_count") #=> nil
    Rails.cache.write("elephant_count", 1) #=> true
    Rails.cache.read("elephant_count") #=> 1
    

    Rails 默认使用文件存储 (tmp/cache)。

    然后您可以将 Rails.cache.write 增量和减量放入模型的 after_createafter_destroy 挂钩中,并通过调用 Rails.cache.read 覆盖 Model.size

    您可以在 Rails 首次初始化时初始化缓存,方法是在 config/initializers 中放置一个名为 initialize_cache.rb 的文件,其中包含:

    Rails.cache.write('elephant_count', 0) if Rails.cache.read('elephant_count').nil?
    

    【讨论】:

      【解决方案2】:

      如果您想要维护一个计数器,无论是使用counter_cache 还是手动执行,Rails 都会使用回调来维护您的计数器,这将在创建/销毁新后代时增加/减少计数器。

      我不知道在不使用belongs_to 关系的情况下存储counter_cache 的方法,因为只有父级可以存储子级的计数。

      称重性能

      如果您的表将变得“大”,请使用大量行填充您的测试数据库,然后使用EXPLAIN 开始运行一些 SQL 查询以获得数据库查询的性能。看看使用counter_cache 进行记录创建/销毁时的性能影响是否会被您首先需要访问这些计数器的频率所抵消。

      如果计数器不需要始终 100% 准确,您可以改为使用 cron 作业或后台工作程序定期更新缓存。

      总结:

      1. 仅当您需要这些计数器足以抵消创建/销毁记录所花费的稍长时间时,才应使用 counter_cache。
      2. 据我所知,使用 counter_cache 与使用回调的手动替代方法相比,不太可能对性能造成很大损害。
      3. 如果缓存不需要准确,请利用这一点并减少执行计算的频率。

      【讨论】:

        【解决方案3】:

        看看http://guides.rubyonrails.org/caching_with_rails.html 具体来说,您需要查看有关缓存存储的部分。使用缓存存储,您可以将任意事物的值存储到缓存中。

        例如,您可以在模型上调用一个名为 get_count 的方法,该方法最初由计数填充,但通过 after_create 回调递增 1。如果没有必要保持最新,您可以每 x 分钟更新一次,以便您最准确。

        我个人使用 memcache 作为这样的存储。只要确保根据您的需要保持缓存是最新的。

        【讨论】:

          【解决方案4】:

          像这样定义CachedCount 关注点怎么样?

          module CachedCount
            extend ActiveSupport::Concern
          
            included do
              after_create :increment_cached_count
              after_destroy :decrement_cached_count
            end
          
            class_methods do
              def count
                return cached_count if cached_count
                Rails.cache.write(cached_count_key, super)
                Rails.cache.read(cached_count_key) || super # fallback because in some Rails env. Rails.cache may not be available
              end
          
              def cached_count_key
                "#{model_name.collection}_count"
              end
          
              def cached_count
                Rails.cache.read(cached_count_key)
              end
            end
          
            def increment_cached_count
              return self.class.count unless self.class.cached_count
              Rails.cache.write(self.class.cached_count_key, self.class.cached_count + 1)
            end
          
            def decrement_cached_count
              return self.class.count unless self.class.cached_count
              Rails.cache.write(self.class.cached_count_key, self.class.cached_count - 1)
            end
          end
          

          然后你将它包含在你的众多模型中:

          class MyNumerousModel
          include CachedCount
          
          # [...]
          
          end
          

          现在,每次调用 MyNumerousModel.count 时,实际上都是在调用关注点中的类方法。当您创建或销毁 MyNumerousModel 的一个实例时,after_createafter_destroy 回调会负责更新 MyNumerousModel.cached_count

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-08
            • 2011-09-25
            • 1970-01-01
            • 2010-10-07
            相关资源
            最近更新 更多