如何在rails中缓存计算列?

我有一个活动记录对象树,如:

class Part  "Part" def complicated_calculation if sub_parts.size > 0 return self.sub_parts.inject(0){ |sum, current| sum + current.complicated_calculation } else sleep(1) return rand(10000) end end end 

每次重新计算复杂计算成本太高。 所以,我需要一种缓存值的方法。 但是,如果更改了任何部分,则需要使其缓存及其父级和祖父级等的缓存无效。

作为草稿,我创建了一个列来保存“部分”表中的缓存计算,但这有点臭。 看起来应该有一种更简洁的方法来缓存计算值,而不会将它们放在“真实”列旁边。

  1. 您可以在Rails缓存中填充实际缓存的值(如果需要将其分发,请使用memcached)。

  2. 困难的一点是缓存到期,但缓存到期是不常见的,对吧? 在这种情况下,我们可以依次遍历每个父对象并删除它的缓存。 我在你的类中添加了一些ActiveRecord魔法,以使父对象本身变得简单 – 你甚至不需要触摸你的数据库。 记得在你的代码中适当地调用Part.sweep_complicated_cache(some_part) – 你可以将它放在回调等中,但是我不能为你添加它,因为我不明白complicated_calculation正在改变。

     class Part < ActiveRecord::Base has_many :sub_parts, :class_name => "Part" belongs_to :parent_part, :class_name => "Part", :foreign_key => :part_id @@MAX_PART_NESTING = 25 #pick any sanity-saving value def complicated_calculation (...) if cache.contains? [id, :complicated_calculation] cache[ [id, :complicated_calculation] ] else cache[ [id, :complicated_calculation] ] = complicated_calculation_helper (...) end end def complicated_calculation_helper #your implementation goes here end def Part.sweep_complicated_cache(start_part) level = 1 # keep track to prevent infinite loop in event there is a cycle in parts current_part = self cache[ [current_part.id, :complicated_calculation] ].delete while ( (level <= 1 < @@MAX_PART_NESTING) && (current_part.parent_part)) { current_part = current_part.parent_part) cache[ [current_part.id, :complicated_calculation] ].delete end end end 

我建议使用关联回调。

 class Part < ActiveRecord::Base has_many :sub_parts, :class_name => "Part", :after_add => :count_sub_parts, :after_remove => :count_sub_parts private def count_sub_parts update_attribute(:sub_part_count, calculate_sub_part_count) end def calculate_sub_part_count # perform the actual calculation here end end 

好又容易=)

有一个类似于计数器缓存的字段。 例如:order_items_amount并且具有缓存的计算字段。

使用after_savefilter重新计算可以修改该值的任何字段。 (包括记录本身)

编辑:这基本上就是你现在所拥有的。 除非您想将缓存的计算字段存储在另一个表中,否则我不知道任何更干净的解决方案。

使用before_save或ActiveRecord Observer是确保缓存值是最新的方法。 我会使用before_save,然后检查您在计算中使用的值是否实际更改。 这样,如果您不需要,则无需更新缓存。
将值存储在db中将允许您在多个请求上缓存计算。 另一个选择是将值存储在memcache中。 您可以为该值创建一个特殊的访问器和设置器,以便检查内存缓存并在需要时进行更新。
另一个想法是:是否会出现更改某个模型中的值并需要在进行保存之前更新计算的情况? 在这种情况下,每当更新模型中的任何计算值时都需要弄脏缓存值,而不是使用before_save。

我发现有时候有充分的理由去除数据库中的信息规范化。 我在我正在处理的应用程序中有类似的东西,我只是在集合发生变化时重新计算该字段。

它不使用缓存,它在数据库中存储最新的数字。