处理任务计数,同时保持性能

时间:2011-10-05 00:27:41

标签: ruby-on-rails ruby performance task

我知道这是一个特定于应用程序的问题,但我想知道某人是否有一个我没有想过的聪明的解决方案。这是一个艰难的,所以任何人都可以优雅地解决这个问题的主要观点(和道具)。

我在rails应用程序上有一个ruby,其全局导航包含任务计数(见下图)

Global navitation with task counts

问题是生成这些计数需要一些强烈的查询。而且因为它的全球性,它在每一页上。他们还需要在人们处理任务时进行更新。这使得它特别具有挑战性。

那就是说,现在我通过memcached缓存导航HTML 10分钟。如果有人负责任务,哦。导航每10分钟更新一次。我知道,这是一个可怕的解决方案,但在我找到合适的解决方案之前,这是我的停止间隙测量。

我想避免在应用程序中添加一堆钩子(例如:处理交付,触发钩子以减少每个相关用户的计数)。请记住,这些计数涉及系统中的许多实体,逻辑有些复杂。它处理访问(每个用户根据权限系统具有不同的计数),并且在某些情况下确定这些计数可能很复杂。 Hooks会很快变得凌乱,产生这些计数背后的逻辑将会重复。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

如果添加用于自动递增/递减计数器的附加代码,则其中一个解决方案是使用缓存并有效地使用它。下面的伪代码。

class User

  def counters
    needs_update(user_id)
    memcache.read(counter_user_id)
  end

  def needs_update(user_id)
      some_job_queue_for_counters.insert(Job.new(User.update_counters(user_id)))
  end

  #executed by the job runner
  def update_counters user_id
     counter_hash = {:counter1 => complex_logic1, :counter2 => complex_logic2 ...}
     memcache.write(counter_user_id, counter_hash.to_json.gzipped)
  end

end

需要注意的几点。

  1. 从浏览器进行AJAX调用,每1分钟或更长时间一次,具体取决于执行计数器逻辑的时间。
  2. 计数器方法尽可能快,因为 memcache.read()返回一个压缩的json字符串。零处理到 生成响应。
  3. 您可以在开始时为所有用户预热memcache 申请。之后,计数器更新只发生了 调用couters方法的用户,即保留的用户 浏览器/移动应用程序打开。
  4. needs_update方法可以为user_id进行作业合并,以避免重复。

答案 1 :(得分:-1)

更新

简单的解决方案是删除所有受影响的更改用户的所有缓存导航html。您还可以通过ajax调用加载navHTML,这样您就不必等待加载页面加载(并在客户端的localStorage中保留navHTML的缓存版本,并且每当页面加载时您都可以然后在需要时触发navHTML的重置。)

更复杂的解决方案是:

将其缓存为2层(navHTML,然后是单个项目/用户/状态项)。您必须找到一种方法来更新范围,以便他们知道应该触发重置的计数。因此,如果您更改状态机中的交付状态,则应重置该交付的所有计数器,并删除受影响用户的导航缓存。

我会以某种命名空间的方式为缓存中的每个交付项目放入所有密钥,例如“delivery:234324-user:123-status”,因此每当使用id#234324的Delivery进行更改时,您将迭代并删除所有缓存属于该交付的项目(可能最好尽量保持愚蠢)。当有人请求页面时,您可以计算并存储计数,以便在实际对象更改时执行缓存删除。然后,您可以完全缓存nav html,但只要有更改了您的用户和ID(如user-123),您就会删除导航缓存并强制navHTML重新加载。再加上导航的某种缓存时间(例如最大1分钟或者其他东西)来解决错误。导航项应通过各个项目的缓存构建,而不必通过每个项目。

您可能希望将Cache Sweeper添加到关联的模型http://guides.rubyonrails.org/caching_with_rails.html


这里的计数器缓存供参考(我认为构建数据库模型以允许使用计数器缓存是处理此问题的最佳方法)。

http://railscasts.com/episodes/23-counter-cache-column

可以使用has_many关系自动使用它,也可以手动更新计数。我假设如果你的模型很复杂并且你有一个多对多的关系,你可以把计数器缓存放在一个关系对象上。