7

私は私のプロジェクトのDB呼び出しの最適化に取り組んできたと私は下の2回の同一の呼び出しの間の性能における「重要」の違いに気づい:ActiveRecordクエリはストレートSQLよりもはるかに遅いですか?

connection = ActiveRecord::Base.connection() 
pgresult = connection.execute(
    "SELECT SUM(my_column) 
    FROM table 
    WHERE id = #{id} 
    AND created_at BETWEEN '#{lower}' and '#{upper}'") 

と第二版:

sum = Table. 
     where(:id => id, :created_at => lower..upper). 
     sum(:my_column) 

メソッドは、平均で最初のバージョンを使用して実行するのに300msかかる(その中で合計数千回と呼ばれます)、第2バージョンを使用するメソッドは約550msかかります。それは速度がほぼ100%低下します。

2番目のバージョンで生成されたSQLを再確認しました。最初のテーブルと同じですが、テーブル名の前にテーブルの列が追加されています。

  • なぜスローダウンですか? ActiveRecordとSQLの間の変換は実際に操作をほぼ2倍にするのですか?
  • 同じ操作を何度も実行する必要があり、オーバーヘッドにぶつかりたくない場合は、まっすぐなSQL(おそらくはsproc)を書くことに固執する必要がありますか?

ありがとう!

+1

だけ.explain使用して生成されたクエリを見て、私はそれが違って見える、それはそんなに長く – antpaw

+0

私は、クエリ・プランダブルチェックを取る理由thatsの、彼らは両方とも同じであると確信している、コストとすべて。 2番目のバージョンで.sumを.selectに置き換えなければならなかったのですが、Fixnumを元に戻すと、生成するために使用されたクエリに対して.explainを実行する方法が見つけられません。 –

答えて

2

いくつかのものが飛び出します。

第1に、このコードが2000回呼び出され、実行に250msかかる場合、それはArelをSQLに変換するための呼び出しあたり〜0.125msです。これは非現実的ではありません。

第2に、RubyのRangeの内部についてはわかりませんが、lower..upperは範囲のサイズなどの計算を行っている可能性があります。これは大きなパフォーマンスヒットになります。

次の場合、同じパフォーマンスが表示されますか?

sum = Table. 
     where(:id => id). 
     where(:created_at => "BETWEEN ? and ?", lower, upper). 
     sum(:my_column) 
+1

私が理解する限り、..は、オペランドがTimeオブジェクトであると判断した場合、ActiveRecordがBETWEENステートメントに変換する単純な構文的な砂糖です。私はそのバージョンを試して、遅いバージョンと同じ正確な数字を取得し続けました。 基本的には、変換が時間を費やしているように聞こえます。私はそれをより細かくするためにプロファイルします。 –

+0

ええ、それはまだ通過するRangeオブジェクトを生成する必要がありますが、イテレータが生成されないため、何の努力もしないと思います。 ARはそれらを合計する前に各項目にオブジェクトを作成できますか?それはそれを遅くするかもしれません。しかし、そうは思わない。 – iHiD

+0

プロファイリングに基づいて、最初のバージョンは直接クエリを実行するようにジャンプします。 しかし、第2のバージョンでは、30レベルのさまざまなARマジックのスタックに全体の実行時間の35%を費やしています。 ここでは、より低速なバージョンを見ることができます:http://pastebin.com/bipTy3c5 ストレートSQLバージョンはhttp://pastebin.com/LysaGUTy –

関連する問題