it-swarm.com.de

Wie lade ich mit Scala 100 Millionen Datensätze für Leistungstests in MongoDB?

Ich habe ein kleines Skript, das in Scala geschrieben ist und eine MongoDB-Instanz mit 100.000.000 Beispieldatensätzen laden soll. Die Idee ist, die Datenbank vollständig zu laden und dann einige Leistungstests durchzuführen (und gegebenenfalls zu optimieren/neu zu laden).

Das Problem ist, dass die Ladezeit pro 100.000 Datensätze ziemlich linear ansteigt. Zu Beginn meines Ladevorgangs dauerte das Laden dieser Datensätze nur 4 Sekunden. Jetzt, bei fast 6.000.000 Datensätzen, dauert es zwischen 300 und 400 Sekunden, um dieselbe Menge (100.000) zu laden! Das sind zwei Größenordnungen langsamer! Abfragen sind immer noch unkompliziert, aber bei dieser Geschwindigkeit kann ich nie die Datenmenge laden, die ich haben möchte.

Funktioniert dies schneller, wenn ich eine Datei mit allen meinen Aufzeichnungen (alle 100.000.000!) Ausschreibe und dann mongoimport verwende, um das Ganze zu importieren? Oder sind meine Erwartungen zu hoch und ich benutze die DB über das hinaus, was sie bewältigen soll?

Irgendwelche Gedanken? Vielen Dank!

Hier ist mein Drehbuch:

import Java.util.Date

import com.mongodb.casbah.Imports._
import com.mongodb.casbah.commons.MongoDBObject

object MongoPopulateTest {
  val ONE_HUNDRED_THOUSAND = 100000
  val ONE_MILLION          = ONE_HUNDRED_THOUSAND * 10

  val random     = new scala.util.Random(12345)
  val connection = MongoConnection()
  val db         = connection("mongoVolumeTest")
  val collection = db("testData")

  val INDEX_KEYS = List("A", "G", "E", "F")

  def main(args: Array[String]) {
    populateCoacs(ONE_MILLION * 100)
  }

  def populateCoacs(count: Int) {
    println("Creating indexes: " + INDEX_KEYS.mkString(", "))
    INDEX_KEYS.map(key => collection.ensureIndex(MongoDBObject(key -> 1)))

    println("Adding " + count + " records to DB.")

    val start     = (new Date()).getTime()
    var lastBatch = start

    for(i <- 0 until count) {
      collection.save(makeCoac())
      if(i % 100000 == 0 && i != 0) {
        println(i + ": " + (((new Date()).getTime() - lastBatch) / 1000.0) + " seconds (" +  (new Date()).toString() + ")")
        lastBatch = (new Date()).getTime()
      }
    }

    val elapsedSeconds = ((new Date).getTime() - start) / 1000

    println("Done. " + count + " COAC rows inserted in " + elapsedSeconds + " seconds.")
  }

  def makeCoac(): MongoDBObject = {
    MongoDBObject(
      "A" -> random.nextPrintableChar().toString(),
      "B" -> scala.math.abs(random.nextInt()),
      "C" -> makeRandomPrintableString(50),
      "D" -> (if(random.nextBoolean()) { "Cd" } else { "Cc" }),
      "E" -> makeRandomPrintableString(15),
      "F" -> makeRandomPrintableString(15),
      "G" -> scala.math.abs(random.nextInt()),
      "H" -> random.nextBoolean(),
      "I" -> (if(random.nextBoolean()) { 41 } else { 31 }),
      "J" -> (if(random.nextBoolean()) { "A" } else { "B" }),
      "K" -> random.nextFloat(),
      "L" -> makeRandomPrintableString(15),
      "M" -> makeRandomPrintableString(15),
      "N" -> scala.math.abs(random.nextInt()),
      "O" -> random.nextFloat(),
      "P" -> (if(random.nextBoolean()) { "USD" } else { "GBP" }),
      "Q" -> (if(random.nextBoolean()) { "PROCESSED" } else { "UNPROCESSED" }),
      "R" -> scala.math.abs(random.nextInt())
    )
  }

  def makeRandomPrintableString(length: Int): String = {
    var result = ""
    for(i <- 0 until length) {
      result += random.nextPrintableChar().toString()
    }
    result
  }
}

Hier ist die Ausgabe von meinem Skript:

