2016-09-19 30 views
1

我試圖解決性能問題,我們在大量的非順序ID上運行WHERE IN子句。根據this和Performance MySQL的書籍,您可以通過創建一個有問題的臨時表並加入您關心的表來加速性能。推遲使用ActiveRecord在MySQL中刪除一個臨時表

我有一個ActiveRecord::Base類中的以下Rails代碼:

def self.where_in(field, ids) 
    tmp_table = "tmp_table_#{SecureRandom.uuid.gsub('-', '_')}" 
    begin 
    # Create temporary table with one column 
    connection.execute("CREATE TEMPORARY TABLE #{tmp_table} (param INT NOT NULL PRIMARY KEY) ENGINE=Memory") 

    # Insert ids into the table (doesn't have to be ids) 
    vals = ids.map{|i| "(#{i})"}.join(", ") 
    connection.execute("INSERT INTO #{tmp_table} (param) VALUES #{vals};") 

    # Return the join relation which is the same as WHERE IN (...) 
    return self.joins("INNER JOIN #{tmp_table} on #{field} = #{tmp_table}.param").all 
    ensure 
    # Drop table after we're done...this is the problem 
    connection.execute("DROP TEMPORARY TABLE IF EXISTS #{tmp_table}") 
    end 
end 

但問題是,這創造依賴於我丟棄在保證聲明的臨時表存在的SQL語句。如果我刪除了確保聲明,它仍然可以正常工作,但臨時表仍然存在。

鑑於此,我的問題是這樣的:

我怎麼會「推遲」這個表短大跌眼鏡表名到背景上的工人在以後的時間下降的下落?

OR

我是安全的無法刪除表,只是假設,連接池將收穫的連接,從而刪除表最後?

回答

0

因此相當多的研究之後,我回答我自己的問題:

  1. 沒有辦法推遲表的下降,但是,我能夠迫使關係執行現在使用ActiveRecord::Relation#load方法查詢。

  2. 在我們的應用程序(我相信很多其他人)中,我們緩存連接以備後用,很少回收它們,所以不要刪除表格會是非常明顯的內存泄漏。

我結束了在一個Util類代替AR基本編碼這種方法了:

def self.where_in(collection, field, params) 
    tmp_table = "tmp_table_#{SecureRandom.uuid.gsub('-', '_')}" 
    collection.connection.execute("CREATE TEMPORARY TABLE #{tmp_table} (param INT NOT NULL PRIMARY KEY) ENGINE=Memory") 

    vals = params.map{|i| "(#{i})"}.join(", ") 
    collection.connection.execute("INSERT INTO #{tmp_table} (param) VALUES #{vals};") 

    records = collection.joins("INNER JOIN #{tmp_table} on #{field} = #{tmp_table}.param").load 

    yield records if block_given? 

    collection.connection.execute("DROP TEMPORARY TABLE IF EXISTS #{tmp_table}") 
    return records.to_a 
end 

的問題變成了,當我和基準反駁我的前提下,這種方法實際上是更快。我用下面的代碼基準:

Benchmark.bm do |x| 
    x.report { 1000.times { Thing.where(id: refs).count } } 
    x.report { 1000.times { Util.where_in(Thing, :id, refs) {|o| o.count }}} 
end 

,結果是非常糟糕:

user  system  total  real 
0.940000 0.050000 0.990000 ( 1.650669) 
8.950000 0.260000 9.210000 (12.201616) 

由於MySQL的緩存,我試過的方法是在多次迭代慢得多。我可以嘗試其他的基準測試,但目前看來這種優化不值得。

哦好吧¯\_(ツ)_/¯