Ruby 在Rails中创建数千条记录

Ruby 在Rails中创建数千条记录,ruby,database,optimization,activerecord,ruby-on-rails-3,Ruby,Database,Optimization,Activerecord,Ruby On Rails 3,让我来准备一下:我的应用程序处理礼品卡。当我们创建卡片时,它们必须有一个唯一的字符串,用户可以用它来兑换。因此,当有人像零售商一样订购我们的礼品卡时,我们需要制作许多新的卡片对象并将它们存储在DB中 考虑到这一点,我正在尝试看看我的应用程序生成100000张卡的速度有多快。数据库专家,我不是,所以我需要有人来解释这个小现象:当我创建1000张卡时,需要5秒钟。当我创建100000张卡时,应该需要500秒,对吗 现在我知道你想看到什么了,我正在使用的卡片创建方法,因为第一个假设是,它会变得越来越慢

让我来准备一下:我的应用程序处理礼品卡。当我们创建卡片时,它们必须有一个唯一的字符串,用户可以用它来兑换。因此,当有人像零售商一样订购我们的礼品卡时,我们需要制作许多新的卡片对象并将它们存储在DB中

考虑到这一点,我正在尝试看看我的应用程序生成100000张卡的速度有多快。数据库专家,我不是,所以我需要有人来解释这个小现象:当我创建1000张卡时,需要5秒钟。当我创建100000张卡时,应该需要500秒,对吗

现在我知道你想看到什么了,我正在使用的卡片创建方法,因为第一个假设是,它会变得越来越慢,因为它会检查一堆卡片的唯一性,随着时间的推移会越来越多。但我可以给你看我的任务

desc "Creates cards for a retailer"
task :order_cards, [:number_of_cards, :value, :retailer_name] => :environment do |t, args|
  t = Time.now
  puts "Searching for retailer"
  @retailer = Retailer.find_by_name(args[:retailer_name])
  puts "Retailer found"
  puts "Generating codes"
  value = args[:value].to_i
  number_of_cards = args[:number_of_cards].to_i
  codes = []
  top_off_codes(codes, number_of_cards)
  while codes != codes.uniq
    codes.uniq!
    top_off_codes(codes, number_of_cards)
  end
  stored_codes = Card.all.collect do |c|
    c.code
  end
  while codes != (codes - stored_codes)
    codes -= stored_codes
    top_off_codes(codes, number_of_cards)
  end
  puts "Codes are unique and generated"
  puts "Creating bundle"
  @bundle = @retailer.bundles.create!(:value => value)
  puts "Bundle created"
  puts "Creating cards"
  @bundle.transaction do
    codes.each do |code|
      @bundle.cards.create!(:code => code)
    end
  end
  puts "Cards generated in #{Time.now - t}s"
end

def top_off_codes(codes, intended_number)
  (intended_number - codes.size).times do
    codes << ReadableRandom.get(CODE_LENGTH)
  end
end
desc“为零售商创建卡片”
任务:订购卡片,[:卡片数量,:价值,:零售商名称]=>:环境do | t,参数|
t=时间。现在
放置“搜索零售商”
@零售商=零售商。按零售商名称查找零售商(args[:零售商名称])
放置“找到零售商”
放置“生成代码”
value=args[:value]。到_i
卡片的数量=参数[:卡片的数量]。到
代码=[]
顶置密码(密码、卡片数量)
而代码!=代码uniq
代码,uniq!
顶置密码(密码、卡片数量)
结束
存储的|代码=Card.all.collect do | c|
c、 代码
结束
而代码!=(代码-存储的_代码)
代码-=存储的\u代码
顶置密码(密码、卡片数量)
结束
放置“代码是唯一的且已生成”
放置“创建捆绑包”
@bundle=@retailer.bundles.create!(:value=>value)
放置“已创建捆绑包”
将“创建卡片”放入
@捆绑交易
代码。每个do |代码|
@bundle.cards.create!(:code=>code)
结束
结束
放置“在#{Time.now-t}中生成的卡”
结束
def top_off_代码(代码、预期_编号)
(预期数量-代码。大小)。时间

代码我的第一个想法是关于事务——如果在事务中有100000个待提交的更改等待提交,这会使事情稍微慢一点,但是任何体面的DB都应该能够处理这一点

你用的是什么数据库

有哪些索引

任何数据库优化,如聚集表/索引


不确定是否支持Ruby事务处理-@bundle.transaction行是来自ActiveModel还是您正在使用的另一个库?

不是对您问题的回答,而是关于如何加快插入速度的几点建议:

  • 使用Ruby的
    Hash
    消除重复项-使用卡代码作为哈希键,将它们添加到哈希中,直到哈希增长到所需的大小。您也可以改用class(但我怀疑它是否比Hash快)
  • 在数据库中使用批量插入,而不是一系列插入查询。大多数数据库管理系统都提供了这种可能性:用新记录创建文本文件,并告诉数据库导入它。以下是和的链接

您在没有事务的情况下尝试过吗?在我知道事务存在之前,我只是在没有事务块的情况下创建它们。事务加快了进程。我正在使用postgreSQL,它托管在Heroku上。除了默认索引之外,我没有任何索引,但我肯定会尝试一下并发回。我试图实现您的哈希建议,但遇到了一些麻烦。我在生成代码时理解了唯一性测试。但是,如何针对DB中已经存在的卡测试唯一性呢?此外,我还试图实现您的导入想法,我正在使用一个名为activerecord导入的gem。因此,我将在一分钟内对其进行测试,看看这是否有帮助。由于我在上一篇评论中提到的原因,我最终没有使用哈希,但我使用activerecord导入进行了一些运行,加上一些新添加的索引,我的时间从一天多减少到了5.5分钟!非常感谢你的帮助!是的,独特性可能很难完全保证。您可以尝试的另一件事是UUID,而不是
可读的\u random
——它们往往是唯一的,因为它们包括(但不披露)创建时间戳以及随机部分。我认为使用UUID可以将冲突视为异常,而不是规则。哦,这里有另一个想法:使用PostgreSQL中的存储过程,可以完全跳过Ruby/Rails来执行此任务。使零售商id和所需的卡片数量输入参数,并在存储过程的循环中创建输入记录。嘿,这是一个非常好的主意!我对数据库一无所知,但我一定会调查的。我们将Heroku上的数据库升级到他们的Ronin软件包,这将卡的创建速度提高了100000到3.5分钟。但我肯定会调查UUID和Postgres程序。谢谢你的帮助!