7

我一直在努力優化我的項目的數據庫調用,我注意到在性能下面的兩個相同的呼叫之間「顯著」的區別: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) 

的平均使用第一個版本的方法需要300毫秒才能執行(該操作在其中被稱爲總共幾千倍),而使用第二版本的方法需要大約550毫秒。速度幾乎降低了100%。

我仔細檢查了由第二個版本生成的SQL,它與第一個相同,除了預先在表名列中添加表列之外。

  • 爲什麼減速? ActiveRecord和SQL之間的轉換是否真的使得這個操作接近2倍?
  • 如果我需要多次執行相同的操作並且我不想打開開銷,我是否需要堅持直寫SQL(甚至可能是sproc)?

謝謝!

+1

只是使用.explain看看生成的查詢,我敢肯定,它看起來不同,這就是爲什麼需要這麼長很多 – antpaw

+0

我雙重檢查查詢計劃,它們都是相同的,成本和所有。必須從第二個版本的.sum中替代.select,因爲您從該版本中獲得了Fixnum,並且我無法找到在用於生成它的查詢上執行.explain的方法。 –

回答

2

一些東西跳出來。首先,如果這段代碼被調用2000次,並且需要額外運行250ms,那麼每次調用〜0.125ms將Arel轉換爲SQL,這並不是不現實的。其次,我不確定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

基於分析,第一個版本跳轉到執行查詢。 然而,第二個版本花費了大約30%的整個執行時間在各種AR魔法的30層深處。 版本較慢的版本可以在這裏看到:http://pastebin.com/bipTy3c5 直接的SQL版本在這裏:http://pastebin.com/LysaGUTy –