Активный сериализатор моделей - повышение производительности рендеринга

Я сталкиваюсь с конкретной ситуацией, когда визуализированный json, сгенерированный ActiveModel :: Serializer, чрезвычайно медленный (около 6-8 секунд). Как я могу улучшить скорость этого рендеринга? Вот код.

Модели:

class Comment < ActiveRecord::Base
  has_many :children_comments,
           class_name: 'Comment',
           foreign_key: 'parent_comment_id'

  belongs_to :user
  belongs_to :parent_comment,
             class_name: 'Comment',
             foreign_key: 'parent_comment_id'
end

Сериализаторы:

class CommentSerializer < ActiveModel::Serializer
  include ActionView::Helpers::DateHelper

  attributes :id, :message, :created_at_in_words,
             :created_at, :parent_comment_id
  belongs_to :user
  has_many :children_comments

  def created_at_in_words
    time_ago_in_words(object.created_at) + ' ago'
  end

  def children_comments
    object.children_comments.map do |comment|
      CommentSerializer.new(comment).as_json
    end
  end
end

class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :avatar_url

  def avatar_url
    object.avatar.url
  end
end

В моем контроллере у меня есть

  parent_comments = Comment.where(parent_comment_id: nil)

  render status: :ok,
         json: parent_comments,
         each_serializer: CommentSerializer,
         key_transform: :camel_lower

Вот мой частичный вывод журнала, когда я звоню на сервер. Как вы видите, Active Model Serializer занимает около 20 мсек на каждый вызов запроса.

Started GET "/comments?lesson_id=420" for ::1 at 2016-09-01 11:09:14 -0400
Processing by Api::CommentsController#index as HTML
  Parameters: {"lesson_id"=>"420"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 102]]
  Lesson Load (0.5ms)  SELECT  "lessons".* FROM "lessons" WHERE "lessons"."id" = $1  ORDER BY position ASC LIMIT 1  [["id", 420]]
  Comment Load (53.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2 AND "comments"."parent_comment_id" IS NULL  [["commentable_id", 420], ["commentable_type", "Lesson"]]
[active_model_serializers]   User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (24.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41401]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41402]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41403]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (21.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41404]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41405]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41406]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41407]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41408]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41409]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41410]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41411]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41412]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41413]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (23.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41414]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41415]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41416]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (23.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41417]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41418]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41419]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41420]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41421]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41422]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41423]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41424]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41425]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41426]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41427]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41428]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41429]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41430]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41431]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41432]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41433]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (21.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41434]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41435]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (21.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41436]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41437]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41438]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41439]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41440]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41441]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.9ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41442]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41443]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41444]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41445]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41446]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41447]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41448]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41449]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41450]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41451]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41452]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41453]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41454]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41455]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (22.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41456]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41457]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41458]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41459]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41460]]
[active_model_serializers]   CACHE (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41461]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41462]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41463]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41464]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41465]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41466]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41467]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41468]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41469]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41470]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41471]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41472]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41473]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41474]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41475]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41476]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (19.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41477]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41478]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41479]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41480]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.2ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41534]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41535]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41536]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.8ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41537]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (18.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 41538]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Json (3895.33ms)
Completed 200 OK in 4007ms (Views: 1222.1ms | ActiveRecord: 2743.8ms)

С ответом Михала вот небольшой образец из журнала.

Started GET "/comments?lesson_id=370" for ::1 at 2016-09-02 17:13:06 -0400
Processing by Api::CommentsController#index as HTML
  Parameters: {"lesson_id"=>"370"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 102]]
  Lesson Load (0.4ms)  SELECT  "lessons".* FROM "lessons" WHERE "lessons"."id" = $1  ORDER BY position ASC LIMIT 1  [["id", 370]]
  Comment Load (23.0ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" IS NULL AND "comments"."commentable_type" = 'Lesson' AND "comments"."commentable_id" = 370
  User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (102)
  Comment Load (25.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" IN (38641, 38687, 38733)
  CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (102)
[active_model_serializers]   Comment Load (20.3ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 38642]]
[active_model_serializers]   User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (20.7ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 38643]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]
[active_model_serializers]   Comment Load (30.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."parent_comment_id" = $1  [["parent_comment_id", 38644]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 102]]

Моя теория такова. Я убежден, что сериализация children_comments вызывает большинство проблем с производительностью. Поскольку мне приходится вызывать children_comments для каждого комментария, это приводит к каскадному эффекту. Интересно, могу ли я переписать код таким образом, чтобы повысить производительность.


