使用ActiveRecord延迟删除MySQL中的临时表
我试图解决一个性能问题,我们在大量非顺序ID上运行where IN子句。根据和Performance 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
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缓存的原因,我尝试的方法在多次迭代中的速度要慢得多。我可能还可以尝试其他基准测试,但目前看来这种优化不值得
哦,好吧,”\_(ツ)_/“