热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于MongoDB数据库中mapreduce的研究

声明:本文是学习Mongodb过程中的副产品,因为接触时间并不长,难免有理解上的偏差,希望借此文与感兴趣的朋友讨论切磋,呵呵。去年年底,开始接触并学习Mapreduce模型。因为工作上的关系,最近开始研究Mongodb,其中对其新特性(2010年四月)reduce模型实现产
       声明:本文是学习Mongodb过程中的副产品,因为接触时间并不长,难免有理解上的偏差,希望借此文与感兴趣的朋友讨论切磋,呵呵。
       去年年底,开始接触并学习Mapreduce模型。因为工作上的关系,最近开始研究Mongodb,其中对其新特性(2010年四月)reduce模型实现产生的兴趣,因为特别留意了一下。当然网上关于该方面的内容并不是很多,且多为EN文,所以我想有必要将学习使用过程中的一些问题作一下记录并加以整理,因为就有了此文。
       废话不多说了,开始正文吧!

       目前支持Mongodb的C#客户端应该就是Samuel Corder 开源的这个项目了,链接:http://github.com/samus/mongodb-csharp。
       其中在它的源码包中的MongoDB.Net-Tests目录下有对TestMapReduce和TestMapReduceBuilder相应测试用例,因为我本地没安装NUnit,所以接下来的内容我是在一个新建的web项目中直接Copy其中的部分代码做的测试(注:有关Mapreduce模型的内容请查阅相关资料)。

      首先我们要先加载测试数据,这里我们以DNT中的在线用户列表的(结构)作为依据,批量倒入10条记录,
Mongo db = new Mongo("Servers=10.0.4.66:27017;COnnectTimeout=300000;COnnectionLifetime=300000;MinimumPoolSize=25;MaximumPoolSize=25;Pooled=true");          db.Connect(); 
Database test = db.GetDatabase("test");
IMongoCollection things = test["things"];
for (int i = 1; i <= 10;i++)
          {    
              Document record = new Document();
              record["_id"] = i;               
              record["userid"] = i;   
              record["ip"] = "10.0.7." + i;
              record["username"] = "用户" + i;
              record["nickname"] = "用户" + i;
              record["password"] = "";
              record["groupid"] = i;//下面将就该字段使用MAPREDUCE方式进行分组统计
              record["olimg"] = "";
              record["adminid"] = 0;
              record["invisible"] = 0;
              record["action"] = 0;
              record["lastactivity"] = 1;
              record["lastposttime"] = DateTime.Now.ToString();
              record["lastpostpmtime"] = DateTime.Now.ToString();
              record["lastsearchtime"] = DateTime.Now.ToString();
              record["lastupdatetime"] = "1212313221231231213321";
              record["forumid"] = 0;
              record["forumname"] = "";
              record["titleid"] = 0;
              record["title"] = "";
              record["verifycode"] = "";
              record["newpms"] = 0;
              record["newnotices"] = 0;
              things.Insert(record);             
          } 
      db.Disconnect(); 
        假定目前我们有这样一个需求,就是找出该表中用户组(groupid)字段为5的用户数,当然这里我们不会使用普通的查询方法,而是使用MAPREDUCE方式,其工作过程分为两个阶段:map阶段和reduce阶段。每个阶段都有键/值对作为输入和输出,并且它们的类型可由程序员选择。下面是其实现方式:

首先是map方法:    
 string mapfunction = "function() {  if(this.groupid==5) {emit({groupid : 5}, 1);} }";        

然后是reduce方法:    
 string reducefunction = "function(key, current ){" +
                                "   var count = 0;" +
                                "   for(var i in current) {" +
                                "       count+=current[i];" +
                                "   }" +
                                "   return count;" +
                              "};";          

最后我们使用下面代码实现对上面MAP,REDUCE的相应代码绑定和MapReduce类的声明:    
MapReduce mr = mrcol.MapReduce();
    mr.Map = new Code(mapfunction);
    mr.Reduce = new Code(reducefunction4);
    mr.Execute();
    foreach (Document doc in mr.Documents)
    {
           int groupCount = Convert.ToInt32(doc["value"]);
    }     mr.Dispose();
       运行上面代码,显示结果如下: 
    

      当前上面监视窗口中的"id:"{"groupid":5},即是mapfunction中的定义,当然如果要统计所有用户组(10个用户组)中各自的用户数,只把将mapfunction改写成:
      string mapfunction = "function() { emit(this.groupid, 1); }";
    这样,它就会按当前用户所属的groupid来作为键(确保不重复),凡是同一组的用户就作为输出进行发送(emit),emit可以理解为调用reduce方法,这里参数为1[即累加1操作])。
     目前我在网上打到mongodb示例基本上都是围绕分组统计功能展开的。
     当然就其传参和返回值都可以使用类似元组的方式,记得上面的“emit({groupid : 5}, 1)”代码吗?返回值这里也可以使用下面的方式:  
string reducefunction = "function(key, current ){" +
                                 "   var count = 0;" +
                                 "   for(var i in current) {" +
                                 "       count+=current[i];" +
                                 "   }" +
                                 "   return { groupcount : count };" +  //注意这里的返回方式
                               "};";       

返回类型变了,取值的方式也要发生变成:
int groupCount = int.Parse(((Document)doc["value"])["groupcount"].ToString());       

当然,上面的MapReduce 类的声明使用方式过于拘谨,下面使用链式调用的方式:
using (MapReduceBuilder mrb = mrcol.MapReduceBuilder().Map(mapfunction).Reduce(reducefunction)) 
{
        using (MapReduce mr = mrb.Execute())
        {
                   foreach (Document doc in mr.Documents)
                   {
                       int groupCount = int.Parse(((Document)doc["value"])["groupcount"].ToString());
                   }
        }
}        

