MongoDB: ужасная производительность MapReduce

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

У меня есть таблица базы данных в MySQL, которая отслеживает количество просмотров профилей участников за каждый день. Для тестирования у него 10 000 000 строк.

CREATE TABLE `profile_views` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `username` varchar(20) NOT NULL,
  `day` date NOT NULL,
  `views` int(10) unsigned default '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `username` (`username`,`day`),
  KEY `day` (`day`)
) ENGINE=InnoDB;

Типичные данные могут выглядеть так.

+--------+----------+------------+------+
| id     | username | day        | hits |
+--------+----------+------------+------+
| 650001 | Joe      | 2010-07-10 |    1 |
| 650002 | Jane     | 2010-07-10 |    2 |
| 650003 | Jack     | 2010-07-10 |    3 |
| 650004 | Jerry    | 2010-07-10 |    4 |
+--------+----------+------------+------+

Я использую этот запрос, чтобы получить 5 самых просматриваемых профилей с 16 июля 2010 г.

SELECT username, SUM(hits)
FROM profile_views
WHERE day > '2010-07-16'
GROUP BY username
ORDER BY hits DESC
LIMIT 5\G

Этот запрос выполняется менее чем за минуту. Неплохо!

Теперь перейдем к миру MongoDB. Я настраиваю сегментированную среду, используя 3 сервера. Серверы M, S1 и S2. Я использовал следующие команды для настройки установки (Примечание: я скрыл IP-адреса).

S1 => 127.20.90.1
./mongod --fork --shardsvr --port 10000 --dbpath=/data/db --logpath=/data/log

S2 => 127.20.90.7
./mongod --fork --shardsvr --port 10000 --dbpath=/data/db --logpath=/data/log

M => 127.20.4.1
./mongod --fork --configsvr --dbpath=/data/db --logpath=/data/log
./mongos --fork --configdb 127.20.4.1 --chunkSize 1 --logpath=/data/slog

После того, как они были запущены и работали, я запрыгнул на сервер M и запустил mongo. Я выполнил следующие команды:

use admin
db.runCommand( { addshard : "127.20.90.1:10000", name: "M1" } );
db.runCommand( { addshard : "127.20.90.7:10000", name: "M2" } );
db.runCommand( { enablesharding : "profiles" } );
db.runCommand( { shardcollection : "profiles.views", key : {day : 1} } );
use profiles
db.views.ensureIndex({ hits: -1 });

Затем я импортировал те же 10 000 000 строк из MySQL, что дало мне документы, которые выглядят следующим образом:

{
    "_id" : ObjectId("4cb8fc285582125055295600"),
    "username" : "Joe",
    "day" : "Fri May 21 2010 00:00:00 GMT-0400 (EDT)",
    "hits" : 16
}

Теперь здесь самое настоящее мясо и картошка ... Моя карта и функции сокращения. Вернувшись на сервер M в оболочке, я настраиваю запрос и выполняю его следующим образом

use profiles;
var start = new Date(2010, 7, 16);
var map = function() {
    emit(this.username, this.hits);
}
var reduce = function(key, values) {
    var sum = 0;
    for(var i in values) sum += values[i];
    return sum;
}
res = db.views.mapReduce(
    map,
    reduce,
    {
        query : { day: { $gt: start }}
    }
);

И вот тут я столкнулся с проблемами. На выполнение этого запроса ушло более 15 минут! Запрос MySQL занял менее минуты. Вот' s результат:

{
        "result" : "tmp.mr.mapreduce_1287207199_6",
        "shardCounts" : {
                "127.20.90.7:10000" : {
                        "input" : 4917653,
                        "emit" : 4917653,
                        "output" : 1105648
                },
                "127.20.90.1:10000" : {
                        "input" : 5082347,
                        "emit" : 5082347,
                        "output" : 1150547
                }
        },
        "counts" : {
                "emit" : NumberLong(10000000),
                "input" : NumberLong(10000000),
                "output" : NumberLong(2256195)
        },
        "ok" : 1,
        "timeMillis" : 811207,
        "timing" : {
                "shards" : 651467,
                "final" : 159740
        },
}

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

db[res.result].find().sort({ hits: -1 }).limit(5);
{ "_id" : "Joe", "value" : 128 }
{ "_id" : "Jane", "value" : 2 }
{ "_id" : "Jerry", "value" : 2 }
{ "_id" : "Jack", "value" : 2 }
{ "_id" : "Jessy", "value" : 3 }

Я знаю, что эти значения должны быть намного выше.

Мое понимание всего MapReduce Парадигма заключается в том, что задача выполнения этого запроса должна быть разделена между всеми членами шарда, что должно повысить производительность. Я подождал, пока Mongo не закончит распределение документов между двумя серверами сегментов после импорта. Когда я начал этот запрос, у каждого было почти ровно 5 000 000 документов.

Так что я, должно быть, делаю что-то не так. Может ли кто-нибудь дать мне какие-нибудь указатели?

Edit: Кто-то в IRC упомянул добавление индекса в поле дня, но насколько я могу судить, это было сделано автоматически MongoDB.

42
задан Community 22 September 2017 в 18:01
поделиться