说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

MongoDB Map Reduce速度提升20倍的优化宝典

HTML文档下载 WORD文档下载 PDF文档下载
MongoDB提供的Map Reduce非常灵活,对于大规模数据分析也相当实用。尽管MongoDB 2.4中Map Reduce有了大幅改进,但是相对来说其性能还是有很大的提升空间。本文就来尝试找出让Map Reduce速度最大化提升的方法。

自从MongoDB被越来越多的大型关键项目采用后,数据分析也成为了越来越重要的话题。人们似乎已经厌倦了使用不同的软件来进行分析(这都利用到了Hadoop),因为这些方法往往需要大规模的数据传输,而这些成本相当昂贵。

MongoDB提供了2种方式来对数据进行分析:Map Reduce(以下简称MR)和聚合框架(Aggregation Framework)。MR非常灵活且易于使用,它可以很好地与分片(sharding)结合使用,并允许大规模输出。尽管在MongoDB v2.4版本中,由于JavaScript引擎从Spider切换到了V8,使得MR的性能有了大幅改进,但是与Agg Framework(使用C++)相比,MR的速度还是显得比较慢。本文就来看看,有哪些方法可以让MR的速度有所提升。

测试

首先我们来做个测试,插入1000万文档,这些文档中包含了介于0和100万之间的单一整数值,这意味着,平均每10个文档具有相同的值。

> for (var i = 0; i < 10000000; ++i){ db.uniques.insert({ dim0: Math.floor(Math.random()*1000000) });}> db.uniques.findOne(){ "_id" : ObjectId("51d3c386acd412e22c188dec"), "dim0" : 570859 }> db.uniques.ensureIndex({dim0: 1})> db.uniques.stats(){        "ns" : "test.uniques",        "count" : 10000000,        "size" : 360000052,        "avgObjSize" : 36.0000052,        "storageSize" : 582864896,        "numExtents" : 18,        "nindexes" : 2,        "lastExtentSize" : 153874432,        "paddingFactor" : 1,        "systemFlags" : 1,        "userFlags" : 0,        "totalIndexSize" : 576040080,        "indexSizes" : {                "_id_" : 324456384,                "dim0_1" : 251583696        },        "ok" : 1}

这里我们想要得到文档中唯一值的计数,可以通过下面的MR任务来轻松完成:

> db.runCommand({ mapreduce: "uniques", map: function () { emit(this.dim0, 1); }, reduce: function (key, values) { return Array.sum(values); }, out: "mrout" }){        "result" : "mrout",        "timeMillis" : 1161960,        "counts" : {                "input" : 10000000,                "emit" : 10000000,                "reduce" : 1059138,                "output" : 999961        },        "ok" : 1}

正如你看到的,输出结果大约需要1200秒(在EC2 M3实例上测试),共输出了1千万maps、100万reduces、999961个文档。结果类似于:

> db.mrout.find(){ "_id" : 1, "value" : 10 }{ "_id" : 2, "value" : 5 }{ "_id" : 3, "value" : 6 }{ "_id" : 4, "value" : 10 }{ "_id" : 5, "value" : 9 }{ "_id" : 6, "value" : 12 }{ "_id" : 7, "value" : 5 }{ "_id" : 8, "value" : 16 }{ "_id" : 9, "value" : 10 }{ "_id" : 10, "value" : 13 }...

下面就来看看如何进行优化。

使用排序

我在之前的这篇文章中简要说明了使用排序对于MR的好处,这是一个鲜为人知的特性。在这种情况下,如果处理未排序的输入,意味着MR引擎将得到随机排序的值,

基本上没有机会在RAM中进行reduce,相反,它将不得不通过一个临时collection来将数据写回磁盘,然后按顺序读取并进行reduce。

下面来看看如果使用排序,会有什么帮助:

> db.runCommand({ mapreduce: "uniques", map: function () { emit(this.dim0, 1); }, reduce: function (key, values) { return Array.sum(values); }, out: "mrout", sort: {dim0: 1} }){        "result" : "mrout",        "timeMillis" : 192589,        "counts" : {                "input" : 10000000,                "emit" : 10000000,                "reduce" : 1000372,                "output" : 999961        },        "ok" : 1}

