在Rails中创建数千条记录

时间:2011-01-06 07:12:35

标签: ruby database optimization activerecord ruby-on-rails-3

让我设置舞台:我的应用程序处理礼品卡。当我们创建卡片时,他们必须有一个唯一的字符串,用户可以用它来兑换它。因此,当有人订购我们的礼品卡时,如零售商,我们需要制作大量新卡对象并将其存储在数据库中。

考虑到这一点,我试图看看我的应用程序生成100,000张卡的速度有多快。数据库专家,我不是,所以我需要有人解释这个小现象:当我创建1000张卡时,需要5秒钟。当我创建100,000张卡时,它应该需要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

我正在使用名为readable_random的gem作为唯一代码。因此,如果您仔细阅读所有代码,您将会看到它在开始创建卡片之前就完成了所有的唯一性测试。它还会在屏幕运行时将状态更新写入屏幕,并且它在创建时始终存在一段时间。与此同时,它通过独特性测试。所以我对stackoverflow社区的问题是:为什么我的数据库会因为添加更多卡而减慢速度?为什么每张卡的时间不是线性函数?我确信答案很简单,我只是一个对数据存储一无所知的白痴。如果有人有任何建议,你会如何优化这种方法,你认为你可以多快创建100,000张卡?

(当我在图表上绘制出我的时间并快速曲线拟合以获得我的线条公式时,我计算了使用我当前的代码创建100,000张卡需要多长时间并且它说5.5小时。这可能是完全错误的,我不确定。但是如果它保持在我弯曲的线上,就会在那附近。)

2 个答案:

答案 0 :(得分:2)

不是您的问题的答案,但有关如何更快地插入的一些建议:

  • 使用Ruby的Hash消除重复项 - 使用您的卡代码作为哈希键,将它们添加到哈希值,直到哈希增长到所需的大小。您也可以使用类Set代替(但我怀疑它比哈希更快)。
  • 批量插入用于数据库,而不是一系列INSERT查询。大多数DBMS提供了这样的可能性:创建具有新记录的文本文件,并告诉数据库导入它。以下是MySQLPostgreSQL的链接。

答案 1 :(得分:1)

我的第一个想法是围绕事务 - 如果你有100,000个等待在事务中提交的挂起更改会使事情变慢一些,但任何体面的DB都应该能够处理它。

你在使用什么数据库?

有哪些索引?

任何数据库优化,例如集群表/索引。

不确定Ruby事务支持 - 来自ActiveModel或您正在使用的其他库的@ bundle.transaction行是什么?