Redis正在进行大规模DEL + SET时如何处理路由超时?

时间:2017-08-10 14:17:13

标签: ruby-on-rails ruby caching redis

我的一个路由(overview_route)正在调用大量的模型操作,这些操作通常由我在Redis中的方法缓存处理 。但是,当新的/更新的记录保存到PG中时,我有一个SideKiq作业,它将通过删除需要更新的Redis键来处理数据的更改,然后调用我们的方法将其缓存回Redis。在重建Redis期间,如果有人试图到达overview_route,它将在30秒内终止(可能需要1-3分钟才能运行)。

典型的例子:

User A will go to route `overview_route` when everything in redis is 
cached -- which will allow the page to be served quickly.

User A will add 2 new records that will change a lot of the 
computations on `overview_route`. User B submit those records and a 
background process fires to go and delete the records that need to be 
changed and and then gets rebuilt.

User A will go check `overview_route` to see the updated date, 
however can not even load the page and gets an application error.

User A can check back in 3-5 minutes (hopefully) and the page can be 
served again.

正在使用的重建逻辑示例:

def update(id)
  delete_keys("example:#{id}:current")
  delete_keys("example:#{id}:last_week")
  delete_keys("example:#{id}:start_of_week")
  ...
  rebuild(id)
end

def delete_keys(regex)
  $redis.scan_each(match: regex) do |key|
    $redis.del(key)
  end
end

#Basically loop through all records on a given model. If models 
#methods come across anything that isn't currently chached -- it will 
#set it back in redis
def rebuild(id)
  records = ModelExample.find(id)
  records.all.each do |a|
    a.rebuild
  end
  ...
end

人们如何通过对通常缓存的未处理方法进行多次调用来处理此问题(通常是为性能缓存)?

我已经尝试过/想过:

  • 修复了我的模型方法中最大的N + 1个查询。这有很多帮助,但仍无法解决超时问题。
  • 考虑到现有在Redis键上添加某种形式的指示,即一个是旧数据并添加新的(向键中添加is_stale)。在删除旧数据后完全添加新内容。模型方法可以引用两个键并检查两个Redis键吗?
  • 我可以在Redis上使用某种重复数据删除然后进行故障转移吗?如果我一次有大量的后台工作,不知道如何处理。
  • 我知道Redis上有一个EXPIRE。我能用这样的东西吗?我如何故障转移到新密钥?
  • 不知何故不删除任何密钥并只更新它们?

1 个答案:

答案 0 :(得分:0)

可能有很多方法可以做到这一点,但是当我的代码需要一段时间才能完成时,我会在后台运行这些任务。我会推荐Sidekiq http://sidekiq.org/。免费版本就足够了。

看起来你已经有了逻辑,所以我只是传递sidekiq的id,然后将逻辑移动到一个worker。像

这样的东西
# app/worker/redis_worker.rb
class RedisWorker
  include Sidekiq::Worker
  def perform(id)
    update(id)
  end
 ... Your logic
 def update(id)
   delete_keys("example:#{id}:current")
   delete_keys("example:#{id}:last_week")
   delete_keys("example:#{id}:start_of_week")
   ...
   rebuild(id)
 end

 def delete_keys(regex)
   $redis.scan_each(match: regex) do |key|
      $redis.del(key)
    end
  end

 def rebuild(id)
   records = ModelExample.find(id)
   records.all.each do |a|
     a.rebuild
   end
   ...
 end
end

要拨打此电话,您只需拨打RedisWorker.perform_async(id)

即可