2011-01-06 15 views
3

Lassen Sie mich die Bühne: Meine Anwendung beschäftigt sich mit Geschenkkarten. Wenn wir Karten erstellen, müssen sie eine eindeutige Zeichenfolge haben, mit der der Benutzer sie einlösen kann. Wenn also jemand unsere Geschenkkarten wie ein Einzelhändler bestellt, müssen wir viele neue Kartenobjekte erstellen und in der Datenbank speichern.Erstellen von Tausenden von Datensätzen in Rails

In diesem Sinne versuche ich zu sehen, wie schnell ich meine Anwendung 100.000 Karten generieren lassen kann. Datenbankexperte, bin ich nicht, also brauche ich jemanden, der dieses kleine Phänomen erklärt: Wenn ich 1000 Karten erstelle, dauert es 5 Sekunden. Wenn ich 100.000 Karten erstelle, sollte es 500 Sekunden dauern, oder?

Jetzt weiß ich, was Sie sehen wollen, die Kartenerstellungsmethode, die ich verwende, weil die erste Annahme sein würde, dass es langsamer wird, weil es die Einzigartigkeit eines Kartenbündels prüft, mehr wie es weitergeht . Aber ich kann Ihnen meine Rake-Aufgabe zeigen

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 

Ich benutze ein Juwel namens lesbar_random für den eindeutigen Code. Wenn Sie also den gesamten Code durchlesen, werden Sie sehen, dass er alle Eindeutigkeitstests durchführt, bevor er mit dem Erstellen von Karten beginnt. Es schreibt auch Statusaktualisierungen auf den Bildschirm, während es ausgeführt wird, und es bleibt immer eine Weile beim Erstellen. Mittlerweile fliegt es durch die Einzigartigkeitstests. Meine Frage an die stackoverflow-Community lautet also: Warum wird meine Datenbank langsamer, wenn ich weitere Karten hinzufüge? Warum ist das keine lineare Funktion in Bezug auf die Zeit pro Karte? Ich bin sicher, die Antwort ist einfach und ich bin nur ein Idiot, der nichts über die Datenspeicherung weiß. Und wenn jemand Vorschläge hat, wie würden Sie diese Methode optimieren, und wie schnell würden Sie es schaffen, 100.000 Karten zu erstellen?

(Als ich meine Zeiten in einem Diagramm ausmachte und eine schnelle Kurve passierte, um meine Linienformel zu erhalten, berechnete ich, wie lange es dauern würde, 100.000 Karten mit meinem aktuellen Code zu erstellen falsch, ich bin mir nicht sicher, aber wenn es auf der Linie bleibt, bin ich genau dort.

+0

Haben Sie es ohne Transaktionen versucht? –

+0

Bevor ich wusste, dass Transaktionen existierten, habe ich sie nur ohne diesen Transaktionsblock erstellt. Die Transaktionen beschleunigten den Prozess. – willCosgrove

Antwort

2

keine Antwort auf Ihre Frage, aber ein paar Vorschläge, wie der Einsatz schneller zu machen:

  • Verwenden Ruby-Hash der Duplikate zu beseitigen - Ihren Kartencodes als Hash-Schlüssel verwendet, und fügen hinzu, sie zu einem Hash bis Ihr Hash auf die gewünschte Größe wächst. Sie können stattdessen auch die Klasse Set verwenden (aber ich bezweifle, dass es schneller ist als Hash).
  • Verwenden Sie Masseneinfügung in die Datenbank statt Reihe von INSERT-Abfragen. Die meisten DBMS bieten die Möglichkeit, eine Textdatei mit neuen Datensätzen zu erstellen und der Datenbank mitzuteilen, dass sie importiert werden soll. Hier finden Sie Links für MySQL und PostgreSQL.
+0

Ich habe versucht, Ihren Hash-Vorschlag zu implementieren, aber ich geriet in Schwierigkeiten. Ich habe die Eindeutigkeitsprüfung beim Generieren der Codes verstanden. Aber wie werde ich auf Einzigartigkeit gegen die Karten prüfen, die bereits in der DB sind? Außerdem habe ich versucht, Ihre Import-Idee zu implementieren, und ich verwende ein Juwel, das ich Activecore-Import genannt habe. Also werde ich es in einer Minute hier testen und ich werde sehen, ob das hilft. – willCosgrove

+0

Ich habe den Hash nicht verwendet, aus dem Grund, den ich in meinem letzten Kommentar erwähnt habe, aber ich habe einige Runs mit dem ActiveRecord-Import gemacht, und zusammen mit einigen neu hinzugefügten Indizes habe ich meine Zeit für 100.000 von über einem runter Tag bis 5.5 Minuten! Vielen Dank für deine Hilfe! – willCosgrove

+0

Yup, Einzigartigkeit kann schwierig sein, vollständig sicherzustellen. Eine andere Sache, die Sie anstelle Ihres 'lesbaren Randoms' ausprobieren können, sind UUIDs - sie sind in der Regel einzigartig, da sie den Erstellungszeitstempel zusammen mit dem Zufallsteil enthalten (aber nicht offen legen). Ich denke, dass die Verwendung von UUIDs Kollisionen als Ausnahmen behandeln kann, nicht in der Regel. –

1

Meine ersten Gedanken würden sich um Transaktionen handeln - wenn Sie 100.000 ausstehende Änderungen haben, die darauf warten, begangen zu werden die Transaktion, die die Dinge ein wenig verlangsamen würde, aber jeder ordentliche DB sollte damit umgehen können.

Welche DB benutzen Sie?

Welche Indizes sind vorhanden?

Alle DB-Optimierungen, z. B. gruppierte Tabellen/Indizes.

Nicht sicher von der Ruby-Transaktionsunterstützung - ist die @ bundle.transaction-Zeile etwas von ActiveModel oder einer anderen Bibliothek, die Sie verwenden?

+0

Ich benutze PostgreSQL, das auf Heroku gehostet wird. Ich habe keine Indizes neben dem Standard, aber ich werde es definitiv versuchen und zurück posten. – willCosgrove

Verwandte Themen