现在时间降到了192秒,速度提升了6倍。其实reduces的数量是差不多的,但是它们在被写入磁盘之前已经在RAM中完成了。

使用多线程

在MongoDB中,一个单一的MR任务并不能使用多线程——只有在多个任务中才能使用多线程。但是目前的多核CPU非常有利于在单一服务器上进行并行化工作,就像Hadoop。我们需要做的是,将输入数据分割成若干块,并为每个块分配一个MR任务。splitVector命令可以帮助你非常迅速地找到分割点,如果你有更简单的分割方法更好。

> db.runCommand({splitVector: "test.uniques", keyPattern: {dim0: 1}, maxChunkSizeBytes: 32000000}){    "timeMillis" : 6006,	"splitKeys" : [		{			"dim0" : 18171		},		{			"dim0" : 36378		},		{			"dim0" : 54528		},		{			"dim0" : 72717		},…		{			"dim0" : 963598		},		{			"dim0" : 981805		}	],	"ok" : 1}

从1千万文档中找出分割点,使用splitVector命令只需要大约5秒,这已经相当快了。所以,下面我们需要做的是找到一种方式来创建多个MR任务。从应用服务器方面来说,使用多线程和$gt / $lt查询命令会非常方便。从shell方面来说,可以使用ScopedThread对象,它的工作原理如下:

> var t = new ScopedThread(mapred, 963598, 981805)> t.start()> t.join()

现在我们可以放入一些JS代码,这些代码可以产生4个线程,下面来等待结果显示:

> var res = db.runCommand({splitVector: "test.uniques", keyPattern: {dim0: 1}, maxChunkSizeBytes: 32 *1024 * 1024 })> var keys = res.splitKeys> keys.length39> var mapred = function(min, max) { return db.runCommand({ mapreduce: "uniques", map: function () { emit(this.dim0, 1); }, reduce: function (key, values) { return Array.sum(values); }, out: "mrout" + min, sort: {dim0: 1}, query: { dim0: { $gte: min, $lt: max } } }) }> var numThreads = 4> var inc = Math.floor(keys.length / numThreads) + 1> threads = []; for (var i = 0; i < numThreads; ++i) { var min = (i == 0) ? 0 : keys[i * inc].dim0; var max = (i * inc + inc >= keys.length) ? MaxKey : keys[i * inc + inc].dim0 ; print("min:" + min + " max:" + max); var t = new ScopedThread(mapred, min, max); threads.push(t); t.start() }min:0 max:274736min:274736 max:524997min:524997 max:775025min:775025 max:{ "$maxKey" : 1 }connecting to: testconnecting to: testconnecting to: testconnecting to: test> for (var i in threads) { var t = threads[i]; t.join(); printjson(t.returnData()); }{         "result" : "mrout0",        "timeMillis" : 205790,        "counts" : {                "input" : 2750002,                "emit" : 2750002,                "reduce" : 274828,                "output" : 274723        },        "ok" : 1}{         "result" : "mrout274736",        "timeMillis" : 189868,        "counts" : {                "input" : 2500013,                "emit" : 2500013,                "reduce" : 250364,                "output" : 250255        },        "ok" : 1} {        "result" : "mrout524997",        "timeMillis" : 191449,        "counts" : {                "input" : 2500014,                "emit" : 2500014,                "reduce" : 250120,                "output" : 250019        },        "ok" : 1}{        "result" : "mrout775025",        "timeMillis" : 184945,        "counts" : {                "input" : 2249971,                "emit" : 2249971,                "reduce" : 225057,                "output" : 224964        },        "ok" : 1}

第1个线程所做的工作比其他的要多一点,但时间仍达到了190秒,这意味着多线程并没有比单线程快!

使用多个数据库

这里的问题是,线程之间存在太多锁争用。当锁时,MR不是非常无私(每1000次读取会进行yield)。由于MR任务做了大量写操作,线程之间结束时会等待彼此。由于MongoDB的每个数据库都有独立的锁,那么让我们来尝试为每个线程使用不同的输出数据库:

