ActiveRecord :: Relation#explain始终完整运行查询

问题描述 投票:4回答:3

我正在尝试在Rails 3和Rails 4中使用explain方法来估计在一个特别昂贵的查询中返回的行数。它连接3个表,并可能导致对1000万行表的表扫描,这与count()聚合的结合特别慢(数据库为Postgres 9.3)。

我的问题是这个。如果使用内置的explain()方法,则在返回结果之前,查询总是在幕后IN FULL运行。这可能需要2分钟以上。在其他情况下,我想分析的查询可能要花几个小时才能运行(例如,用于报告)。

我有一个稍微难看的解决方案,其中我执行一个to_sql,在前面加上“解释”,然后执行查询。这适用于Rails 3,但需要对Rails 4进行一些重新设计。

所以我想我的问题是这个。有没有办法让内置的ARexplain()方法做我想要的事情,有没有其他优雅的方法做到这一点,还是这是AR :: explain()中的错误,需要记录并修复为某些错误点?

ruby-on-rails postgresql activerecord explain
3个回答
2
投票

这是我这样做的方式。在Rails 3和4中,我都为ActiveRecord :: Relation编写了一个初始化程序。

首先,在Rails 3中:

class ActiveRecord::Relation
  HUGE_COUNT = 20000

  def count(column_name = nil, options = {})
    exact, has_conditions = false, false
    h = (column_name.class == Hash ? column_name : options)
    exact = h[:exact]
    has_conditions = h[:conditions]
    has_distinct = (column_name.class == String) && (column_name =~ /\bdistinct\b/i)
    h = h.except(:exact) # Remove it because super won't understand it
    column_name.class == Hash ? column_name = h : options = h
    if exact || has_conditions || has_distinct
      super
    else
      est = estimated_count
      est > HUGE_COUNT ? est : super
    end
  end

  def estimated_count
    node = connection.execute("EXPLAIN #{self.to_sql}").first
    match = node['QUERY PLAN'].match(/rows=\d+\b/)
    match ? match[0].split('=').last.to_i : 0
  end

结束

Rails 4相同,除了:

  def estimated_count
    node = {}
    connection.unprepared_statement do
      node = connection.execute("EXPLAIN #{self.to_sql}").first
    end
    match = node['QUERY PLAN'].match(/rows=\d+\b/)
    match ? match[0].split('=').last.to_i : 0
  end

HUGE_COUNT很低,因为到目前为止,我发现这通常非常准确,精确到1或2%以内。可以满足我的需求,但是显然这很危险...


1
投票

我不确定是否有一种方法可以异步执行此操作。但是,使用resquesidekiq异步运行查询绝对可以从中受益。

这是resque的链接:

https://github.com/resque/resque

这是sidekiq的链接:

https://github.com/mperham/sidekiq


0
投票

之所以完全运行查询,是因为ActiveRecord .explain设计用于运行查询。它与SQL EXPLAIN不同。它更像是SQL EXPLAIN ANALYZE

[documentation建议,

解释实际上是执行查询,然后询问查询计划。

© www.soinside.com 2019 - 2024. All rights reserved.