返回的结果与之前的一样,呵呵。
       另外,mongodb还支持更加复杂的数据结构,比如官司方给的下面这个数据结构示例:
          mrcol.Insert(new Document().Append("_id", 1).Append("tags", new String[]{"dog", "cat"}));
          mrcol.Insert(new Document().Append("_id", 2).Append("tags", new String[]{"dog"}));
          mrcol.Insert(new Document().Append("_id", 3).Append("tags", new String[]{"mouse", "cat", "dog"}));
          mrcol.Insert(new Document().Append("_id", 4).Append("tags", new String[]{}));        

可以看出tags字段(这里暂且这么说,呵呵),就是一个字符串数组,而下面的mapreduce方法将会统计里面单词dog,cat,mouse的出现次数:
string mapfunction = "function(){\n" +
                           "   this.tags.forEach(\n" +
                           "       function(z){\n" +
                           "           emit( z , { count : 1 } );\n" +
                           "       });\n" +
                           "};";
string reducefunction = "function( key , values ){\n" +
                               "    var total = 0;\n" +
                               "    for ( var i=0; i
                               "        total += values[i].count;\n" +
                               "    return { count : total };\n" +
                               "};";       

对于如何对(含)日期型数据的键进行分组统计,下面的这个链接中有详细说明(统计每天用户的访问量):
       Counting Unique Items with Map-Reduce 
      下面这个链接就是官方给出示例的文档链接页面,其中包括更加复杂的mapreduce示例:
       http://www.mongodb.org/display/DOCS/MapReduce
      当然目前对于Mapreduce模式,Mongodb使用一个单独的进程来跑的,这主要是因为Javascript 引擎的限制。目前开发团队正在设计解决这一问题。原文:
       As of right now, MapReduce jobs on a single mongod process are single threaded. This is due to a design limitation in current Javascript engines. We are looking into alternatives to solve this issue, but for now if you want to parallelize your MapReduce jobs, you will need to either use sharding or do the aggregation client-side in your code.        另外就是到现在对于MONGODB那一端是如果把输入数据划分成等长的小数据发送到MapReduce(Hadoop把这一操作称为input split,即输入切片),因为这一点对于并发运行的作业进行负载平衡很重要,而在 Hadoop中一个理想的切片大小往往是一个HDFS块的大小,默认是64 MB(Hadoop权威指南(中文版))。

       除了上面所提到了,在MONGODB的mapreduce模型中,还支持map输出的临时结果集的持久化,而这一特色还在文档中专门作了如下说明:
Note on Permanent Collections Even when a permanent collection name is specified, a temporary collection name will be used during processing. At map/reduce completion, the temporary collection will be renamed to the permanent name atomically. Thus, one can perform a map/reduce job periodically with the same target collection name without worrying about a temporary state of incomplete data. This is very useful when generating statistical output collections on a regular basis.
     而如果想要持久化该临时集合,只要将mapreduce实例的Keptemp属性设为true,同时使用Out属性(方法)指定输出的集合名称即可。
     当然就目前我测试时结果来看,在单台机器上做这种模型测试就效率上是得不尝失的(执行周期太长),特别是数据量特别大(比如3000w以上),所以应用(或运行)场景的选择很重要。
     上面所说的示例比较简单,都是在单一reduce任务中的执行场景,如下图:
  

     而实际的生产环境要比上图复杂许多,比如多reduce任务情况,在Hadoop中,如果运行多个reduce任务,map任务会对其输出进行分区,为每个reduce任务创建一个分区(partition)。每个分区包含许多键(及其关联的值),但每个键的记录都在同一个分区中。分区可以通过用户定义的partitioner来控制。如下图:
     

     鉴于目前网上mongodb相关文档内容并不多,所以这里暂不多做讨论了。

推荐阅读
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • ch3中可视化软件pangolin的安装步骤及注意事项
    本文介绍了在ch3中安装可视化软件pangolin的步骤及注意事项。首先提供了pangolin的下载地址,并说明了下载后需要放到与虚拟机交互的文件夹地址。然后详细介绍了安装pangolin所需的依赖项,并提供了在终端进行安装的命令。最后给出了解压pangolin的步骤。 ... [详细]
  • Hadoop源码解析1Hadoop工程包架构解析
    1 Hadoop中各工程包依赖简述   Google的核心竞争技术是它的计算平台。Google的大牛们用了下面5篇文章,介绍了它们的计算设施。   GoogleCluster:ht ... [详细]
  • mapreduce源码分析总结
    这篇文章总结的非常到位,故而转之一MapReduce概述MapReduce是一个用于大规模数据处理的分布式计算模型,它最初是由Google工程师设计并实现的ÿ ... [详细]
  • 前言折腾了一段时间hadoop的部署管理,写下此系列博客记录一下。为了避免各位做部署这种重复性的劳动,我已经把部署的步骤写成脚本,各位只需要按着本文把脚本执行完,整个环境基本就部署 ... [详细]
  • 今天我们学习,数据库mongodb的使用,最下面有mongodb的下载链接。pipinstallpymongo首先安装pymongo,然后在需要用到的地方importpymongo ... [详细]
  • MongoDB学习:(二)MongoDB简单使用
    MongoDB学习:(二)MongoDB简单使用MongoDB使用:执行mongodb的操作之前,我们需要运行命令,来进入操作命令界面&amp;amp;gt;mongo提示 ... [详细]
author-avatar
昆明DVD导航
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有