Mongodb Mapreduce 初窥

本文介绍Mongodb中MapReduce的使用方法,包括通过MapReduce进行分组统计的具体实现细节,以及不同数据类型的处理方式。
摘要:作者声明:本文是学习Mongodb过程中的副产品,因为接触时间并不长,难免有理解上的偏差,希望借此文与感兴趣的朋友讨论切磋,呵呵。 去年年底,开始接触并学习Mapreduce模型。因为工作上的

 作者声明:本文是学习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条记录,代码如下:

      
  1. Mongo db = new Mongo("Servers=10.0.4.66:27017;ConnectTimeout=300000;ConnectionLifetime=300000;MinimumPoolSize=25;MaximumPoolSize=25;Pooled=true");           
  2.  
  3. db.Connect();   
  4.  
  5. Database test = db.GetDatabase("test");  
  6. IMongoCollection things = test["things"];  
  7.  
  8. for (int i = 1; i <= 10;i++)  
  9.           {      
  10.               Document record = new Document();  
  11.               record["_id"] = i;                 
  12.               record["userid"] = i;     
  13.               record["ip"] = "10.0.7." + i;  
  14.               record["username"] = "用户" + i;  
  15.               record["nickname"] = "用户" + i;  
  16.               record["password"] = "";  
  17.               record["groupid"] = i;//下面将就该字段使用MAPREDUCE方式进行分组统计  
  18.               record["olimg"] = "";  
  19.               record["adminid"] = 0;  
  20.               record["invisible"] = 0;  
  21.               record["action"] = 0;  
  22.               record["lastactivity"] = 1;  
  23.               record["lastposttime"] = DateTime.Now.ToString();  
  24.               record["lastpostpmtime"] = DateTime.Now.ToString();  
  25.               record["lastsearchtime"] = DateTime.Now.ToString();  
  26.               record["lastupdatetime"] = "1212313221231231213321";  
  27.               record["forumid"] = 0;  
  28.               record["forumname"] = "";  
  29.               record["titleid"] = 0;  
  30.               record["title"] = "";  
  31.               record["verifycode"] = "";  
  32.               record["newpms"] = 0;  
  33.               record["newnotices"] = 0;  
  34.               things.Insert(record);               
  35.  
  36.           }   
  37.  
  38.       db.Disconnect();  

假定目前我们有这样一个需求,就是找出该表中用户组(groupid)字段为5的用户数,当然这里我们不会使用普通的查询方法,而是使用MAPREDUCE方式,其工作过程分为两个阶段:map阶段和reduce阶段。每个阶段都有键/值对作为输入和输出,并且它们的类型可由程序员选择。下面是其实现方式:

首先是map方法:

      
  1. string mapfunction = "function() {  if(this.groupid==5) {emit({groupid : 5}, 1);} }"

然后是reduce方法:

      
  1. string reducefunction = "function(key, current ){" +  
  2.                                  "   var count = 0;" +  
  3.                                  "   for(var i in current) {" +  
  4.                                  "       count+=current[i];" +  
  5.                                  "   }" +  
  6.                                  "   return { groupcount : count };" +  //注意这里的返回方式  
  7.                                "};";  

最后我们使用下面代码实现对上面MAP,REDUCE的相应代码绑定和MapReduce类的声明:

      
  1. MapReduce mr = mrcol.MapReduce();  
  2.     mr.Map = new Code(mapfunction);  
  3.     mr.Reduce = new Code(reducefunction4);  
  4.     mr.Execute();  
  5.     foreach (Document doc in mr.Documents)  
  6.     {  
  7.            int groupCount = Convert.ToInt32(doc["value"]);  
  8.     }  
  9.  
  10.     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)”代码吗?返回值这里也可以使用下面的方式:

      
  1. string reducefunction = "function(key, current ){" +  
  2.                                  "   var count = 0;" +  
  3.                                  "   for(var i in current) {" +  
  4.                                  "       count+=current[i];" +  
  5.                                  "   }" +  
  6.                                  "   return { groupcount : count };" +  //注意这里的返回方式  
  7.                                "};";  

返回类型变了,取值的方式也要发生变成:

      
  1. int groupCount = int.Parse(((Document)doc["value"])["groupcount"].ToString());  

