MongoDB: Fatalna Wydajność Map

Mam długą historię z relacyjnymi bazami danych, ale jestem nowy w MongoDB i MapReduce, więc jestem prawie pewien, że robię coś nie tak. Przejdę od razu do pytania. Przepraszam, jeśli jest długa.

Mam tabelę bazy danych w MySQL, która śledzi liczbę wyświetleń profilu użytkownika każdego dnia. Do testowania ma 10 000 000 wierszy.

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;

Typowe dane mogą wyglądać tak.

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

Używam tego zapytania, aby uzyskać top 5 najczęściej oglądanych profili od 2010-07-16.

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

To zapytanie kończy się w mniej niż minutę. Nieźle!

Teraz przenosi się do świata MongoDB. Skonfigurowałem sharded environment używając 3 serwerów. Serwery M, S1 i S2. Użyłem następujących poleceń, aby ustawić rig up (Uwaga: zasłoniłem Addy 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

Gdy te były już uruchomione, wskoczyłem na serwer M i uruchomiłem mongo. Wydałem następujące polecenia:

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

Następnie zaimportowałem te same 10 000 000 wierszy z MySQL, co dało mi dokumenty, które wyglądają tak:

{
    "_id" : ObjectId("4cb8fc285582125055295600"),
    "username" : "Joe",
    "day" : "Fri May 21 2010 00:00:00 GMT-0400 (EDT)",
    "hits" : 16
}
Teraz nadchodzi prawdziwe mięso i ziemniaki... Moja mapa i zmniejsz funkcje. Z powrotem na serwerze M w powłoce ustawiłem zapytanie i wykonałem je w ten sposób.
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 }}
    }
);

I oto napotkałem problemy. to zapytanie zajęło ponad 15 minut, aby zakończyć! zapytanie MySQL zajęło mniej niż minutę. Oto wyjście:

{
        "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
        },
}

Bieganie nie tylko trwało wieczność, ale wyniki nawet nie wydają się być zgadza się.

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 }
Wiem, że te wartości powinny być znacznie wyższe.

Moje zrozumienie całego paradygmatu MapReduce polega na tym, że zadanie to powinno być podzielone pomiędzy wszystkie elementy shard, co powinno zwiększyć wydajność. Czekałem, aż Mongo skończy dystrybucję dokumentów między dwoma serwerami shard po imporcie. Każdy miał prawie dokładnie 5,000,000 dokumentów, kiedy zacząłem to zapytanie.

Więc muszę robić coś nie tak. Czy ktoś może mi dać jakieś wskazówki?

Edit: ktoś na ircu wspomniał o dodaniu indeksu w polu dnia, ale z tego co wiem, to zostało zrobione automatycznie przez MongoDB.

Author: Community, 2010-10-16

4 answers

Fragmenty MongoDB Z O ' Reilly:

Ceną korzystania z MapReduce jest szybkość: grupa nie jest szczególnie szybka, ale MapReduce jest wolniejszy i nie jest miało być użyte w "czasie rzeczywistym." Uruchamiasz MapReduce jako tło job, tworzy zbiór wyniki, a następnie można odpytywać, że kolekcja w czasie rzeczywistym.

options for map/reduce:

"keeptemp" : boolean 
If the temporary result collection should be saved when the connection is closed. 

"output" : string 
Name for the output collection. Setting this option implies keeptemp : true. 
 53
Author: nonopolarity,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-27 10:13:43

Może się spóźniłem, ale.....

Najpierw zapytujesz kolekcję, aby wypełnić MapReduce bez indeksu. Należy utworzyć indeks na "dzień".

MongoDB MapReduce jest pojedynczym wątkiem na jednym serwerze, ale działa równolegle na odłamkach. Dane w odłamkach mongo są przechowywane razem w ciągłych kawałkach posortowanych według klucza odłamkowego.

Jako że Twoim kluczem do shardingu jest " day " i pytasz o niego, prawdopodobnie używasz tylko jednego z trzech serwerów. Klucz odłamkowy jest używany tylko aby rozpowszechniać dane. Map Reduce zapyta przy użyciu indeksu" day " na każdym Odłamku i będzie bardzo szybkie.

Dodaj coś przed klawiszem dnia, aby rozpowszechnić dane. Nazwa użytkownika może być dobrym wyborem.

W ten sposób Mapa reduce zostanie uruchomiona na wszystkich serwerach i miejmy nadzieję, że skróci czas o trzy.

Coś takiego:

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 : {username : 1,day: 1} } );
use profiles
db.views.ensureIndex({ hits: -1 });
db.views.ensureIndex({ day: -1 });

Myślę, że z tymi dodatkami, można dopasować szybkość MySQL, jeszcze szybciej.

Również, lepiej nie używać go w czasie rzeczywistym. Jeśli Twoje dane nie trzeba być "drobiazgowo" precyzyjne, shedule map reduce zadanie co jakiś czas i korzystać z kolekcji wyników.

 28
Author: FrameGrace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-01 18:04:20

Nie robisz nic złego. (Oprócz sortowania na niewłaściwej wartości, jak już zauważyłeś w komentarzach.)

MongoDB map / reduce performance po prostu nie jest taki wielki. Jest to znany problem; patrz na przykład http://jira.mongodb.org/browse/SERVER-1197 gdzie naiwne podejście jest ~350x szybsze niż M / R.

Jedną z zalet jest to, że można określić nazwę stałej kolekcji wyjściowej za pomocą argumentu out wywołania mapReduce. Po zakończeniu M / R tymczasowy kolekcja zostanie przemianowana na stałą nazwę atomicznie. W ten sposób można zaplanować aktualizacje statystyk i odpytywać kolekcję wyjściową M/R w czasie rzeczywistym.

 6
Author: Joris Bontje,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-16 17:29:35

Czy próbowałeś już użyć złącza hadoop dla mongodb?

Spójrz na ten link tutaj: http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-hadoop/

Ponieważ używasz tylko 3 odłamków, Nie wiem, czy takie podejście poprawiłoby Twoją sprawę.

 0
Author: Rogerio Hilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-17 06:19:13