我一直在为 sqlite(无导轨)使用 activerecord 适配器,当我尝试插入任何东西时,它似乎太慢了。例如,当我执行 ~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
但仍然没有性能提升。
为了测试,我什至尝试将整个数据库放入 more 中,整个过程仍然需要大约 5 分钟才能插入 250 次。调试日志显示每个 SQL 查询只有大约 0.1-0.2 毫秒,并且在某些插入(每次相同的插入)时,整个事情似乎在那里卡住了几秒钟。
更新: 在使用 ruby-prof 找到大部分时间花费的地方后,我发现 80% 甚至更多的时间都花在了 IO.select 上。这个方法是什么,叫什么?
最佳答案
您是否考虑过改用 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)
当然,您必须用实际值替换您放置在 announcements 数组中的内容。
我在我的一个项目中使用了类似的东西,我们在不到一分钟的时间内在我的笔记本电脑上创建了 38000 条记录。
由于主要的性能优势之一是不会触发任何回调,因此您可以根据 activerecord 验证插入非法数据。记住并考虑这一点很重要。
唯一性验证可能会由您的数据库强制执行,但它可能仍会强制执行。
关于ruby - SQLite3 Activerecord 太慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28974772/