2011-07-21 13 views
36

Tôi có một tập lệnh nhỏ được viết bằng Scala nhằm mục đích tải một cá thể MongoDB lên tới 100.000.000 bản ghi mẫu. Ý tưởng là để có được tất cả các DB nạp, và sau đó làm một số thử nghiệm hiệu suất (và điều chỉnh/tái tải nếu cần thiết).Làm thế nào để tải 100 triệu bản ghi vào MongoDB với Scala để kiểm tra hiệu năng?

Vấn đề là thời gian tải trên 100.000 bản ghi tăng lên khá tuyến tính. Khi bắt đầu quá trình tải, chỉ mất 4 giây để tải các bản ghi này. Bây giờ, với gần 6.000.000 bản ghi, nó mất từ ​​300 đến 400 giây để tải cùng một số tiền (100.000)! Đó là hai đơn đặt hàng của cường độ chậm hơn! Các truy vấn vẫn linh hoạt, nhưng với tốc độ này, tôi sẽ không bao giờ có thể tải lượng dữ liệu mà tôi muốn.

Điều này sẽ hoạt động nhanh hơn nếu tôi viết một tệp với tất cả hồ sơ của mình (tất cả 100.000.000!), Và sau đó sử dụng mongoimport để nhập toàn bộ nội dung? Hoặc là những kỳ vọng của tôi quá cao và tôi đang sử dụng DB vượt quá những gì nó phải xử lý?

Mọi suy nghĩ? Cảm ơn!

Dưới đây là kịch bản của tôi:

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

Dưới đây là đầu ra từ kịch bản của tôi:

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) 
+0

Dữ liệu của bạn nên sử dụng ít nhất khoảng 8 GB bộ nhớ. Ngay cả chỉ mục cũng phải mất ít nhất nửa gigabyte. Bạn có chắc chắn rằng DB có thể phù hợp với nó vào RAM? Tôi không phải là một chuyên gia MongoDB bằng bất kỳ phương tiện nào, nhưng tôi cho rằng nó có thể bị chậm do trao đổi. – Madoc

+4

Hãy thử thêm chỉ mục * sau * bạn chèn dữ liệu, điều này sẽ cải thiện hiệu suất chèn. – pingw33n

Trả lời

49

Một số lời khuyên:

  1. Đừng chỉ số bộ sưu tập của bạn trước khi chèn, như chèn sửa đổi các chỉ số mà là một chi phí. Chèn mọi thứ, sau đó tạo chỉ mục.

  2. thay vì "lưu", sử dụng mongoDB "batchinsert" có thể chèn nhiều bản ghi trong 1 thao tác. Vì vậy, có khoảng 5000 tài liệu được chèn vào mỗi đợt. Bạn sẽ thấy hiệu suất vượt trội.

    xem phương pháp # 2 chèn here, phải mất nhiều tài liệu để chèn thay vì tài liệu đơn lẻ. Xem thêm các cuộc thảo luận trong this thread

    Và nếu bạn muốn chuẩn hơn -

  3. Đây chỉ là một phỏng đoán, hãy thử sử dụng một bộ sưu tập mũ của một kích thước lớn được xác định trước để lưu trữ tất cả các dữ liệu của bạn. Bộ sưu tập được giới hạn không có chỉ mục có hiệu suất chèn rất tốt.

+1

Rất đẹp. Tôi đang ở ~ 5 giây cho mỗi 100k đối tượng sử dụng casbah collection.insert (Danh sách [MongoDBObject] (...)) với các lô 5000 cho mỗi lần chèn (và không có chỉ mục). Hơn 15 triệu thực hiện ngay bây giờ, chỉ trong vài phút. Để có được con số đó trước đó, tôi phải chạy qua đêm. Cảm ơn bạn! –

+2

100 triệu lượt tải trong 1 giờ và 20 phút. Mỗi một trong bốn chỉ mục có vẻ mất khoảng một giờ để tạo, vì vậy sẽ mất khoảng 5 giờ tổng cộng. Không tệ. Cảm ơn một lần nữa. –

+0

Âm thanh tuyệt vời. Không có gì . – DhruvPathak

6

Tôi đã có được điều tương tự. Theo như tôi có thể nói, nó đi xuống đến sự ngẫu nhiên của các giá trị chỉ mục. Bất cứ khi nào một tài liệu mới được chèn vào, nó rõ ràng cũng cần cập nhật tất cả các chỉ mục cơ bản. Bởi vì bạn đang chèn ngẫu nhiên, trái ngược với tuần tự, giá trị vào các chỉ mục này, bạn liên tục truy cập toàn bộ chỉ mục để tìm vị trí đặt giá trị mới. Điều này là tất cả tốt khi bắt đầu khi tất cả các chỉ mục đang ngồi vui vẻ trong bộ nhớ, nhưng ngay khi chúng phát triển quá lớn, bạn cần bắt đầu nhấn đĩa để chèn chỉ mục, sau đó đĩa bắt đầu đập và ghi hiệu suất chết .

Khi bạn đang tải dữ liệu, hãy thử so sánh db.collection.totalIndexSize() với bộ nhớ khả dụng và có thể bạn sẽ thấy điều này xảy ra.

Đặt cược tốt nhất của bạn là tạo chỉ mục sau bạn đã tải dữ liệu. Tuy nhiên, điều này vẫn không giải quyết được vấn đề khi đó là chỉ số _id bắt buộc có chứa một giá trị ngẫu nhiên (GUID, băm, vv), thì cách tiếp cận tốt nhất của bạn có thể là suy nghĩ về việc sharding hoặc nhận được nhiều RAM hơn.

4

Những gì tôi đã làm trong dự án của tôi đã được thêm lên một chút đa luồng (dự án là trong C# nhưng tôi hy vọng mã là tự giải thích). Sau khi chơi với số lượng cần thiết của chủ đề nó bật ra rằng thiết lập số lượng các chủ đề đến số lõi dẫn đến một hiệu suất tốt hơn một chút (10-20%) nhưng tôi cho rằng tăng này là phần cứng cụ thể. Đây là mã:

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); 
    } 
0

Một cách khác là thử TokuMX. Họ sử dụng chỉ số Fractal có nghĩa là it does not slow down over time as the database gets bigger.

TokuMX sẽ được đưa vào như một trình điều khiển lưu trữ tùy chỉnh trong phiên bản MongoDB sắp tới.

Phiên bản hiện tại của MongoDB chạy dưới Linux. Tôi đã và đang chạy trên Windows khá nhanh chóng bằng cách sử dụng Vagrant.

Các vấn đề liên quan