跟进我的问题here,我试图进一步改进搜索。我们首先搜索重播表(搜索2k记录),然后获得与该表相关联的唯一玩家(每个10个,所以20k记录)并渲染JSON。这是通过控制器完成的,搜索内容如下:
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...
这大部分都有效,但仍需要很长时间。有没有办法提高绩效?
答案 0 :(得分:0)
我知道find_each
可以用于批处理查询,这可能会减轻这里的内存负担。你可以尝试以下内容,看看它对时间的影响吗?
Replay.where(map_id: params['map_id'].to_i).includes(:players).find_each(batch_size: 100).map do |replay|
replay.to_json(includes: :players)
end
我不确定这会有效。可能是映射否定了批处理的好处 - 肯定有更多的查询,但它会使用更少的内存,因为它不需要存储>一次20k记录。
玩游戏并看看它的样子 - 也可以调整批量大小,看看它是如何影响事物的。
有一点需要注意,你不能申请限制,所以请记住这一点。
我相信其他人会想出一个更加流畅的解决方案,但希望在此期间这可能会有所帮助。如果检查速度很糟糕,请告诉我,我会删除这个答案:)
答案 1 :(得分:0)
你被这个问题https://postgres.cz/wiki/PostgreSQL_SQL_Tricks_I#Predicate_IN_optimalization
所困扰当值列表长于八十个数字时,我发现了关于IN谓词的最优化可能性的注释pg_performance。对于更长的列表,最好使用多值创建常量子查询:
SELECT * FROM标签 WHERE x IN(1,2,3,... n); - n> 70
- 更快的情况 选择 * FROM标签 在哪里x IN(VALUES(10),(20));
对于较大数量的项目,使用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关系的唯一数据类型会有一些问题。在我的数据库中,我只使用uuids或整数来表示我们的关联。有关猴子修补核心轨道行为的常见警告适用。