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

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

上海云人联合创始人吴朱华:中国人也能做出最好的实时数据库产品 Facebook RSS替代Google Reader?或因其社交属性失败 让这么多国外开发者如此兴奋,iOS 7到底好在哪里? Web开发中那些不招人“待见”的功能 最好的超级计算机在中国 为开发者提供的10款Web应用程序 5000万次下载:HandyGames如何征服Google Play? 红帽大战Ubuntu:750小时免费企业版红帽套餐即将登陆AWS EMC Sanjay Mirchandani:92%虚拟化,使更少IT人拥有更大交付能力 从NSA携谷歌、微软等9家名企监控用户行为看数据安全 【专访间】新科兰德创始人左磊:我们的“卖点”是“数据弹药” 甲骨文公司宣布推出最新Java EE 7 10款最佳CSS/jQuery开源图片说明 创业者应避免对投资者说的五句话 直接拿来用!最火的iOS开源项目(二) 西部数据Pat Wilkison:云计算市场是创新热点 猎豹浏览器发布手机版:PM谈开发理念 成为伟大开发者的“九步曲” 使用GPU构建更便宜的Google神经网络 Facebook与Yahoo!的“火花”:系统间实时数据流管理工具 视金钱如粪土?Yahoo!又要买了,梅姐的算盘是这样打的 MySQL手册不再遵循GPL协议:MySQL走向闭源前兆? 扁平化设计会扼杀人们的创造力吗? 又一巨头涉足移动视频分享领域:传雅虎欲收购短视频应用Qwiki 史上最牛:主要靠三消游戏IPO King也许会是第一家 乐视超级电视预约启动 1小时预约近1.4万台 王坚:未来一年阿里云会有很大变化 梁胜:开源并不是做好软件的唯一方式 如何为IE 10编写特定的样式 MySQL手册许可证更改乃是Bug惹得祸 开源世界高峰论坛首批演讲议题公布 活见鬼,真气人....... 问一个trigger问题? 问一下:我的char*都用_T("asdfsafd")表示了,资源也没任何问题,可有时候运行程序显示乱码 在论坛里,发一个帖子新开一个窗口,当发完后让原来那个窗口刷新一下 我的机子到底怎么回事???? 我试试.. CListCtrl中如何选定整个一行? 来到济南天气太热,特来送分 ★★★★★ 见于现在伪ID和盗用密码的现象十分嚣张,我提议…… ★★★★★ 请教各位,我用vb6.0访问ms sql7.0数据库,为什么有时出现,“过期,数据已超时?” 请问COM和DLL有什么区别和联系?(30分) 关于侯老师 win95-a-developers-guide 一书的源配书... ISAPI中不能存取ASP中的Session、Application变量?高手你老人家在哪里? 大家来看看这些招聘题目合不合适 提一个网页制作的问题哈,大家不要扔西红市啊,网叶制作可是程序员的基本技能之一 activeskin里的skinbuilder有谁用过???来讨论吧 我在msdn上看到vb中的字符串有两种:定长字符串和变长字符串。请问如何分别声明这两种类型? 怎样在ADO中引用已打开的recordset的某一字段值,请给几个例,谢谢了! seedundersnow and holyfire help me!about 8253! 谁有cute ftp 4.2.6 beta的注册码 请问如何把页面查询到的记录导入到Excel中呢? 有用过TAdvStringGrid的吗?OnGetEditorType( )为何不被调用? 请教高手如何用Xsl编制函数控制Xml??急急 哪位给一个MD5的例子!谢谢了 长春的李延生来领分 求救:一个VC中多线程的问题 提个问题,原来对VC尤其是界面和C++语法这一块比较熟,现在由于公司需要转C++BUILDER,难不难,需要熟悉哪些 [总300分,会继续加分]哪位大哥用编程实现过创建SQL Server的DTS package并且run the package的全过程[急] 能不能使用html 压缩中提到的文件头有什么作用 , 保含哪些信息 菜鸟问题1 提一个网页制作的问题哈,大家不要扔西红市啊,网叶制作可是程序员的基本技能之一。。。。。。。 关于:import java.util.StringTokenizer; 问题! 如何实现在某特定时间(如每月1号,或7天后)执行特定asp程序? 在开发工具中如:(vc,jsp)中如何得到oracle中自定义函数的返回值 当form被移动的时候会相应什么事件的? 关于使用mscomm32.ocx中的一些问题 类似股票信息显示系统的问题(图形显示) 强烈抗议:无怨无仇,为什么冒用我的ID 写一些乱七八糟的东西????? 一个简单的线程问题! 请问高手:两个类之间的关联关系如何实现? 各位请问, 我的系统要求支持三种语言, BIG5,GB2312,ENGLISH,数据库应该怎样设计!! 求救!!!vfp ,控件 optiongroup1, 属性 ButtonCount=N,如何给第N个OPtionN.Caption赋值 假如上天给你一个机会。。。请看过来。。。 如何配置IIS 使其利用TOMCAT 支持JSP AND SERVLET ? 请问:ORACLEH中保存每列的数据类型的系统表名叫什么? !!这个简单的问题,没解决!!!给分!! 对公的帐号不能向私人帐号汇钱,怎么办? wm_keydown 中 wparam 和 Lparam 中高位和低位所表示的意思是何东西 我用 ADO 从数据库里读数据,当记录实在太多了的时候就死机了,该怎么办? 笨问题:JAVA如何设置全局变量,要在所有的类和整个程序中有效 (2012•台湾)判断下列哪一组的a、b、c,可使二次函数y=ax2+bx+c-5x2-3x+7在坐标平面上的图形有最低点?(  )A.a=0,b=4,c=8 B.a=2,b=4,c=-8 C.a=4,b=-4,c=8 D.a=6,b=-4,c=-8 设X1,X2是一元二次方程X²+4X-3=0的两个根,2X1(X2²+5X2-3)+a=2,则a=? x平方-3[2x-5(x平方-x+1)] 分解2x四次+3x三次减5x2次+3x+2 计算:1的平方-2的平方+3的平方-4的平方+5的平方-6的平方+...+2001的平方-2002的平方+2003的平方-2004的平方. (x+5)的平方-(x-2)(x-3) 若x3+5x2-12x+m有因式x-3.则m=? 1.(2√3-3√2)的平方 2.(3√5+2√6)(2√6-3√5)3.√2(√2-√6)+(1+√3)(1-√3)4.(2√3-3√2)(2√2+√3) 如果3x的平方-2x=2,求(x的平方+5)-2(-2的平方+x+3)的值 下列何式为5x^2+17x-12的因式?A:x+1 B:x-1 C:x+4 D:x-4 说出理由啊 , (x+2)(x-3)-2(x-6)(x+5)-3(x平方-5x+17),x=5.5 若2÷(3*x的平方+2x+5)的值为1/3,则1/(6*x的平方+4x+1)的值为? 分解因式5x2+6xy-8y2 (1/2-3+5/6-7/12)*(-6)的平方 100x9.5x0.98+9.5x2简便运算 1分之1等于1是什么成语 设x2+ax+b是xn-x3+5x2+x+1与3xn-3x3+14x2+13x+2的公因式,则1+a+b=() 1=1的平方 1+3=2的平方 1+3+5=3的平方 5x2 =1打一成语 抛物线y=-0.5x2+2.5x-2与x轴相交于点A,B,与Y轴相交与点C.1.求证:ΔAOC相似与ΔCOB2.过点C做CD//X轴交抛物线与点D.若点P在线段AB上以每秒1个单位的速度由A向B运动,同时点Q在线段CD上也每秒1个单位 (√5+1)(√5-1)= (2-√ 7)(√7+2)= (2√3+1)平方 (7-√3)(2√3+1)=(√5+1)(√5-1)= (2-√ 7)(√7+2)= (2√3+1)平方=?(7-√3)(2√3+1)= 1*1等于1,根据提示写成语还有12345679 24.(13分)如图,在平面直角坐标系中,直线y=-x+3与x轴、y轴分别交于点B、C;抛物线y=-x2+bx+c经过24.(13分)如图,在平面直角坐标系中,直线y=-x+3与x轴、y轴分别交于点B、C;抛物线 -4的平方+3×(-2)的平方+(-5)÷(-3分之1)的平方 0+0等于一什么成语 已知:如图12,抛物线y=-4/5x²+mx+4与y轴交于点C,与x轴交于点A、B,(点A在点B的左侧)且满足OC=4OA.设抛物线的对称轴与x轴交于点M:(1)求抛物线的解析式及点M的坐标;(2)联接CM,点Q是射 观察1的平方+2的平方>2*2*1 3的平方+5的平方>>>> 拍立得相机拍出来的照片效果相当于数码相机的多少像素 如图,抛物线y=-0.5x2+0.5x+6,与x轴交于A、B两点,与y轴相交于C点已知E点(0,-3),在第一象限的抛物线上取点D,连结DE,使DE被x轴平分,试判定四边形ACDE的形状,并证明你的结论为什么D坐标是(3,3) 1+2+3+4+5+6的平方 9 8 7 6 5 4 3 2 1中间加上加减号等于21咋写 急 如图,抛物线y=-x²+5x+n经过点A(1,0)与y轴交于点B 杨辉三角形有什么规律? 5X^2-2根号15X+3等于0, 5x2-4(a+3)x+a2-29=0 谁能帮我讲一下杨辉三角 我数学不好,有点看不懂,有一道例题(x+2)^5 我想知道系数确定了,剩下怎么办 若y等于根号下5x减1再加根号下1减5x,则5x加y等于几 5X2—3因式分解 谁能告诉我杨辉三角中有哪些数学规律啊~~~ 大家帮帮忙了哦~!~~! 已知A=4x²-10x+8,B=5x²-6x+8,C=2x²-3,①求(a-b)*C②当X=-2时,求(A-b)*C 2x一10二4.5x2怎么算? 数学上杨辉三角怎么运用?如求(a+b)^4 ,(a+b)^5 ,(a+b)^6等,怎么运用杨辉三角来简便运算?(系数我懂,是依次来的,可问题是那些字母的幂数是怎么安排的?)也可以举几个例子让我看懂就好, 4/5x2=4x2一5x2=8/10=4/5是对还是错 (负1减二分之1)乘(负1减3分之1)乘.乘(负1减10分之1) 椭圆5x2+ky2=5的一个焦点为(0,2).求k的值这是一道椭圆方程题,请书写全过程. 当x变化时,分式3x2+6x+5/0.5x2+x+1的最小值为多少3x2,0.5x2指3倍的x的平方,0.5倍的x的平方尽量按一元二次方程格式 (负3又3分之2)十(负6分之一)= 点(0,4)是椭圆3KX2+KY2=1的一个焦点 求k 已知A=4x²-10x+8,B=5x²-6x+8,C=2x²-3,求(A-B)C分解因式 (负1.3)十[负4(负2负7)]= 椭圆8k2x2-ky2=2的一个焦点为(0,√7)求k的值.书里面没有过程的答案是:k=-1或-1/7再次抱歉!题应该是8k2x2-ky2=8我毫无怨言! 6x-6.5x2=8求方程 数学题所有的要过程3/5X2/11+2/11÷5/2 48X(1/2+5/6+3/8) 2又5/8X9.25+7又3/8X9又1/47/9÷11/8X(1/4+2/3) 7/18÷[(5/7-1/3X7/16] 请不要嫌麻烦 若椭圆2kx2+ky2=1的一个焦点为(0.-4),则k的值为 已知A=1-x2,B=x2-4x-3,C=5x2+4,求多项式A-2[A-B-2(B-C)]的值,其中x=-1 超急! 利用EXCEL确定x3-5x2-4x+6=0近似解?1)x为-9、-8、-7、-6、-5、-4、-3、-2、-1、0、1、2、3、4、5、6、72)x为-1.6、-1.5、-1.4、-1.3、-1.2、-1.1、-1、……、0.6、0.7、0.8、0.9、1、1.1、……、5.2、5.3、5.4、5.5 已知F是椭圆5x2+9y2=45的左焦点,P是此椭圆上的动点,A(1,1)是一定点,求|PA|+3/2|PF|的最小值,并求点P的坐标 已知多项式x2-(a-1)x^3+5x2-(b+3)x-1中不含x^3和x项,求a、b的值 设x1、x2是一元二次方程x^2+4x-3=0的两个根,2x1(x2的平方+5x2-3)=_____ 3(x+1)的平方-5(x+1)=2怎么解
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn