SQLite3 Activerecord太慢了

时间:2015-03-10 21:51:10

标签: ruby performance activerecord sqlite

我一直在使用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上。这种方法是什么以及它叫什么?

1 个答案:

答案 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验证插入非法数据。重要的是要记住并考虑到这一点。

您的数据库可能会强制执行唯一性验证,但仍可能会强制执行。