성능 테스트를 위해 스칼라와 함께 1억 개의 레코드를 MongoDB에 로드하는 방법은 무엇입니까?
MongoDB 인스턴스를 100,000,000개의 샘플 레코드로 로드하기 위한 Scala로 작성된 작은 스크립트가 있습니다.DB를 모두 로드한 다음 성능 테스트를 수행합니다(필요한 경우 튜닝/재로드).
문제는 레코드 100,000개당 로드 시간이 상당히 선형적으로 증가한다는 것입니다.로드 프로세스를 시작할 때 이 레코드를 로드하는 데 4초밖에 걸리지 않았습니다.이제 거의 6백만 개의 레코드에서 동일한 양(100,000)을 로드하는 데 300초에서 400초가 걸립니다!그것은 두 배나 느립니다!쿼리는 여전히 신속하지만, 이 속도라면 원하는 양의 데이터를 로드할 수 없습니다.
모든 레코드(100,000,000!)로 파일을 작성한 다음 mongoimport를 사용하여 전체를 가져오면 이 작업이 더 빨리 수행됩니까?아니면 제 기대가 너무 커서 DB가 감당할 수 있는 수준을 넘어서 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)
몇 가지 팁:
삽입하기 전에 컬렉션을 인덱싱하지 마십시오. 삽입하면 오버헤드가 되는 인덱스가 수정됩니다.모든 항목을 삽입한 다음 인덱스를 만듭니다.
"저장" 대신 mongoDB "batch insert"를 사용하면 한 번의 작업으로 많은 레코드를 삽입할 수 있습니다.따라서 배치당 약 5,000개의 문서가 삽입됩니다.놀라운 성능 향상을 볼 수 있습니다.
여기에 삽입 방법#2를 참조하십시오. 단일 문서 대신 삽입할 문서 배열이 필요합니다.이 스레드의 토론도 참조하십시오.
그리고 더 벤치마킹하고 싶다면,
이것은 단지 추측일 뿐입니다. 미리 정의된 큰 크기의 상한 컬렉션을 사용하여 모든 데이터를 저장해 보십시오.인덱스가 없는 캡션 컬렉션은 삽입 성능이 매우 우수합니다.
저도 같은 일을 겪었습니다.제가 아는 한, 그것은 지수 값의 무작위성으로 귀결됩니다.새 문서가 삽입될 때마다 모든 기본 색인을 업데이트해야 합니다.이러한 인덱스에 순차적인 값이 아닌 임의의 값을 삽입하기 때문에 새 값을 배치할 위치를 찾기 위해 전체 인덱스에 지속적으로 액세스하게 됩니다.
이는 모든 인덱스가 메모리에 올바르게 저장되어 있을 때 시작하는 것으로 괜찮지만 인덱스 삽입을 위해 디스크를 치기 시작하면 디스크가 스레싱을 시작하고 쓰기 성능이 저하됩니다.
데이터를 로드할 때 비교해 보십시오.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에서 매우 빠르게 실행할 수 있었습니다.
언급URL : https://stackoverflow.com/questions/6783212/how-to-load-100-million-records-into-mongodb-with-scala-for-performance-testing
'source' 카테고리의 다른 글
Ionic 잘못된 패키지 이름 __ngcc_entry_points_.json (0) | 2023.06.21 |
---|---|
Git는 "경고: 알려진 호스트 목록에 영구적으로 추가되었습니다"라고 말합니다. (0) | 2023.06.21 |
getResources().getColor()가 더 이상 사용되지 않습니다. (0) | 2023.06.11 |
UIButton: 선택된 강조 표시된 상태에 대한 이미지 설정 (0) | 2023.06.11 |
Angular 2 호출 집합Interval() 정의되지 않은 서비스 양식 종속성 주입 (0) | 2023.06.11 |