2016-09-19 32 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的缓存,我试过的方法是在多次迭代慢得多。我可以尝试其他的基准测试,但目前看来这种优化不值得。

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