Creating indexes: A, G, E, F
Adding 100000000 records to DB.
100000: 4.456 seconds (Thu Jul 21 15:18:57 EDT 2011)
200000: 4.155 seconds (Thu Jul 21 15:19:01 EDT 2011)
300000: 4.284 seconds (Thu Jul 21 15:19:05 EDT 2011)
400000: 4.32 seconds (Thu Jul 21 15:19:10 EDT 2011)
500000: 4.597 seconds (Thu Jul 21 15:19:14 EDT 2011)
600000: 4.412 seconds (Thu Jul 21 15:19:19 EDT 2011)
700000: 4.435 seconds (Thu Jul 21 15:19:23 EDT 2011)
800000: 5.919 seconds (Thu Jul 21 15:19:29 EDT 2011)
900000: 4.517 seconds (Thu Jul 21 15:19:33 EDT 2011)
1000000: 4.483 seconds (Thu Jul 21 15:19:38 EDT 2011)
1100000: 4.78 seconds (Thu Jul 21 15:19:43 EDT 2011)
1200000: 9.643 seconds (Thu Jul 21 15:19:52 EDT 2011)
1300000: 25.479 seconds (Thu Jul 21 15:20:18 EDT 2011)
1400000: 30.028 seconds (Thu Jul 21 15:20:48 EDT 2011)
1500000: 24.531 seconds (Thu Jul 21 15:21:12 EDT 2011)
1600000: 18.562 seconds (Thu Jul 21 15:21:31 EDT 2011)
1700000: 28.48 seconds (Thu Jul 21 15:21:59 EDT 2011)
1800000: 29.127 seconds (Thu Jul 21 15:22:29 EDT 2011)
1900000: 25.814 seconds (Thu Jul 21 15:22:54 EDT 2011)
2000000: 16.658 seconds (Thu Jul 21 15:23:11 EDT 2011)
2100000: 24.564 seconds (Thu Jul 21 15:23:36 EDT 2011)
2200000: 32.542 seconds (Thu Jul 21 15:24:08 EDT 2011)
2300000: 30.378 seconds (Thu Jul 21 15:24:39 EDT 2011)
2400000: 21.188 seconds (Thu Jul 21 15:25:00 EDT 2011)
2500000: 23.923 seconds (Thu Jul 21 15:25:24 EDT 2011)
2600000: 46.077 seconds (Thu Jul 21 15:26:10 EDT 2011)
2700000: 104.434 seconds (Thu Jul 21 15:27:54 EDT 2011)
2800000: 23.344 seconds (Thu Jul 21 15:28:17 EDT 2011)
2900000: 17.206 seconds (Thu Jul 21 15:28:35 EDT 2011)
3000000: 19.15 seconds (Thu Jul 21 15:28:54 EDT 2011)
3100000: 14.488 seconds (Thu Jul 21 15:29:08 EDT 2011)
3200000: 20.916 seconds (Thu Jul 21 15:29:29 EDT 2011)
3300000: 69.93 seconds (Thu Jul 21 15:30:39 EDT 2011)
3400000: 81.178 seconds (Thu Jul 21 15:32:00 EDT 2011)
3500000: 93.058 seconds (Thu Jul 21 15:33:33 EDT 2011)
3600000: 168.613 seconds (Thu Jul 21 15:36:22 EDT 2011)
3700000: 189.917 seconds (Thu Jul 21 15:39:32 EDT 2011)
3800000: 200.971 seconds (Thu Jul 21 15:42:53 EDT 2011)
3900000: 207.728 seconds (Thu Jul 21 15:46:21 EDT 2011)
4000000: 213.778 seconds (Thu Jul 21 15:49:54 EDT 2011)
4100000: 219.32 seconds (Thu Jul 21 15:53:34 EDT 2011)
4200000: 241.545 seconds (Thu Jul 21 15:57:35 EDT 2011)
4300000: 193.555 seconds (Thu Jul 21 16:00:49 EDT 2011)
4400000: 190.949 seconds (Thu Jul 21 16:04:00 EDT 2011)
4500000: 184.433 seconds (Thu Jul 21 16:07:04 EDT 2011)
4600000: 231.709 seconds (Thu Jul 21 16:10:56 EDT 2011)
4700000: 243.0 seconds (Thu Jul 21 16:14:59 EDT 2011)
4800000: 310.156 seconds (Thu Jul 21 16:20:09 EDT 2011)
4900000: 318.421 seconds (Thu Jul 21 16:25:28 EDT 2011)
5000000: 378.112 seconds (Thu Jul 21 16:31:46 EDT 2011)
5100000: 265.648 seconds (Thu Jul 21 16:36:11 EDT 2011)
5200000: 295.086 seconds (Thu Jul 21 16:41:06 EDT 2011)
5300000: 297.678 seconds (Thu Jul 21 16:46:04 EDT 2011)
5400000: 329.256 seconds (Thu Jul 21 16:51:33 EDT 2011)
5500000: 336.571 seconds (Thu Jul 21 16:57:10 EDT 2011)
5600000: 398.64 seconds (Thu Jul 21 17:03:49 EDT 2011)
5700000: 351.158 seconds (Thu Jul 21 17:09:40 EDT 2011)
5800000: 410.561 seconds (Thu Jul 21 17:16:30 EDT 2011)
5900000: 689.369 seconds (Thu Jul 21 17:28:00 EDT 2011)
37
Mike Cialowicz

