0

如果我必须更新 50,000 个用户,我将如何以最适合后台处理库而不是 N+1 问题的方式进行?

我有用户、会员和积分。

会员资格与总分值有关。如果使用点值修改成员资格,我必须遍历所有用户以更新其正确的成员资格。这是我需要排队的,这样服务器就不会挂起 30 多分钟。

现在我有一个控制器动作

def update_memberberships
  User.find_each do |user|
    user.update_membership_level! # looks for a Membership defined by x points and assigns it to user. Then Saves the user.
  end
end

这是一个非常昂贵的操作。我将如何优化处理和后台,以使帖子在表单中几乎是瞬时的?

4

2 回答 2

2

你正在寻找update_all. 从文档:

如果它们与提供的一组条件匹配,则使用给定的详细信息更新所有记录,也可以提供限制和顺序。

在 SQL 方面可能还需要一段时间,但您至少可以使用一条语句来完成。查看文档以查看使用示例。

于 2013-01-31T00:07:10.950 回答
2

您似乎在寻找如何使用 Resque 或延迟工作来完成这项工作。我将举一个delayed_job的例子。

要创建作业,请向 app/models/user.rb 添加一个方法:

def self.process_x_update
  User.where("z = 1").find_each(:batch_size => 5000) do |user|
    user.x = user.y + 3
    user.save
  end
end
handle_asynchronously :process_x_update

这将更新Userz = 1 的所有记录,设置 user.x = user.y + 3。这将以 5,000 个批次完成,因此性能更加线性。

这将导致 User.process_x_update 很快完成。要实际处理作业,您应该rake jobs:work在后台运行或启动一个守护进程集群./script/delayed_job start

另一件事:你能把这个逻辑转移到一个 SQL 语句中吗?这样,您就可以拥有一个快速且原子的语句。您仍然希望在后台执行此操作,因为它可能需要一些时间来处理。您可以执行以下操作:

def process_x_update
  User.where("z = 1").update_all("x = y + 3")
end
handle_asynchronously :process_x_update
于 2013-01-31T00:10:08.007 回答