Статьи

Факты MongoDB: Молниеносная агрегация

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

Вот так выглядит одна запись события времени:

1
2
3
4
5
{
        "_id" : ObjectId("529a2a988cccdb538932d31f"),
        "created_on" : ISODate("2012-05-02T06:08:47.835Z"),
        "value" : 0.9270193106494844
}

Помимо индекса первичного ключа по умолчанию «_id», мы также создали один для поля «create_on», так что это все наши индексы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "random.randomData",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "created_on" : 1
                },
                "ns" : "random.randomData",
                "name" : "created_on_1"
        }
]

Теперь давайте используем все 50 миллионов записей для создания ежедневного отчета, подсчитывая, сколько событий было сгенерировано за день, включая минимальное, максимальное и среднее значение для этого конкретного дня.

Вот как выглядит наш скрипт:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var start = new Date();
var dataSet = db.randomData.aggregate([
    {
        $group: {
                "_id": {
                    "year" : {
                        $year : "$created_on"
                    },
                    "dayOfYear" : {
                        $dayOfYear : "$created_on"
                    }
                },
                "count": {
                    $sum: 1
                },
                "avg": {
                    $avg: "$value"
                },
                "min": {
                    $min: "$value"
                },
                "max": {
                    $max: "$value"
                }      
            }
    },
    {
        $sort: {
            "_id.year" : 1,
            "_id.dayOfYear" : 1
        }  
    }
]);
if(dataSet.result != null && dataSet.result.length > 0) {
    print("Aggregated:" + dataSet.result.length + " days.");   
    db.dailyReport.insert(dataSet.result);
}
var end = new Date();
print("Aggregation took:" + (end.getTime() - start.getTime())/1000 + "s");

После объединения всех данных результаты сохраняются в новой коллекции dailyReport. Давайте запустим скрипт и посмотрим, что мы получим:

1
2
3
4
5
D:\wrk\vladmihalcea\mongodb-facts\aggregator\timeseries>mongo random aggregate_daily_report.js
MongoDB shell version: 2.4.6
connecting to: random
Aggregated:367 days.
Aggregation took:129.052s

Итак, за 129 секунд нам удалось построить наш отчет для всех этих данных. Давайте проверим новую коллекцию и посмотрим наши ежедневные отчеты.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
{
        "_id" : {
                "year" : 2012,
                "dayOfYear" : 1
        },
        "count" : 137244,
        "avg" : 0.5009360724400802,
        "min" : 0.0000013632234185934067,
        "max" : 0.9999953350052238
}
{
        "_id" : {
                "year" : 2012,
                "dayOfYear" : 2
        },
        "count" : 136224,
        "avg" : 0.49982110975583033,
        "min" : 0.0000023238826543092728,
        "max" : 0.9999841095414013
}

Так как мы сгенерировали наши значения, связанные с событием времени, используя Math.random (), мы все равно ожидали среднего, минимального и максимального значений Что действительно интересно, так это то, как быстро MongoDB удалось собрать все эти данные со скоростью 387440 документов в секунду.

Порадовавшись этому результату, давайте теперь проверим, как быстро мы можем случайным образом выбрать отчет за один час. Сначала мы сопоставляем интервал записей в один час, затем группируем и сортируем, чтобы наконец отобразить результаты в оболочке Mongo.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
var minDate = new Date(2012, 0, 1, 0, 0, 0, 0);
var maxDate = new Date(2013, 0, 1, 0, 0, 0, 0);
var delta = maxDate.getTime() - minDate.getTime();
var fromDate = new Date(minDate.getTime() + Math.random() * delta);
fromDate.setHours(0, 0, 0, 0);
var toDate = new Date(fromDate.getTime() + 60 * 60 * 1000);
 
print("Aggregating from " + fromDate + " to " + toDate);
 
var start = new Date();
 
var dataSet = db.randomData.aggregate([
    {
        $match: {
            "created_on" : {
                $gte: fromDate,
                $lt : toDate   
            }
        }
    },
    {
        $group: {
                "_id": {
                    "year" : {
                        $year : "$created_on"
                    },
                    "dayOfYear" : {
                        $dayOfYear : "$created_on"
                    },
                    "hour" : {
                        $hour : "$created_on"
                    }
                },
                "count": {
                    $sum: 1
                },
                "avg": {
                    $avg: "$value"
                },
                "min": {
                    $min: "$value"
                },
                "max": {
                    $max: "$value"
                }      
            }
    },
    {
        $sort: {
            "_id.year" : 1,
            "_id.dayOfYear" : 1,
            "_id.hour" : 1
        }  
    }
]);
if(dataSet.result != null && dataSet.result.length > 0) {
    dataSet.result.forEach(function(document)  {
        printjson(document);
    });
}
var end = new Date();
print("Aggregation took:" + (end.getTime() - start.getTime())/1000 + "s");

Запустив этот скрипт, мы получим следующий результат:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
D:\wrk\vladmihalcea\mongodb-facts\aggregator\timeseries>mongo random aggregate_hour_report.js
MongoDB shell version: 2.4.6
connecting to: random
Aggregating from Mon Jul 16 2012 00:00:00 GMT+0300 (GTB Daylight Time) to Mon Jul 16 2012 01:00:00 GMT+0300 (GTB Daylight Time)
{
        "_id" : {
                "year" : 2012,
                "dayOfYear" : 197,
                "hour" : 21
        },
        "count" : 5808,
        "avg" : 0.5015344015735451,
        "min" : 0.00005716201849281788,
        "max" : 0.9998941225931048
}
Aggregation took:0.209s

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

Структура агрегирования MongoDB чрезвычайно полезна, и ее производительность не может остаться незамеченной. Я показал вам только простые примеры, которые не требуют дополнительной оптимизации, чтобы продемонстрировать готовую производительность MongoDB.

  • Код доступен на GitHub .