场景: 我需要从一个 txt 文件 (有 4000 行,每行只有一个手机号码) 读取文件中的手机号,并将其存入数据库中。最后,将手机号码和密码生成一个新的文件 output.txt 中。我的代码如下:
path = File.join(Rails.root, "db/phones.txt")
output = File.join(Rails.root, "db/output.txt")
output_str = ""
File.open(path, "r").each do |line|
phone = line.chomp
output_str << phone
user = User.find_by_phone(phone)
if user
output_str << " " + "已注册\n"
else
user = User.simple_create(phone)
output_str << " " + user.passwd + "\n"
end
end
File.open(output, "w") do |f|
f << output_str
end
这段代码执行时间大概在 9 分钟以上,如何能提高这段代码的执行效率?
用 SQLITE 可以用INSERT OR IGNORE
一次插入很多条记录,问题是 SQLITE 一条语句参数个数是有上限的,一次插入 4000 可能已经超了。别的数据库应该也有类似的方法,且没参数个数的限制。
数据量太小了,文件操作完全可以依托内存,这样可以文件读写一次完成。 进一步,如果数据库数据不多,那么干脆一次性把 user 表全读出来,然后数组减数组,暴力解决有时候也是一个方案
4000 条记录 * 50ms / 1000 / 60 = 才 3 分钟 用了 9 分钟,那每条耗费 135ms
Ubuntu 11.04 \n \l
mysql Ver 14.14 Distrib 5.1.54, for debian-linux-gnu (i686) using readline 6.2
我觉得时间花的最多的就是 4000 次插入操作:
https://github.com/zdennis/activerecord-import
users = []
4000.times do
users << User.new
end
User.import users
4000 个查询,可以分成 40 个查询,每次 100 个?
Rails 的数组有方法 #in_groups_of
和 #in_groups
,可以给数据分组。分好组后,用select xxx where id in (a, b, c)
这种查询就减少查询次数了。
默认一次 insert 就是一次事务。如果把所有的 insert 用一个事物包起来,会快很多。前提是保证不要回滚啊。
除了数据库,我问个问题,楼主的 ruby 版本是什么?建议用最新的 1.9.3 试试? 4000 个 sql,不做什么优化,应该非常快的。 bechmark 下,看下瓶颈真正在哪里。