Rails 创建 100 万条记录时控制台退出

Rails Console Quits when creating 1 million records

我正在尝试构建一个逻辑,允许我使用 Ruby 在 Rails 6、PostgreSQL 和 Docker 上批量插入一百万条记录到数据库中。为了提供一点背景知识,我有一个 Food 模型,其中 table:

  create_table "foods", force: :cascade do |t|    
    t.string  "name", null: false
    t.string  "type", null: false
    t.datetime "expiration_date", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

在模型中,我构建了以下逻辑:

class Food < ApplicationRecord
  def self.bulk(record_num)
    self.insert_all(create_records(record_num))
  end

  def self.create_records(record_num)
    record_num.times.map do |num|
      {
        name: "food_#{num}",
        type: "food_type_#{num}",
        expiration_date: rand(1.years.ago..Time.current),
        created_at: Time.current,
        updated_at: Time.current
      }
    end
  end
end

我可以在大约 70 秒内播种大约 200,000 条记录,但是当它达到 300,000 条记录时,我的 postgres 服务器退出并给我以下错误:

ActiveRecord::StatementInvalid: PG::ConnectionBad: PQconsumeInput() server closed the connection unexpectedly 

我尝试重置数据库以及 docker 容器,但似乎没有任何效果。 我还将继续搜索选项,但如果有人可以帮助我找到解决此问题的线索,我将不胜感激。

谢谢!

这里有几个问题:

  1. 您有一个名为 type 的字段,它是 Rails 中的保留关键字。这会在您尝试调用时引起问题,例如 Food.first:
> Food.first
  Food Load (0.8ms)  SELECT "foods".* FROM "foods" ORDER BY "foods"."id" ASC LIMIT   [["LIMIT", 1]]
Traceback (most recent call last):
        2: from (irb):5
        1: from (irb):6:in `rescue in irb_binding'
ActiveRecord::SubclassNotFound (The single-table inheritance mechanism failed to locate the subclass: 'food_type_0'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Food.inheritance_column to use another column for that information.)

最好将其重命名为类似 food_type 的名称。

  1. 我能够在配备 16GB RAM 和 M1 芯片的 MacBook Pro 上 运行 代码。这让我认为您的计算机在执行期间 运行ning 内存不足。我怀疑问题是您的方法使用 .map,其中 returns 一个哈希数组:
def self.create_records(record_num)
  record_num.times.map do |num|
    {
      name: "food_#{num}",
      type: "food_type_#{num}",
      expiration_date: rand(1.years.ago..Time.current),
    }
  end
end
  1. 无需多次调用 Time.current。最好将其存储在局部变量中并使用它。

我认为您的问题有两种解决方案:

  1. 批量执行插入
  2. 运行 具有更多 RAM 的机器上的代码:
class Food < ApplicationRecord
  class << self
    def bulk num
      insert_all create_records(num)
    end

    private def create_records(num)
      time = Time.current
      num.times.map do |num|
        {
          name: "food_#{num}",
          food_type: "food_type_#{num}",
          expiration_date: rand(1.year.ago..time),
          created_at: time,
          updated_at: time,
        }
      end
    end
  end
end