当然,上面的MapReduce 类的声明使用方式过于拘谨,下面使用链式调用的方式:

      
  1. using (MapReduceBuilder mrb = mrcol.MapReduceBuilder().Map(mapfunction).Reduce(reducefunction))   
  2. {  
  3.         using (MapReduce mr = mrb.Execute())  
  4.         {  
  5.                    foreach (Document doc in mr.Documents)  
  6.                    {  
  7.                        int groupCount = int.Parse(((Document)doc["value"])["groupcount"].ToString());  
  8.                    }  
  9.         }  

返回的结果与之前的一样,呵呵。

另外,mongodb还支持更加复杂的数据结构,比如官司方给的下面这个数据结构示例:

      
  1. mrcol.Insert(new Document().Append("_id", 1).Append("tags", new String[]{"dog", "cat"}));  
  2. mrcol.Insert(new Document().Append("_id", 2).Append("tags", new String[]{"dog"}));  
  3. mrcol.Insert(new Document().Append("_id", 3).Append("tags", new String[]{"mouse", "cat", "dog"}));  
  4. mrcol.Insert(new Document().Append("_id", 4).Append("tags", new String[]{}));   

可以看出tags字段(这里暂且这么说,呵呵),就是一个字符串数组,而下面的mapreduce方法将会统计里面单词dog,cat,mouse的出现次数:

      
  1. string mapfunction = "function(){\n" +  
  2.                            "   this.tags.forEach(\n" +  
  3.                            "       function(z){\n" +  
  4.                            "           emit( z , { count : 1 } );\n" +  
  5.                            "       });\n" +  
  6.                            "};";  
  7. string reducefunction = "function( key , values ){\n" +  
  8.                                "    var total = 0;\n" +  
  9.                                "    for ( var i=0; i<values.length; i++ )\n" +  
  10.                                "        total += values[i].count;\n" +  
  11.                                "    return { count : total };\n" +  
  12.                                "};";  

对于如何对(含)日期型数据的键进行分组统计,下面的这个链接中有详细说明(统计每天用户的访问量):

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任务中的执行场景,如下图:

singlereduce

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

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

相关链接:

http://www.10gen.com/event_mongosf_10apr30#cool

http://github.com/mongodb/mongo/tree/master/jstests/mr1.js

http://github.com/mongodb/mongo/tree/master/jstests/mr2.js

http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/

http://www.snailinaturtleneck.com/blog/2010/03/15/mapreduce-the-fanfiction/

http://www.mongodb.org/display/DOCS/Aggregation

原文链接:http://www.cnblogs.com/daizhj/archive/2010/06/10/1755761.html

【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)内容概要:本文介绍了基于IEEE33节点的配电网韧性提升方法,重点研究了移动储能系统的预布局与动态调度策略。通过Matlab代码实现,提出了一种结合预配置和动态调度的两阶段优化模型,旨在应对电网故障或极端事件时快速恢复供电能力。文中采用了多种智能优化算法(如PSO、MPSO、TACPSO、SOA、GA等)进行对比分析,验证所提策略的有效性和优越性。研究不仅关注移动储能单元的始部署位置,还深入探讨其在故障发生后的动态路径规划与电力支援过程,从而全面提升配电网的韧性水平。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事智能电网、能源系统优化等相关领域的工程技术人员。; 使用场景及目标:①用于科研复现,特别是IEEE顶刊或SCI一区论文中关于配电网韧性、应急电源调度的研究;②支撑电力系统在灾害或故障条件下的恢复力优化设计,提升实际电网应对突发事件的能力;③为移动储能系统在智能配电网中的应用提供理论依据和技术支持。; 阅读建议:建议读者结合提供的Matlab代码逐模块分析,重点关注目标函数建模、约束条件设置以及智能算法的实现细节。同时推荐参考文中提及的MPS预配置与动态调度上下两部分,系统掌握完整的技术路线,并可通过替换不同算法或测试系统进一步拓展研究。
先看效果: https://pan.quark.cn/s/3756295eddc9 在C#软件开发过程中,DateTimePicker组件被视为一种常见且关键的构成部分,它为用户提供了图形化的途径来选取日期与时间。 此类控件多应用于需要用户输入日期或时间数据的场景,例如日程管理、订单管理或时间记录等情境。 针对这一主题,我们将细致研究DateTimePicker的操作方法、具备的功能以及相关的C#编程理念。 DateTimePicker控件是由.NET Framework所支持的一种界面组件,适用于在Windows Forms应用程序中部署。 在构建阶段,程序员能够通过调整属性来设定其视觉形态及运作模式,诸如设定日期的显示格式、是否展现时间选项、预设的始值等。 在执行阶段,用户能够通过点击日历图标的下拉列表来选定日期,或是在文本区域直接键入日期信息,随后按下Tab键或回车键以确认所选定的内容。 在C#语言中,DateTime结构是处理日期与时间数据的核心,而DateTimePicker控件的值则表现为DateTime类型的实例。 用户能够借助`Value`属性来读取或设定用户所选择的日期与时间。 例如,以下代码片段展示了如何为DateTimePicker设定始的日期值:```csharpDateTimePicker dateTimePicker = new DateTimePicker();dateTimePicker.Value = DateTime.Now;```再者,DateTimePicker控件还内置了事件响应机制,比如`ValueChanged`事件,当用户修改日期或时间时会自动激活。 开发者可以注册该事件以执行特定的功能,例如进行输入验证或更新关联的数据:``...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值