ActiveJob GlobalID和内存中的ActiveRecord对象

时间:2015-08-14 17:25:24

标签: ruby-on-rails ruby activerecord rails-activejob

我正在使用排队系统(Sidekiq)并希望转移到ActiveJob以获得性能优势,即每次将ActiveRecord对象传递给worker时都不必查询数据库。我想询问并确认,因为我不是100%肯定,但我的理解是,当ActiveJob使用GlobalID传递ActiveRecord对象时,这些对象全部在内存中完成并且未对数据库进行单独查询,是否正确?

2 个答案:

答案 0 :(得分:1)

这是不正确的。

如果使用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

你可以在没有去DB的情况下得到你的对象。但是,YAML会很大,而Redis的性能损失可能不值得。

还有一些你应该注意的陷阱。在数据和结构方面,对象可能已过时。如果更改代码,由于结构更改,序列化对象可能无法再次加载。如果在序列化对象后更新数据库,则在加载对象时,您将在不知情的情况下使用旧数据。

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

答案 1 :(得分:0)

数据库查询将同样但透明地执行。以下面的代码作为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, ... >

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

性能

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

其他信息

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