使用ActiveRecord延迟删除MySQL中的临时表

使用ActiveRecord延迟删除MySQL中的临时表,mysql,ruby-on-rails,ruby,activerecord,Mysql,Ruby On Rails,Ruby,Activerecord,我试图解决一个性能问题,我们在大量非顺序ID上运行where IN子句。根据和Performance MySQL的书,您可以通过创建一个带有相关字段的临时表并加入您关心的表来提高性能 我在ActiveRecord::Base类中有以下Rails代码: def self.where_in(field, ids) tmp_table = "tmp_table_#{SecureRandom.uuid.gsub('-', '_')}" begin # Create temporary t

我试图解决一个性能问题,我们在大量非顺序ID上运行where IN子句。根据和Performance 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语句,我将在Sure语句中删除临时表。如果删除sure语句,它可以正常工作,但是临时表仍然存在

有鉴于此,我的问题是:

我如何“推迟”删除此表,而不将表名弹出到后台工作程序中,以便稍后删除?


我是否可以安全地不丢弃表,而只是假设连接池将获取连接,从而最终丢弃表?

因此,经过大量研究,我回答了自己的问题:

  • 没有办法推迟表的删除,但是,我现在可以使用
    ActiveRecord::relation\load
    方法强制关系执行查询

  • 在我们的应用程序中(我相信还有许多其他应用程序),我们缓存连接以供以后使用,很少回收它们,因此不删除表将是一个非常明显的内存泄漏

  • 我最终在
    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缓存的原因,我尝试的方法在多次迭代中的速度要慢得多。我可能还可以尝试其他基准测试,但目前看来这种优化不值得


    哦,好吧,
    ”\_(ツ)_/“

    所以经过一番研究,我回答了自己的问题:

  • 没有办法推迟表的删除,但是,我现在可以使用
    ActiveRecord::relation\load
    方法强制关系执行查询

  • 在我们的应用程序中(我相信还有许多其他应用程序),我们缓存连接以供以后使用,很少回收它们,因此不删除表将是一个非常明显的内存泄漏

  • 我最终在
    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缓存的原因,我尝试的方法在多次迭代中的速度要慢得多。我可能还可以尝试其他基准测试,但目前看来这种优化不值得

    哦,好吧,
    ”\_(ツ)_/“