我在关系数据库方面有很长的历史,但我对MongoDB和MapReduce还不熟悉,所以我几乎可以肯定我一定做错了什么.我就直接开始这个问题.抱歉,时间太长了.

我在MySQL中有一个数据库表,用于跟踪每天的成员概要视图数量.对于测试,它有10000000行.

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

我用这个查询获得了自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

此查询在一分钟内完成.不错!

现在进入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 });

然后我从MySQL中导入了相同的10000000行,这给了我如下所示的文档:

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

现在真正的肉和土豆来了...我的map和reduce函数.回到shell中的服务器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 }}
    }
);

以下是我遇到的问题.This query took over 15 minutes to complete! MySQL查询耗时不到一分钟.以下是输出:

{
        "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完成导入后在两个碎片服务器之间分发文档.当我开始这个查询时,每个人都有将近5000000个文档.

所以我一定是做错了什么.有人能给我一些建议吗?

编辑:IRC上有人提到在day字段中添加索引,但据我所知,这是MongoDB自动完成的.

推荐答案

摘自O'Reilly的MongoDB权威指南:

使用MapReduce的代价是速度:

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. 

Mongodb相关问答推荐

MongoDB:用特殊字符创建的drop collection

如何在MongoDB中查找和过滤嵌套数组

如何在MongoDB中通过限制和跳过查找项进行匹配

当日期和时间在不同键的字符串中时,Mongo 查询过滤今天的数据

DTO 验证适用于 POST,但不适用于 PUT

MongoDB 聚合使用 $match 和 $expr 和数组

MongoDB 到 Snowflake 连续加载

更新 Mongodb 中的多嵌套数组

Mongoose 架构引用和未定义类型ObjectID

更新 mongoengine 中的嵌入文档列表

GeoJSON 和 MongoDB:将点存储为 GeoJSON.Point 是否值得?

.NET 4 中是否有 mongodb C# 驱动程序支持 System.Dynamic.DynamicObject?

Springboot 使用 find*() 查询时出现 Mongodb 错误

如何解决 ClassNotFoundException:com.mongodb.connection.BufferProvider?

使用 MongoDB 进行嵌套分组

是否有支持 MongoDB 和 Devise 的 Rails 管理界面?

如何将转储文件夹导入 mongodb 数据库?

Mongodb 按字段名称查找任何值

MongoDB 的 MMAPV1、WiredTiger 或 In-Memory StorageEngine 如何 Select ?

Mongodb同时在多个字段上聚合(计数)