> var mapred = function(min, max) { return db.runCommand({ mapreduce: "uniques", map: function () { emit(this.dim0, 1); }, reduce: function (key, values) { return Array.sum(values); }, out: { replace: "mrout" + min, db: "mrdb" + min }, sort: {dim0: 1}, query: { dim0: { $gte: min, $lt: max } } }) }> threads = []; for (var i = 0; i < numThreads; ++i) { var min = (i == 0) ? 0 : keys[i * inc].dim0; var max = (i * inc + inc >= keys.length) ? MaxKey : keys[i * inc + inc].dim0 ; print("min:" + min + " max:" + max); var t = new ScopedThread(mapred, min, max); threads.push(t); t.start() }min:0 max:274736min:274736 max:524997min:524997 max:775025min:775025 max:{ "$maxKey" : 1 }connecting to: testconnecting to: testconnecting to: testconnecting to: test> for (var i in threads) { var t = threads[i]; t.join(); printjson(t.returnData()); }...{         "result" : {                "db" : "mrdb274736",                "collection" : "mrout274736"        },        "timeMillis" : 105821,        "counts" : {                "input" : 2500013,                "emit" : 2500013,                "reduce" : 250364,                "output" : 250255        },        "ok" : 1}...

所需时间减少到了100秒,这意味着与一个单独的线程相比,速度约提高2倍。尽管不如预期,但已经很不错了。在这里,我使用了4个核心,只提升了2倍,如果使用8核CPU,大约会提升4倍。

使用纯JavaScript模式

在线程之间分割输入数据时,有一些非常有趣的东西:每个线程只拥有约25万主键来输出,而不是100万。这意味着我们可以使用“纯JS模式”——通过jsMode:true来启用。开启后,MongoDB不会在JS和BSON之间反复转换,相反,它会从内部的一个50万主键的JS字典来reduces所有对象。下面来看看该操作是否对速度提升有帮助。

> var mapred = function(min, max) { return db.runCommand({ mapreduce: "uniques", map: function () { emit(this.dim0, 1); }, reduce: function (key, values) { return Array.sum(values); }, out: { replace: "mrout" + min, db: "mrdb" + min }, sort: {dim0: 1}, query: { dim0: { $gte: min, $lt: max } }, jsMode: true }) }> threads = []; for (var i = 0; i < numThreads; ++i) { var min = (i == 0) ? 0 : keys[i * inc].dim0; var max = (i * inc + inc >= keys.length) ? MaxKey : keys[i * inc + inc].dim0 ; print("min:" + min + " max:" + max); var t = new ScopedThread(mapred, min, max); threads.push(t); t.start() }min:0 max:274736min:274736 max:524997min:524997 max:775025min:775025 max:{ "$maxKey" : 1 }connecting to: testconnecting to: testconnecting to: testconnecting to: test> for (var i in threads) { var t = threads[i]; t.join(); printjson(t.returnData()); }...{         "result" : {                "db" : "mrdb274736",                "collection" : "mrout274736"        },        "timeMillis" : 70507,        "counts" : {                "input" : 2500013,                "emit" : 2500013,                "reduce" : 250156,                "output" : 250255        },        "ok" : 1}...

现在时间降低到70秒。看来jsMode确实有帮助,尤其是当对象有很多字段时。该示例中是一个单一的数字字段,不过仍然提升了30%。

MongoDB v2.6版本中的改进

在MongoDB v2.6版本的开发中,移除了一段关于在JS函数调用时的一个可选“args”参数的代码。该参数是不标准的,也不建议使用,它由于历史原因遗留了下来(见SERVER-4654)。让我们从Git库中pull最新的MongoDB并编译,然后再次运行测试用例:

...{         "result" : {                "db" : "mrdb274736",                "collection" : "mrout274736"        },        "timeMillis" : 62785,        "counts" : {                "input" : 2500013,                "emit" : 2500013,                "reduce" : 250156,                "output" : 250255        },        "ok" : 1}...

从结果来看,时间降低到了60秒,速度大约提升了10-15%。同时,这种更改也改善了JS引擎的整体堆消耗量。

结论

