Scalaで記述された小さなスクリプトがあります。これは、100,000,000個のサンプルレコードでMongoDBインスタンスをロードすることを目的としています。DBをすべてロードしてから、いくつかのパフォーマンステストを行います(そして必要に応じて調整/再ロードします)。
問題は、100,000レコードあたりの読み込み時間がかなり直線的に増加することです。ロードプロセスの開始時に、これらのレコードをロードするのに4秒しかかかりませんでした。現在、約6,000,000レコードで、同じ量(100,000)をロードするのに300〜400秒かかります。これは2桁遅くなります。クエリはまだ素早いですが、このレートでは、必要な量のデータを読み込むことができません。
すべてのレコード(すべて100,000,000!)を含むファイルを書き出し、 mongoimport を使用して全体をインポートすると、これはより速く機能しますか?または、私の期待が高すぎて、DBが処理するはずの範囲を超えて使用していますか?
何かご意見は?ありがとう!
これが私のスクリプトです:
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
}
}
これが私のスクリプトの出力です。
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)
いくつかのヒント :
挿入するとインデックスが変更されるため、挿入する前にコレクションにインデックスを付けないでください。オーバーヘッドです。すべてを挿入してから、インデックスを作成します。
「save」の代わりに、1回の操作で多くのレコードを挿入できるmongoDB「batchinsert」を使用します。したがって、バッチごとに約5000のドキュメントが挿入されます。驚くべきパフォーマンスの向上が見られます。
挿入のmethod#2 here を参照してください。単一のドキュメントではなく、挿入するドキュメントの配列が必要です。 this thread の説明も参照してください
さらにベンチマークを行いたい場合は-
これは単なる推測です事前定義された大きなサイズの上限付きコレクションを使用してすべてのデータを保存してみてください。インデックスのないキャップされたコレクションは、挿入パフォーマンスが非常に優れています。
私は同じことをしました。私が知る限り、それはインデックス値のランダム性に帰着します。新しいドキュメントが挿入されるときはいつでも、すべての基礎となるインデックスも更新する必要があります。これらのインデックスには、シーケンシャルな値ではなくランダムな値を挿入しているため、常にインデックス全体にアクセスして、新しい値を配置する場所を見つけています。
これは、すべてのインデックスがメモリにうまく収まっている状態から始めれば問題ありませんが、インデックスが大きくなりすぎるとすぐにディスクにアクセスしてインデックスの挿入を開始する必要があり、その後ディスクがスラッシングを開始し、書き込みパフォーマンスが低下します。
データをロードしているときに、db.collection.totalIndexSize()
を使用可能なメモリと比較してみてください。これが行われるのを確認できます。
あなたの最善の策は、インデックスを作成することです後データをロードしました。ただし、ランダムな値(GUID、ハッシュなど)を含む必須の_idインデックスである場合、これでも問題は解決されません。最善のアプローチは、シャーディングまたはRAMの増設について考えることです。
私のプロジェクトでやったことは、マルチスレッドを少し追加したことです(プロジェクトはC#ですが、コードが自明であることを願っています)。必要な数のスレッドで遊んだ後、スレッドの数をコアの数に設定すると、パフォーマンスがわずかに向上する(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);
}
別の選択肢は、 TokuMX を試すことです。それらはフラクタルインデックスを使用します。つまり、データベースが大きくなっても 時間の経過に伴って速度が低下することはありません 。
TokuMXは、MongoDBの次期バージョンにカスタムストレージドライバーとして含まれる予定です。
MongoDBの現在のバージョンはLinuxで動作します。 Vagrant を使用して、Windowsで非常に迅速に稼働していました。