J'ai une longue histoire avec les bases de données relationnelles, mais je suis nouveau sur MongoDB et MapReduce, donc je suis presque certain que je dois faire quelque chose de mal. Je vais sauter directement dans la question. Désolé si c'est long.MongoDB: Terrible MapReduce Performance
J'ai une table de base de données dans MySQL qui suit le nombre de vues de profil de membre pour chaque jour. Pour tester il a 10 000 000 lignes.
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;
Les données typiques peuvent ressembler à ceci. J'utilise cette requête pour obtenir les 5 profils les plus consultés depuis 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
Cette requête se termine en moins d'une minute. Pas mal!
Maintenant, passer à l'univers de MongoDB. Je configure un environnement fragmenté en utilisant 3 serveurs. Serveurs M, S1 et S2. J'ai utilisé les commandes suivantes pour régler le gréement (Note: j'ai obscurci les adresses IP). Une fois que ceux-ci étaient opérationnels, j'ai sauté sur le serveur M et lancé mongo. J'ai donné les commandes suivantes:
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 });
Je puis importé les mêmes 10.000.000 lignes de MySQL, ce qui m'a donné des documents qui ressemblent à ceci:
{
"_id" : ObjectId("4cb8fc285582125055295600"),
"username" : "Joe",
"day" : "Fri May 21 2010 00:00:00 GMT-0400 (EDT)",
"hits" : 16
}
Maintenant vient la vraie viande et pommes de terre ici ... Ma carte et réduire les fonctions. De retour sur le serveur M dans le shell, je configure la requête et l'exécute comme ceci.
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 }}
}
);
Et voici, j'ai eu des problèmes. Cette requête a duré plus de 15 minutes! La requête MySQL a pris moins d'une minute. Voici le résultat:
{
"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
},
}
Non seulement at-il fallu pour toujours courir, mais les résultats ne semblent même pas être correct.
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 }
Je sais que ces valeurs devraient être beaucoup plus élevées.
Ma compréhension de l'ensemble du paradigme MapReduce est que la tâche d'effectuer cette requête doit être répartie entre tous les membres de fragment, ce qui devrait augmenter les performances. J'ai attendu que Mongo ait fini de distribuer les documents entre les deux serveurs de partition après l'importation. Chacun avait presque exactement 5 000 000 de documents lorsque j'ai commencé cette requête.
Je dois donc faire quelque chose de mal. Quelqu'un peut-il me donner des indications? Edit: Quelqu'un sur IRC a mentionné l'ajout d'un index sur le champ jour, mais autant que je sache, cela a été fait automatiquement par MongoDB.
Gah .. Juste réalisé une raison pour laquelle les résultats sont incorrects. J'aurais dû trier sur "valeur" plutôt que "hits". – mellowsoon
Un problème est que lorsque vous importez vos données dans Mongo, la valeur 'day' est une chaîne géante, mais dans mysql, c'est une date (entier).Lorsque vous placez vos données dans mongo, veillez à les stocker en tant que type de date. – Clint
vous pouvez également séparer le champ de date et heure, et stocker la date sous forme de chaîne "20110101" ou entier 20110101 et l'index basé sur la date –