回头来看,对于同样的MR任务,与最开始时的1200秒相比,速度已经提升了20倍。这种优化应该适用于大多数情况,即使一些技巧效果不那么理想(比如使用多个输出dbs /集合)。但是这些技巧可以帮助人们来提升MR任务的速度,未来这些特性也许会更加易用——比如,这个ticket 将会使splitVector命令更加可用,这个ticket将会改进同一数据库中的多个MR任务。

英文原文:How to speed up MongoDB Map Reduce by 20x

Delphi3的DBGrid中的下拉列表和查找字段编程方法 DELPHI常见问题 Delphi的竖排标签 DELPHI构件制作方法 DELPHI控件Tweblabel的编制 DELPHI中MEMO组件的光标定位 Delphi中RichEdit的奥妙 Delphi中TApplication类 Delphi中日期时间输入的简洁方法 delphi中怎么调用interbase数据库? Memo的Undo功能-Delphi资料 RECT在Delphi中的灵活使用 StringGrid制作只读列-Delphi资料 TADOQuery下主明细表其属性关系如何设置,如何保存主表和明细表?-Delphi资料 TBatchMove用法-Delphi资料 TMemo的真正插入字符-Delphi资料 TREEVIEW的使用-Delphi资料 TreeView在电信综合统计管理系统中的应用-Delphi资料 VB6.0动态加载ActiveX控件漫谈-Delphi资料 捕捉来自 Thread 的异常-Delphi资料 定制Speedbar-Delphi资料 动态产生构件并相应事件-Delphi资料 对Borland可视部件的一处改进-Delphi资料 控件使用技巧-Delphi资料 改变VCL的行为--一个使用可视化元件的实例-Delphi资料 具有不同字体的列表框-Delphi资料 快速大量地更改控件内容-Delphi资料 快速动态创建MenuItem-Delphi资料 利用Delphi 5中调用Excel 97 利用Delphi的Tbitmap控件抓取屏幕图象 利用delphi美化你的菜单 修改数据出错,不知为何? 等级考试的问题 请问能不能在不同用户登陆的情况来决定服务A是否启动。 关于还原精灵转储后win98不能启动的问题,请指教!~ 今天,我终于听到项目经理的一句内心话,让我们作为程序员感到一阵阵的寒心 请教一下 dll调试问题 怎样得到OnLButtonDown的point在整个屏幕上的坐标啊?谢谢 递归调用FindFirstFile、FindNextFile、FindClose的问题 页面调度有关的课程设计 关于client端与server端的连接问题 请问能不能在不同用户登陆的情况来决定服务A是否启动。 Textbox如何禁止它记忆以前曾经输入过的值? 我的XP的CPU经常占用90%以上,高手指点. 安装完win2000,又安装了天网防火墙,再安装SQL server 2000,快完成时启动服务警告“无法定位程序输入点” 急招深圳周边(含深圳).Net程序员 ??qustion Laney(6吨大肥猫) 快来啊^@^ 有关struts的<html:text>标签的问题!!!!!!!!!!!!!!!!!!!!!!! 为了考试挑灯夜读,遇到一个菜问题请大家帮忙! 哪位高手帮帮我!时间紧急! 关于还原精灵转储后win98不能启动的问题,请指教!~ 有几张漂亮图片,看看你的计算机是怎么累死的 求C++算法代码。 怎么实现在IE中将查找到的关键字用红色标记指示出来? 求C++算法代码。 请问哪里可以下载Numega DevPartenr Studio 测试工具 ㊣㊣ 什么也不多说了,帮忙给解决这个问题吧, 先多谢了!! ㊣㊣ 有个小小问题 寻找监测某端口的数据流量软件 都市幽灵·里面一定有宝藏 非常奇怪:为什么超链接无效? 显示异常 下个星期开始到新公司了,请大家给点经验,好吗 急!jsp+mysql,急寻blob输出方法!!! 修改记录问题 StringGrid的自画 导出 救命呀,关于XP的激活问题. 语句 请问有什么好的串口调试工具,可以模拟串口发送数据的??????? 简单问题高分相送 MessageBox.Show 方法需要什么样的名空间 使用ghost硬盘刻录硬盘怎么区分源盘和目标盘,请大虾详细说说,在线等 概念性问题(没办法,太笨了,见笑了) vf8支持动态SQL吗? 请问:如何单独安装BDE? 因为你无权连接数据库服务器?? 大家看看一个非常专业的商贸企业管理软件,要源码好商量! 如何清除Image中的图片? 数据库查询一个奇怪问题 少昊、颛顼、帝喾、.尧、舜他们还有官名吗 感觉很简单,但是我就是不知如何下手 现实是此岸,理想是彼岸,中间隔着湍急的河流,行动则是架在河上的桥梁.这句话说明?1理想是对现实的超越2崇高的理想信念必须落实在行动上3追求和实现理想是一个艰苦奋斗的过程4 要正确认 为什么希腊是现代科学精神的起源? 与象共舞 大象跳舞给人的感觉是什么 "颛顼、帝喾"这两人是什么人啊? 黄帝、颛顼、帝喾、尧、舜庙宇在什么地方 大学对于我们的意义,请谈谈你的看法吧! 圆的英文怎么写啊“圆的”这个英文单词怎么拼啊?我要准确的! The most chershed Thing in life is friendship The most precious thing Between us怎么翻译没什么难度吧! 对大学社团的看法 二李复习全书第524页例2.21,为什么{X≤x}的概率是两个面积之比分母为半圆面积,分子面积是三角形BOA和扇形ABC的面积之和, Sincere forever,the friendship forevtr,the dearones forever,love forever ls 明珠散落的意思解释对了就好了~ 考研:我已经把数学书看了一遍,不过没做课后题,那我还需要看一遍李永乐复习全书吗?那660题呢?什么时候看啊? 小明先向东走了5米,又向西走了3米,一共向东走了多少米?如果规定向东的方向为正方向 对大学教学的看法?还有理想的大学教学是怎样的?大学教与学的灵魂? 五帝的颛顼的读音是什么呀? 电视上哪个频道讲有关天文的知识 6题..文科学霸请进……为什么c不对 颛顼 怎么读?要拼音 关于天文的知识不少于8条字数不少于260字每条不一样 大概的相近词是什么 颛顼怎么读?拼音 问下问数学题目说出来吧,我在此先谢谢了1e 东阳.为什么叫东阳 为人民服务的人的事迹 一人向东走20米,又向西走了30米,实际走了多少米?他此时的位置应在哪里? 浙江的东阳市有叫东阳商场的吗?想去那里进货. 成语接龙越多越好 “三皇五帝”中为何把炎帝划分在“三皇”里,而黄帝划分在“五帝”里在中国通史远古时期传说中,黄帝和炎帝同为有典氏的儿子,都是很杰出很有名望的部落酋长,为什么“三皇五帝”里把黄 一位同学在一条由东向西的跑道上先向东走了20米又向西走了30米能否确定他现在的位置与原来位置相距多少米 晶莹剔透、水生喧腾,明珠散落的意思 三皇的“天皇”、“地皇”、“泰皇”,和“伏羲”、“神农”、“黄帝”.是一样的么?如题 一位同学在一条由东向西的跑道上,先向东走了20米又向西走了30米能否确定他现在的位置与原来位置相距多少米 耳朵用英语怎么写耳朵,眼睛,鼻子,手,嘴巴,头,脸怎么写 三皇中的“黄帝”与五帝中的“黄帝”是否一样? 某人向东走5米,又向西走5米,那么此人实际走了多少米?他的位置是在?请帮助帮助回答,谢谢!这道题我不知道该怎么写,请来帮助我把! 珍珠泉似明珠散落 ,—————— ( 对对子 )急 某天文观察者观看到下列现象,依据天体的相对位置判断,不可信的是.白天看到日食,同一天晚上看到月食为啥不对,日落后既看到火星又看到木星为啥对?跟剧天体的相对位置判断 某人向东走了20米,又向西走了30米,实际走了多少米?这是一个关于有理数的数学问题主要是要弄清实际说的是路程还是距离 有文科学霸麽 关于天文星象的基础问题:是根据什么判断一个星次里边有二十八宿中的哪几个呢?比如说正月,如果在正月的时候观星,是在黄昏的时候太阳落山的西方出现的星宿属于娵訾吗?是我没有表达清 已知X十y=5⃣️Xy=2⃣️求(x-y)^2.x^4+y^4的值 Lambert1.[男子名] [英格兰人姓氏] 兰伯特.来源于日耳曼语人名,含义是“土地,领土+光明的,著名的”(land,territory+bright,famous) 2.[英格兰人姓氏] 兰伯特.职业名称,牧羊人,来源于古英语,含义是“羔 Taking your measurements won't help when it comes to picking your size 若丨x-3丨+丨y-2丨=0,试求2x+y的值 Lambert1.[男子名] [英格兰人姓氏] 兰伯特.来源于日耳曼语人名,含义是“土地,领土+光明的,著名的”(land,territory+bright,famous) 2.[英格兰人姓氏] 兰伯特.职业名称,牧羊人,来源于古英语,含义是“羔 英语翻译when it comes to 一般在什么时候用的 若丨x丨=3,丨y丨=5,且丨x-y丨=-(x-y).求丨x-3丨+(y-5)²的值 Fedor可以做英文名字用吗?是英文姓还是男子名? 黄帝姓公孙,根据司马迁史记记载,为何其孙,孙之孙颛顼和帝喾都姓高,即高阳和高辛. 已知丨x-2丨+丨y+2丨=0,求 x,y的值要用上初中因为所以的符号,具体点 什么像一颗颗明珠造句 请从语法角度分析.The date marks the end of...the date marks the end of one of the periods of roughly 400 years into which the Mayan calender is divided. Your Personal Day of Death is... Tuesday, April 9, 2075 Seconds left to live...2,120,323,716 Delay Your Date of Death How Migraines Impact Quality of Life Gastroesophageal Reflux Disease: What You Should Know A Lack of Sleep or ADHD? 都是什么 有人说:“现实是此岸,理想是彼岸,中间隔着湍急的河流,行动则是架在河上的桥梁.”这个句子充满了哲理.请你也试着将《在山的那边》全诗的内容浓缩提炼,仿照这个句子写一句能概括全诗 能帮我翻译一下这个句子吗?at the end of an income year ending on the date of death Your Personal Day of Death is... Friday, August 8, 2081什么意思?Delay Your Date of Death The Phases of Migraines What to Expect When Getting an MRI Polycystic Ovary Syndrome: What You Should Know 这都啥意思? 暴雨离檐,犹如明珠散落,一地流水向低而去. 我们与美国不得不重建信任巴西与德国要求联合国通过网络私密性决浙江民营经济隐现离制造业化 地产投资美国白宫变“粉宫” 呼吁关注乳腺癌(美国与沙特进入公开紧张状态 多重分歧四世同堂全家福英媒称斯诺登一机密文件披露美监听35第九届北京—东京论坛26日在北京开幕欧洲多国一致声讨美国监听事件 默克尔希腊疑遭拐卖女童证实身份 其母曾企图国际新闻早报:日本发生7.1级地震马凯会见欧盟委员会主席巴罗佐美媒力赞英国查尔斯王子 称其眼见高过英国王储查尔斯否认说过“当国王像蹲监俄学者:不要对伊核谈判前进步伐抱过高小偷夜闯国务卿克里家车库 美警方调查中欧推动世贸组织在MC9上达成早期收李某某案31号二审开庭 受害人心情未《三国演义》连环画法文版在法国上架马凯会见比利时首相迪吕波沃尔玛3年欲设新址110个塞外建功营改增试点扩围至铁路邮政行业美国希望以最低成本“管理世界”家属申请赔偿159万女孩手机被偷,同事奋不顾身抓小偷原来用车可以更省的!8位热心市民 愿意免费教跳舞把握构建中美新型大国关系正确方向不动珠算“打”成人类非物质文化遗产公安厅长亲敲键盘答网友奥巴马叔叔在美生活50年后拿到绿卡(说好的“共同声明”泡了汤文化眼光(下)美国发行农历马年限量版“吉利钱”多款新车强势出击还找“临时工”?车企高层纵论2014大河股票池(12月4日 星期三)无标题珍惜你的每一分钱,珍惜你的每一分钟“河南100家不可不去的饭店”
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