2

我正在使用排队系统 (Sidekiq) 并希望迁移到 ActiveJob 以获得性能优势,即每次将 ActiveRecord 对象传递给工作人员时都不必查询数据库。我想询问并确认,因为我不是 100% 确定,但我的理解是,当 ActiveJob 使用 GlobalID 传递所有在内存中完成的 ActiveRecord 对象并且没有完成对数据库的单独查询时,对吗?

4

2 回答 2

5

这是不正确的。

如果您使用 ActiveJob,它会将任何 ActiveRecord 对象序列化为 global_id 字符串以保存到您的队列中。然后在作业开始时从该字符串中再次查找它。默认情况下,该字符串仅包含应用程序名称、类名和 id,它将使用您的数据库来加载模型。

"gid://app/User/1"

DelayedJob 会将您提供的任何对象序列化为 yaml 字符串并对其进行反序列化,而不会在加载作业之外访问数据库。您也可以使用 Sidekiq 来执行此操作,而不是点击 Redis 来加载作业而不接触主数据库。

user = User.find(1)
MyJob.perform_later(user.to_yaml)

# Load the user object from the yaml
YAML::load(user.to_yaml) == user # true

您无需前往数据库即可获得您的对象。但是,YAML 会很大,并且使用 Redis 获得的性能损失可能不值得。

还有一些你应该注意的问题。该对象可能在数据和结构方面都已过时。如果您更改代码,序列化对象可能由于结构更改而无法再次加载。如果您在序列化对象后更新数据库,当您加载它时,您将在不知不觉中使用旧数据。

希望能帮助您了解 ActiveJob 和 GlobalId 提供的内容。

于 2015-09-04T21:19:14.460 回答
1

无论如何都会执行数据库查询,但要透明。以下面的代码为例,说明 ActiveJob 内部的作用:

gid = User.find(1).to_global_id
  User Load (0.8ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<GlobalID:0x00007f86f76f46d8 @uri=#<URI::GID gid://app/User/1>>

然后,当作业执行时,ActiveJob 在内部运行以下代码,无论如何都会查询数据库:

GlobalID::Locator.locate(gid)
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, ... >

使用 GlobalIDs 的一个问题是,如果在作业入队之后但在#perform调用方法之前删除了传递的记录,则 Active Job 将引发ActiveJob::DeserializationError异常。

表现

根据 Sidekiq 的作者 Mike Perham 的说法,基准测试表明 ActiveJob 将作业推送到 Redis 的速度要慢 2-20 倍,并且处理开销大约是 3 倍(https://github.com/mperham/sidekiq/wiki/Active-Job#表现)。

附加信息

有关 Sidekiq、ActiveJob 和 GlobalID 的所有信息都可以在这里找到:https ://github.com/mperham/sidekiq/wiki/Active-Job#using-global-id

于 2019-07-24T16:31:20.547 回答