Einige Hinweise :

  1. Indizieren Sie Ihre Sammlung nicht vor dem Einfügen von , da Einfügungen den Index ändern, der ein Overhead ist. Alles einfügen und Index erstellen.

  2. Verwenden Sie statt "save" mongoDB "batchinsert" , das viele Datensätze in einem Vorgang einfügen kann. Legen Sie also pro Stapel ca. 5000 Dokumente ein. Sie werden einen bemerkenswerten Leistungszuwachs feststellen.

    siehe Methode # 2 von insert here , es braucht ein Array von Dokumenten anstelle eines einzelnen Dokuments einzufügen. Siehe auch die Diskussion in diesem Thread

    Und wenn Sie mehr Benchmark setzen wollen -

  3. Dies ist nur eine Vermutung, Versuchen Sie es mit einer begrenzten Sammlung einer vordefinierten Größe , um alle Ihre Daten zu speichern. Die Capped-Collection ohne Index hat eine sehr gute Einfügungsleistung.

50
DhruvPathak

Ich habe das Gleiche gehabt. Soweit ich das beurteilen kann, kommt es auf die Zufälligkeit der Indexwerte an. Wenn ein neues Dokument eingefügt wird, muss es offensichtlich auch alle zugrunde liegenden Indizes aktualisieren. Da Sie zufällige Werte anstelle von sequentiellen Werten in diese Indizes einfügen, greifen Sie ständig auf den gesamten Index zu, um herauszufinden, wo der neue Wert platziert werden soll.

Dies ist alles in Ordnung, wenn alle Indexe glücklich im Arbeitsspeicher sitzen, aber sobald sie zu groß werden, müssen Sie die Platte anschlagen, um Indexeinfügungen durchzuführen, und die Festplatte beginnt zu Thrashing- und Write-Performance-Dies.

Vergleichen Sie beim Laden der Daten db.collection.totalIndexSize() mit dem verfügbaren Speicher, und Sie werden wahrscheinlich feststellen, dass dies der Fall ist.

Am besten erstellen Sie die Indizes nachdem Sie die Daten geladen haben. Das Problem wird jedoch immer noch nicht gelöst, wenn der erforderliche _id-Index einen Zufallswert (GUID, Hash usw.) enthält. Dann ist es am besten, wenn Sie über das Scherben nachdenken oder mehr RAM erhalten.

6
Chris Fulstow

Was ich in meinem Projekt gemacht habe, war ein bisschen Multithreading (das Projekt ist in C #, aber ich hoffe, der Code ist selbsterklärend). Nach dem Spielen mit der erforderlichen Anzahl von Threads stellte sich heraus, dass das Einstellen der Anzahl der Threads auf die Anzahl der Kerne zu einer geringfügig besseren Leistung (10-20%) führt. Ich denke jedoch, dass diese Erhöhung hardwarespezifisch ist. Hier ist der Code:

    public virtual void SaveBatch(IEnumerable<object> entities)
    {
        if (entities == null)
            throw new ArgumentNullException("entities");

        _repository.SaveBatch(entities);
    }


    public void ParallelSaveBatch(IEnumerable<IEnumerable<object>> batchPortions)
    {
        if (batchPortions == null)
            throw new ArgumentNullException("batchPortions");
        var po = new ParallelOptions
                 {
                     MaxDegreeOfParallelism = Environment.ProcessorCount
                 };
        Parallel.ForEach(batchPortions, po, SaveBatch);
    }
4
Yurii Hohan

Eine andere Alternative ist der Versuch von TokuMX . Sie verwenden Fractal-Indizes, was bedeutet, dass mit der Zeit nicht langsamer wird, je größer die Datenbank wird .

TokuMX wird in einer zukünftigen Version von MongoDB als benutzerdefinierter Speichertreiber enthalten sein.

Die aktuelle Version von MongoDB läuft unter Linux. Ich war unter Windows recht schnell mit Vagrant unterwegs.

0
Contango