person thank_you    schedule 29.08.2016    source источник
comment
Вы используете драгоценный камень oj?   -  person DVG    schedule 31.08.2016
comment
Да, у oj gem практически нет разницы в производительности.   -  person thank_you    schedule 31.08.2016
comment
Вы пробовали это профилировать? С ruby-prof или stackprof? Он покажет, какая часть на самом деле медленная.   -  person Michał Młoźniak    schedule 01.09.2016
comment
@Michal Нет, не видел. Но глядя на вывод журнала, я вижу, что сериализатор занимает больше всего времени, когда делает запросы комментариев. Я убежден, что это и является причиной низкой производительности.   -  person thank_you    schedule 01.09.2016
comment
Можете ли вы публиковать вывод из своего журнала с помощью sql-запросов?   -  person Michał Młoźniak    schedule 01.09.2016
comment
@ MichałMłoźniak Я добавил вывод журнала к своему вопросу.   -  person thank_you    schedule 01.09.2016
comment
Делает ли он такое же количество запросов? Можете ли вы опубликовать весь результат (удаление повторяющихся запросов)?   -  person B Seven    schedule 15.09.2016
comment
@BSeven Я не могу опубликовать весь вывод (SO не позволит мне, поскольку он превышает допустимое количество символов). Я могу попробовать завтра посмотреть, существуют ли дубликаты. Хотя я склонен сказать «нет».   -  person thank_you    schedule 16.09.2016
comment
Не могли бы вы уменьшить количество запросов, вызываемых комментариями SELECT. * FROM comments WHERE comments.parent_comment_id in (38642, 38643, 38644) Это сэкономит накладные расходы на установление соединения с БД и отправку нескольких запросов с получением нескольких ответов. По моему опыту, звонки в базу данных чаще, чем необходимо, требуют много времени.   -  person Bodo    schedule 16.09.2016
comment
@Bodo Было бы неплохо. Я поиграю с этим.   -  person thank_you    schedule 16.09.2016


Ответы (2)


Вы столкнулись с проблемой типа запросов n + 1. К сожалению, includes на самом деле не помогает вам здесь, потому что он помогает вам только на одном уровне ассоциации - он позволяет избежать отдельных выборок дочерних элементов комментария верхнего уровня, но не внуков или правнуков.

Вероятно, вы могли бы оптимизировать поиск пользователя, поддерживая свой собственный кеш идентификатора пользователя для пользовательских объектов (кеш рельсов кэширует необработанные данные, но будет повторно создавать объекты снова и снова), но чтобы сделать это значительно быстрее, вам нужно изменить то, как вы загрузить комментарии.

Если вы используете базу данных, которая его поддерживает (например, postgresql), то рекурсивные запросы - это вариант (см. https://hashrocket.com/blog/posts/recursive-sql-in-activerecord для рабочего примера). Я не знаю этой чешуи, так как дерево становится все глубже и глубже.

Если рекурсивные запросы не подходят, существует несколько подходов, которые включают изменение того, что вы храните.

Один из них - это материализованный образец пути. Например, предположим, что корневой комментарий имеет идентификатор 1, дочерний элемент имеет идентификатор 101, а один из его дочерних элементов имеет идентификатор 426. Путь к последнему комментарию - 1/101/426. Все его братья и сестры имеют пути, начинающиеся с 1/101/. Это означает, что вы можете использовать похожие запросы (с подстановочными знаками в конце) для быстрого поиска поддеревьев. Это реализует гем родословная. Если комментарии перемещаются, вам необходимо переписать пути всех дочерних комментариев (и внуков и т. Д.), Но это может не иметь отношения к вашему варианту использования. Я считаю, что очень глубокие деревья проблематичны.

Другой - шаблон вложенного набора. Основная идея заключается в том, что родительский узел хранит минимальный и максимальный идентификатор всех своих дочерних узлов и их дочерних элементов и т. Д. Это позволяет получить всех этих дочерних узлов за один раз. Обратной стороной является то, что вставки и обновления требуют перезаписи большого количества этих данных (больше, чем с материализованным путем). На протяжении многих лет это реализовывалось в различных драгоценных камнях (текущий, похоже, awesome_nested_set).

Также стоит убедиться, что у вас есть правильные индексы для поддержки ваших запросов - если на самом деле нет достаточно много комментариев с данным родителем, 20–30 мс кажутся довольно долгим временем для одного запроса.

person Frederick Cheung    schedule 16.09.2016
comment
Вы дали наиболее исчерпывающий ответ с несколькими возможными решениями. За это я хочу отдать вам должное. Спасибо за развернутый ответ. - person thank_you; 16.09.2016

Измените свой запрос ActiveRecord на этот

parent_comments = Comment.where(parent_comment_id: nil).includes(:user, children_comments: :user)

Это избавит от N + 1 запросов.

person Michał Młoźniak    schedule 01.09.2016
comment
К сожалению, это не улучшило время. Время загрузки комментариев было примерно одинаковым. - person thank_you; 02.09.2016
comment
@ jason328 А как теперь выглядит вывод лога? - person Michał Młoźniak; 02.09.2016
comment
размещено в вопросе. - person thank_you; 03.09.2016
comment
Хм @ jason328, запрос ActiveRecord выше мне нравится, вам обязательно нужно проверить N + 1 запросов. Поскольку вы переименовываете отношение ассоциации, вам, возможно, придется немного попробовать присвоить имя children_comments (вы можете начать с comments). - person Sebastian; 16.09.2016