Rails: ActiveRecord 对同一模型多次执行无意义的查询
Rails: ActiveRecord perform pointless query multiple times over the same model
在我的应用程序中,我稍微覆盖了 current_user 设计方法。这个想法是,如果存在某些 cookie,则方法通过该 cookie 中的 id 和该组织的 returns 所有者而不是普通用户来检查组织:
def current_user
user = warden.authenticate(scope: :user)
return nil if user.nil?
if user.admin? && cookies.key?('mock_admin_login')
organization = Organization.includes(:creator).find(cookies.encrypted[:mock_admin_login])
return organization.creator
end
user
end
一切正常,但当我查看我的控制台时,我注意到组织查询被执行了多次:
CACHE Organization Load (0.5ms) SELECT "organizations".* FROM
"organizations" WHERE "organizations"."id" = LIMIT [["id", 9],
["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.9ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.7ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.3ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.4ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (2.0ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (4.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.4ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (42.8ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (4.5ms) SELECT
"organizations".* FROM "organizations" WHERE "organizations"."id" =
LIMIT [["id", 9], ["LIMIT", 1]] ↳
app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user'
虽然看起来没什么大不了的,但每次调用 current_user 方法时,服务器都会额外花费 30-40 毫秒来执行此操作。为什么这个查询被调用了这么多次而不是一次,我该如何解决?
您需要记住结果,这样它就不会在您每次调用时都重新计算 current_user
。
如果您查看 devise 生成的帮助程序,您会发现它就是这样做的:
def current_#{mapping}
@current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
如果您想修复现有方法,您需要确保记住数据库调用:
def current_user
@current_user ||= warden.authenticate(scope: :#{mapping})
if @current_user&.admin? && cookies.key?('mock_admin_login')
@current_org || = Organization.includes(:creator)
.find(cookies.encrypted[:mock_admin_login])
@current_user = @current_org.creator
end
@current_user
end
但您确实应该将此实现为 a custom Warden strategy instead。
在我的应用程序中,我稍微覆盖了 current_user 设计方法。这个想法是,如果存在某些 cookie,则方法通过该 cookie 中的 id 和该组织的 returns 所有者而不是普通用户来检查组织:
def current_user
user = warden.authenticate(scope: :user)
return nil if user.nil?
if user.admin? && cookies.key?('mock_admin_login')
organization = Organization.includes(:creator).find(cookies.encrypted[:mock_admin_login])
return organization.creator
end
user
end
一切正常,但当我查看我的控制台时,我注意到组织查询被执行了多次:
CACHE Organization Load (0.5ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.9ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.7ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.3ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (2.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (4.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (42.8ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user' CACHE Organization Load (4.5ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = LIMIT [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:incurrent_user' CACHE User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in
current_user'
虽然看起来没什么大不了的,但每次调用 current_user 方法时,服务器都会额外花费 30-40 毫秒来执行此操作。为什么这个查询被调用了这么多次而不是一次,我该如何解决?
您需要记住结果,这样它就不会在您每次调用时都重新计算 current_user
。
如果您查看 devise 生成的帮助程序,您会发现它就是这样做的:
def current_#{mapping}
@current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
如果您想修复现有方法,您需要确保记住数据库调用:
def current_user
@current_user ||= warden.authenticate(scope: :#{mapping})
if @current_user&.admin? && cookies.key?('mock_admin_login')
@current_org || = Organization.includes(:creator)
.find(cookies.encrypted[:mock_admin_login])
@current_user = @current_org.creator
end
@current_user
end
但您确实应该将此实现为 a custom Warden strategy instead。