Подтвердить что ты не робот

Как загрузить 100 миллионов записей в MongoDB с помощью Scala для тестирования производительности?

У меня есть небольшой script, который написан в Scala, который предназначен для загрузки экземпляра MongoDB с 100 000 000 выборочных записей. Идея состоит в том, чтобы загрузить всю базу данных DB, а затем выполнить некоторые тесты производительности (и при необходимости настроить/перезагрузить).

Проблема заключается в том, что время загрузки на 100 000 записей увеличивается довольно линейно. В начале моего процесса загрузки потребовалось всего 4 секунды для загрузки этих записей. Теперь, имея почти 6 000 000 записей, он занимает от 300 до 400 секунд, чтобы загрузить ту же сумму (100 000)! Это на два порядка медленнее! Запросы по-прежнему остаются быстрыми, но с такой скоростью я никогда не смогу загрузить объем данных, который мне бы хотелось.

Будет ли это работать быстрее, если я напишу файл со всеми моими записями (все 100 000 000!), а затем используйте mongoimport, чтобы импортировать Все это? Или мои ожидания слишком высоки, и я использую DB за пределами того, что она должна обрабатывать?

Любые мысли? Спасибо!

Здесь мой script:

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
  }
}

Здесь вывод из моего script:

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)
4b9b3361

Ответ 1

Некоторые советы:

  • Не индексируйте свою коллекцию до вставки, так как вставки изменяют индекс, который является накладными расходами. Вставьте все, затем создайте индекс.

  • вместо "save" используйте mongoDB "batchinsert" , который может вставить много записей за 1 операцию. Так что на каждую партию вставлено около 5000 документов. Вы увидите замечательную производительность.

    см. метод # 2 вставки здесь, он принимает массив документов для вставки вместо одного документа. Также см. Обсуждение в этой теме

    И если вы хотите сравнить больше -

  • Это просто предположение, попробуйте использовать ограниченную коллекцию предопределенного большого размера для хранения всех ваших данных. Коллекция Capped без индекса имеет очень хорошую производительность вставки.

Ответ 2

У меня было то же самое. Насколько я могу судить, это сводится к случайности значений индекса. Всякий раз, когда вставлен новый документ, очевидно, также необходимо обновить все базовые индексы. Поскольку вы вставляете случайные, а не последовательные значения в эти индексы, вы постоянно получаете доступ ко всему индексу, чтобы найти, где разместить новое значение.

Все это хорошо, когда все индексы сидят счастливо в памяти, но, как только они становятся слишком большими, вам нужно начать нажимать на диск, чтобы делать вставки индексов, а затем диск начинает бить и записывать показатели производительности.

Когда вы загружаете данные, попробуйте сравнить db.collection.totalIndexSize() с доступной памятью, и вы, вероятно, увидите, что это произойдет.

Лучше всего создать индексы после загрузки данных. Однако это все еще не решает проблему, когда это необходимый индекс _id, который содержит случайное значение (GUID, хэш и т.д.), Тогда ваш лучший подход может состоять в том, чтобы подумать о том, чтобы ошпарить или получить больше ОЗУ.

Ответ 3

То, что я сделал в моем проекте, заключалось в том, что добавлялось немного многопоточности (проект находится на С#, но я надеюсь, что код не требует пояснений). После воспроизведения с нужным количеством потоков оказалось, что установка количества потоков в число ядер приводит к немного лучшей производительности (10-20%), но я полагаю, что это повышение связано с оборудованием. Вот код:

    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

Другой вариант - попробовать TokuMX. Они используют Фрактальные индексы, что означает, что он не замедляется со временем по мере увеличения базы данных.

TokuMX будет включен как пользовательский драйвер хранилища в предстоящей версии MongoDB.

Текущая версия MongoDB работает под Linux. Я быстро и быстро запускал Windows, используя Vagrant.