ruby-on-rails - 进一步改进 Active Record/Postgresql 查询

标签 ruby-on-rails ruby postgresql activerecord

跟进我的问题 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/

相关文章:

ruby-on-rails - Rails 3 取消转义辅助输出

mysql - Web开发从哪里开始?

ruby-on-rails - Rails 未定义方法 `id'

mysql - PG 尝试将 rails db 推送到 heroku 时出错

ruby-on-rails - 在 Mac 上安装 do_postgres

postgresql - 在 SQLAlchemy 中更新 PostgreSQL 数组字段

ruby-on-rails - 在activeadmin中过滤值或IS NULL

ruby-on-rails - 在自定义 View 中使用 rails_admin 表单?

ruby-on-rails - 在助手中调用yield

javascript - Ruby - 你能像在 javascript 中那样创建一个独立的对象吗?