在活动模型序列化程序中调试慢查询的方法?

时间:2016-05-14 16:55:36

标签: ruby-on-rails rails-activerecord active-model-serializers

我无法优化我的Active Model Serializers以避免n + 1问题。根据他们的文档中的建议,我试图加载我认为导致我的查询瓶颈的关联,但我的序列化器仍然需要永远。

显然,我一定是做错了。我的应用程序与关联深深嵌套,所以我想我更感兴趣的是发现一个工具向我揭示确切的关联会让我付出代价。现在,我将堆栈跟踪附加到通过ActiveRecord

运行的每个查询
ActiveSupport::Notifications.subscribe("sql.active_record") do |_, _, _, _, details|
  puts caller.join("\n")
  puts "*" * 50
end

这给了我一个荒谬的输出,因为我开始运行这么多查询,但是,此外,堆栈跟踪无助于识别哪个序列化器有问题。它向我展示了哪个控制器方法正在调用渲染,但是从那里,堆栈跟踪只是打印来自gems/active_model_serializers的方法,这对我没有帮助。

我希望找到一种调试方法,能够识别哪些序列化程序有问题,这样我就不会猜测如何优化我的查询。有人发现过这样的事吗?谢谢!

=================== 更新

很明显,除了堆栈跟踪之外,我已经打印了查询日志。不幸的是,由于需要跟踪这么多关联,查询日志对于识别查询源并不是很有用。这是最好的猜测,在我正在处理的协会范围内无效。

我完全抛弃了堆栈痕迹,发现它们完全无益。现在,我所有打印的都是SQL日志,我手动筛选它们,试图发现关联的来源。

我将尝试的下一个方法(尽管我讨厌使用它)正在评论关联,直到我看到查询时间的改进。它比试图追踪问题的根源更有效,但它会让我在生产环境中没有任何安慰(评论关键关联不是一种选择),所以如果有人找到一个可以帮助的解决方案,我会仍然非常感激。

当我解决这个问题时,我会继续发布更新,因为它可能在将来帮助很多其他人。

======================更新2 事实证明,在我的序列化器中注释掉关联并一次重新引入它们,虽然在生产中无效,但却是在本地环境中调试的一种很好的方法。我能够在一分钟内深入研究问题并纠正它。不过,这不是一个理想的解决方案。理想情况下,我希望能够从日志中识别问题,以便在生产中我可以确定问题而不会影响应用程序的行为。

2 个答案:

答案 0 :(得分:0)

active_record_query_trace宝石可以做到这一点。

  1. 将以下内容添加到您的Gemfile中:
group :development do
  gem 'active_record_query_trace'
end
  1. 创建一个初始化程序,例如config/initializers/active_record_query_trace.rb以启用gem。如果要自定义gem的行为,还可以将docs中描述的选项的任意组合添加到初始化程序中。
if Rails.env.development?
 ActiveRecordQueryTrace.enabled = true
 # Optional: other gem config options go here
end
  1. 重新启动Rails开发服务器。

答案 1 :(得分:0)

任何gem内的简便方法-记录每个代码字符串。例如,如果您有 序列化器的代码:

module Flats
  class IndexSerializer < Api::V2::RealtyObjects::IndexSerializer
    attributes(
      :flat_number,
      :entrance_number,
      :floor_in_house,
      :live_area,
      :room_count,
      :total_area,
    )
  end
end

添加方法,它将在每个属性上记录到您的development.log的时间:

module Flats
  class IndexSerializer < Api::V2::RealtyObjects::IndexSerializer
    attribute_list = %i[
      flat_number
      entrance_number
      floor_in_house
      live_area
      room_count
      total_area
    ]
    attributes(*attribute_list)

    def logger(name, value)
      Rails.logger.debug name, value, "#{Time.now.strftime('%M:%S--%N')}"
    end

    attribute_list.each do |attribute_name|
      define_method attribute_name do |value|
        logger(attribute_name, value)
        super
      end
    end
  end
end