我一直在使用activerecord适配器用于sqlite(没有rails),当我尝试插入任何东西时,它似乎太慢了。例如,当我做~250次插入时,它可能需要超过5分钟!我的架构如下:
create_table :courses do |t|
t.string :title
t.integer :ethmmy_id
end
add_index :courses, :ethmmy_id
create_table :announcements do |t|
t.string :title
t.string :author
t.string :body
t.string :uhash
t.belongs_to :courses
t.timestamps null: false
end
add_index :announcements, :courses_id
我使用的ActiveRecord模型如下:
class Course < ActiveRecord::Base
validates :ethmmy_id, uniqueness: true
has_many :announcements
end
class Announcement < ActiveRecord::Base
validates :uhash, uniqueness: true
belongs_to :course
end
我尝试修改PRAGMAS,比如在内存中设置日志或关闭同步I / O,但似乎没有太大区别。数据是从网络爬虫获取的,但这不是瓶颈,因为爬虫本身非常快。
更有说服力我注意到它会冻结每10个左右的插入来写入数据库,但它似乎要慢一些。我尝试将创建添加到单个事务中,如下所示:
ActiveRecord::Base.transaction do
Course.all.each do |course|
Announcement.create(....)
end
end
但仍然没有性能提升。
我甚至试图将整个数据库更多地用于测试,并且整个过程仍然需要大约5分钟才能进行250次插入。调试日志显示SQL查询每个只有大约0.1-0.2毫秒,并且在某些插入(每次都是相同的)时,整个事情似乎在那里冻结了几秒钟。
更新:使用ruby-prof查找大部分时间花在哪里后,我发现80%和更多的时间花在了IO.select上。这种方法是什么以及它叫什么?
答案 0 :(得分:1)
您是否考虑过更改为mysql而不是sqlite?
无论如何,使插入更快的一种方法是在原始sql中而不是通过activerecord来完成它们。每次调用create时,都会触发近10个触发的回调函数。
创建一个单独的SQL查询以插入一大堆数据可以如下所示:
base_sql = 'INSERT INTO announcements (`title`, `author`, `body`, `uhash`, `course_id`) VALUES '
announcements = []
Course.all.each do |course|
announcements << [title, author, body, uhash, course.id]
end
values_sql = announcements.map { |announcement| "(#{announcement.join(', ')})" }.join(', ')
ActiveRecord::Base.connection.execute(base_sql + values_sql)
你当然必须用真实值替换你在公告数组中放置的内容。
我在我的一个项目中使用了类似的东西,我们在不到一分钟的时间内在笔记本电脑上创建了38000条记录。
由于其中一个主要的性能优势是没有触发回调,您可以根据您的activerecord验证插入非法数据。重要的是要记住并考虑到这一点。
您的数据库可能会强制执行唯一性验证,但仍可能会强制执行。