所以我有一个脚本应该更新一个巨大的表(Postgres)。由于该表有大约150米的行,我想尽快完成它,使用多个线程似乎是一个完美的答案。但是,我看到的东西非常奇怪。
当我使用单个线程时,更新的写入时间比使用多个线程时要低得多。
require 'sequel'
.....
DB = Sequel.connect(DB_CREDS)
queue = Queue.new
read_query = query = DB["
SELECT id, extra_fields
FROM objects
WHERE XYZ IS FALSE
"]
read_query.use_cursor(:rows_per_fetch => 1000).each do |row|
queue.push(row)
end
到目前为止,IMO并不重要,因为我们只是从数据库中读取内容并且与写入无关。从这里开始,我尝试了两种方法。单线程和多线程。
注意 - 这不是我想要执行的实际UPDATE查询,它只是用于演示目的的伪查询。实际的查询要长得多,并且使用JSON和东西,所以我无法使用单个查询更新整个表。
单线程
until queue.empty?
photo = queue.shift
id = photo[:id]
update_query = DB["
UPDATE objects
SET XYZ = TRUE
WHERE id = #{id}
"]
result = update_query.update
end
如果我执行此操作,我会在数据库日志中看到每个更新查询花费的时间少于 0.01秒
I,[2016-08-15T10:45:48.095324#54495] INFO - :(0.001441s)更新 对象SET XYZ = TRUE WHERE id = 84395179
I,[2016-08-15T10:45:48.103818#54495] INFO - :(0.008331s)更新 对象SET XYZ = TRUE WHERE id = 84395181
I,[2016-08-15T10:45:48.106741#54495]信息 - :(0.002743s)更新 对象SET XYZ = TRUE WHERE id = 84395182
多线程
MAX_THREADS = 5
num_threads = 0
all_threads = []
until queue.empty?
if num_threads < MAX_THREADS
photo = queue.shift
num_threads += 1
all_threads << Thread.new {
id = photo[:id]
update_query = DB["
UPDATE photos
SET cv_tagged = TRUE
WHERE id = #{id}
"]
result = update_query.update
num_threads -= 1
Thread.exit
}
end
end
all_threads.each do |thread|
thread.join
end
现在,理论上它应该更快吧?但每次更新大约需要 0.5秒。我很惊讶那是什么情况。
I,[2016-08-15T11:02:10.992156#54583] INFO - :(0.414288s) UPDATE对象 SET XYZ = TRUE WHERE id = 119498834
I,[2016-08-15T11:02:11.097004#54583] INFO - :(0.622775s) UPDATE对象 SET XYZ = TRUE WHERE id = 119498641
I,[2016-08-15T11:02:11.097074#54583] INFO - :(0.415521s) UPDATE对象 SET XYZ = TRUE WHERE id = 119498826
关于 -
的任何想法为什么会这样?
如何提高多线程方法的更新速度。
答案 0 :(得分:0)
我在一个项目中经历了类似的事情(“将所有历史从遗留数据库导入到具有完全不同结构和组织的新数据库”)。除非你设法在别人的脚下射击自己,否则你有两个基本的瓶颈可供选择:
一些建议,
在您实施这些建议后,您知道在以下情况下您已尽力而为:
找到这个最佳位置,之后不要添加额外的ruby更新线程/进程(或添加更多硬件),那就是
PS签出https://github.com/ruby-concurrency/concurrent-ruby - 这是一个很棒的并行化库