ActiveRecord有效地查询树结构

时间:2014-07-09 10:32:02

标签: ruby-on-rails ruby-on-rails-3 performance activerecord

我继承了一个Rails 3应用程序,它将大部分数据存储为相当复杂的树结构。该应用程序通常运行良好,但我们注意到性能方​​面的一些问题,主要是围绕数据库交互。

我看到很多查询都出现在我的日志中:

SELECT `messages`.* FROM `messages` WHERE `messages`.`context_type` = 'Node' AND `messages`.`context_id` IN (153740, 153741, /* about a thousand records... */ 154837, 154838, 154839, 154840, 154841, 154842, 154843)

接下来是许多单个查询,看起来好像一次又一次地查询同一条记录:

[1m[35mCACHE (0.0ms)[0m  SELECT `plans`.* FROM `plans` WHERE `plans`.`type` IN ('Scenario') AND `plans`.`id` = 1435 LIMIT 1

我的日志具有大约八十次的确切查询 - 现在我猜测初始Cache消息意味着它可能是从缓存中提取而不是每次都返回数据库,但它仍然看起来很多,这种事情反复发生。

我猜测上面的查询是一个向后拉出的关联,以便message belongs_to plan并且它正在加载所有消息然后为每个消息拉出计划而不是像在一个理智的世界中那样做,从计划开始,然后加载所有消息。

在这种情况下,单个请求包含 1641 SELECT语句,而且我很可能认为数据库流量非常大(更不用说顺序LIMIT 1个查询的数量了对于同一表中的相邻数据)是一个重要的瓶颈。我不愿发布太多代码,但这是一个较大查询的典型示例:

 def nodes
    include_objects = [:sector, :market, :messages, :node_user_prefs, :reference_node, :project, {:available_events => :available_event_nodes}, :customer_factors, :active_components, :tags, { :event_histories => :node}, {:event_histories => :user }]

    project = self
    @cached_nodes ||= begin
      all_nodes = orig_nodes.includes(include_objects)
      all_nodes = all_nodes.map { |n| n.tap { |i| i.cached_project = project } }
      all_node_ids = all_nodes.map(&:id)
      all_nodes.select{ |n| n.type == 'InitialNode' || all_node_ids.include?(n.parent_node_id) } 
    end
  end

显然,查询非常多样化,应用程序很大,但这很好地代表了所采用的标准方法。

使用ActiveRecord轻松获胜可以尝试加快速度吗?我可以很容易地将一个查询放在一起,在一次往返中将所有需要的数据拉出来,但是将形式 - 冗余和所有 - 形成到我的模型层次结构中是多么容易?我可以用什么其他技术来帮助你?

1 个答案:

答案 0 :(得分:0)

Ancestry Gem

无论如何都不是直接解决方法,但您可能希望考虑ancestry gem -

这将为您提供一种创建树结构的方法,您可以通过该方式调用单个记录&然后让他们的后代称为你的愿望。这将减少您的SQL查询:

enter image description here

如果以这种方式设置节点/对象,它将允许您调用一次所需的记录。其余部分将填充祖先。如果您希望我透露更多相关信息,请在评论和评论中告诉我。我将详细介绍具体细节