Ho una lunga storia di database relazionali, ma sono nuovo di MongoDB e MapReduce, quindi sono quasi sicuro che devo fare qualcosa di sbagliato. Passerò direttamente alla domanda. Scusa se è lungo.MongoDB: Terribile prestazione MapReduce
Ho una tabella di database in MySQL che tiene traccia del numero di visualizzazioni del profilo membro per ogni giorno. Per il test ha 10.000.000 di righe.
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;
I dati tipici potrebbero essere simili a questo.
+--------+----------+------------+------+
| 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 |
+--------+----------+------------+------+
Io uso questa query per ottenere i 5 profili più visti dal 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
Questa query termina tra meno di un minuto. Non male!
Ora stiamo passando al mondo di MongoDB. Ho installato un ambiente più semplice usando 3 server. Server M, S1 e S2. Ho usato i seguenti comandi per impostare il rig up (Nota: ho oscurato gli 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
Una volta installati e funzionanti, ho saltato sul server M e ho lanciato mongo. Ho emesso i seguenti comandi:
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 });
Ho poi importato gli stessi 10.000.000 righe da MySQL, che mi ha dato i documenti che assomigliano a questo:
{
"_id" : ObjectId("4cb8fc285582125055295600"),
"username" : "Joe",
"day" : "Fri May 21 2010 00:00:00 GMT-0400 (EDT)",
"hits" : 16
}
Ora arriva la vera carne e patate qui ... La mia mappa e ridurre le funzioni. Tornato sul server M nella shell, ho impostato la query e l'ho eseguita in questo modo.
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 }}
}
);
Ed ecco che ho incontrato problemi. Questa richiesta ha richiesto oltre 15 minuti per essere completata! La query MySQL è durata meno di un minuto. Ecco l'output:
{
"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 solo c'è voluto per sempre a correre, ma i risultati non hanno nemmeno sembrano essere corrette.
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 }
So che quei numeri di valore dovrebbero essere molto più alti.
La mia comprensione dell'intero paradigma MapReduce è il compito di eseguire questa query deve essere diviso tra tutti i membri del frammento, che dovrebbe aumentare le prestazioni. Ho aspettato che Mongo avesse finito di distribuire i documenti tra i due server shard dopo l'importazione. Ognuno aveva quasi esattamente 5.000.000 di documenti quando ho iniziato questa query.
Quindi devo fare qualcosa di sbagliato. Qualcuno può darmi qualche suggerimento?
Modifica: qualcuno su IRC ha menzionato l'aggiunta di un indice nel campo del giorno, ma per quanto posso dire è stato fatto automaticamente da MongoDB.
Gah .. Ho appena realizzato un motivo per cui i risultati non sono corretti. Avrei dovuto scegliere "valore" piuttosto che "colpi". – mellowsoon
Un problema è che quando si importano i dati in Mongo, il valore "giorno" è una stringa gigante, ma in mysql è una data (numero intero).Quando metti i tuoi dati in mongo, assicurati di memorizzarli come tipo di data. – Clint
potresti anche separare il campo data e ora e memorizzare la data come stringa "20110101" o intero 20110101 e indice in base alla data –