Gem ActiveRecord 用 bulk_insert 来批量插入数据,提高效率

huacnlee · 发布于 2016年4月06日 · 最后由 night_7th 回复于 2016年7月13日 · 1973 次阅读
2

实际的项目环境中,我们时常会有一次动作写入多条数据的场景,比如导数据,创建通知、Event 给多个人...

简单做法可能大家会直接循环插入

receiver_ids.each do |user_id|
  Event.create(user_id: user_id, event_type: 'test')
end

或者你可能会在外面包个 transaction 来减少 Commit 次数,从而微微的提速,但还是有 N 次数据库的调用

Event.transaction do
 receiver_ids.each do |user_id|
   Event.create(user_id: user_id, event_type: 'test')
 end
end

或者你还知道 SQL 批量插入的方法:

INSERT INTO events (event_type, user_id) VALUES("test", 2),("test", 5),("test", 8) ...;

ref: MySQL, PostgreSQL

关于第三种方式,有 Gem 帮助我们简单来实现:

https://github.com/jamis/bulk_insert

# Gemfile
gem 'bulk_insert'

然后我们就可以用了:

Event.bulk_insert(set_size: 100) do |worker|
  receiver_ids.each do |user_id|
    worker.add({ user_id: user_id, event_type: 'test' })
  end
end

相关链接

共收到 15 条回复
16154

:plus1:

25907

:plus1:

5

:plus1:

1342

不想用 gem 的话以下代码可实现该功能

class ActiveRecord::Base
  def self.import!(record_list)
    raise ArgumentError "record_list not an Array of Hashes" unless record_list.is_a?(Array) && record_list.all? {|rec| rec.is_a? Hash }
    key_list, value_list = convert_record_list(record_list)
    sql = "INSERT INTO #{self.table_name} (#{key_list.join(", ")}) VALUES #{value_list.map {|rec| "(#{rec.join(", ")})" }.join(" ,")}"
    self.connection.insert_sql(sql)
  end

  def self.convert_record_list(record_list)
    key_list = record_list.map(&:keys).flatten.uniq.sort

    value_list = record_list.map do |rec|
      list = []
      key_list.each {|key| list <<  ActiveRecord::Base.connection.quote(rec[key]) }
      list
    end

    return [key_list, value_list]
  end
end

2

#5楼 @ywjno 插入数据有很多细节场景,这段代码在你的项目里面实际跑过么?

1342

#6楼 @huacnlee 跑过的,用在 定时任务里面 创建通知消息里面

2

#8楼 @hooopo

大概130w数据用时16秒,平均每秒插入8w条记录左右。

... 暴快啊!

6061

postgresql 直接 copy 暴快

8

#11楼 @gihnius pg的copy不支持replace选项啊,在做增量插入的时候好麻烦。pg 9.5才支持UPSERT,但copy还不行。

25144

666666 👍

24025

正好碰到这个需求了.刚改成Event.transaction do,准备改成第三种,真是有用👍

15073

测试了下效果挺好的,但是如果Model层有validation的话,并不会触发。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册