跟进我的问题 here ,我正在尝试进一步改进搜索。我们首先搜索一个回放表(搜索 2k 条记录),然后获取与该表关联的唯一播放器(每个 10 个,所以 20k 条记录)并呈现一个 JSON。这是通过 Controller 完成的,搜索内容如下:
def index
@replays = Replay.includes(:players).where(map_id: params['map_id'].to_i).order(id: :desc).limit(2000)
render json: @replays[0..2000].to_json(include: [:players])
end
性能:
Completed 200 OK in 254032ms (Views: 34.1ms | ActiveRecord: 20682.4ms)
实际的 Active Record 搜索内容如下:
Replay Load (80.4ms) SELECT "replays".* FROM "replays" WHERE "replays"."map_id" = $1 ORDER BY "replays"."id" DESC LIMIT $2 [["map_id", 1], ["LIMIT", 2000]]
Player Load (20602.0ms) SELECT "players".* FROM "players" WHERE "players"."replay_id" IN (117217...
这大部分都有效,但仍然需要大量时间。有没有办法提高性能?
最佳答案
你被这个问题困扰了 https://postgres.cz/wiki/PostgreSQL_SQL_Tricks_I#Predicate_IN_optimalization
I found note pg_performance about optimalization possibility of IN predicate when list of values is longer than eighty numbers. For longer list is better create constant subqueries with using multi values:
SELECT * FROM tab WHERE x IN (1,2,3,..n); -- n > 70
-- faster case SELECT * FROM tab WHERE x IN (VALUES(10),(20));
Using VALUES is faster for bigger number of items, so don't use it for small set of values.
基本上,SELECT * FROM WHERE IN ((1),(2)...)
包含一长串值非常慢。如果您可以将其转换为值列表,速度会快得离谱,例如 SELECT * FROM WHERE IN (VALUES(1),(2) ...)
不幸的是,由于这是在事件记录中发生的,因此对查询进行控制有点棘手。您可以避免使用 includes
调用,而只是手动构造 SQL 来加载所有子记录,然后手动建立关联。
或者,您可以猴子修补事件记录。这是我在 Rails 4.2 上的初始化程序中所做的。
module PreloaderPerformance
private
def query_scope(ids)
if ids.count > 100
type = klass.columns_hash[association_key_name.to_s].sql_type
values_list = ids.map do |id|
if id.kind_of?(Integer)
" (#{id})"
elsif type == "uuid"
" ('#{id.to_s}'::uuid)"
else
" ('#{id.to_s}')"
end
end.join(",")
scope.where("#{association_key_name} in (VALUES #{values_list})")
else
super
end
end
end
module ActiveRecord
module Associations
class Preloader
class Association #:nodoc:
prepend PreloaderPerformance
end
end
end
end
这样做后,我发现我的一些查询速度提高了 50 倍,目前还没有任何问题。请注意,它还没有经过全面的测试,我敢打赌,如果您的关联正在为 foreign_key 关系使用唯一的数据类型,它会出现一些问题。在我的数据库中,我只使用 uuid 或整数来表示我们的关联。有关猴子修补核心 rails 行为的常见警告适用。
关于ruby-on-rails - 进一步改进 Active Record/Postgresql 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47477103/