3.7.4 MapReduce
MapReduce 是整个大数据的精髓所在(实际中别用,因为在MongoDB中属于最底层操作)。
MapReduce是一种计算模型,简单的说就是将大批量的工作分解执行,然后再将结果合并成最终结果。
MapReduce 就是分为两步处理数据:
- Map:将数据分别取出
- Reduce:负责数据的最后的处理
范例:建立一组雇员数据
db.emps.insert({"name": "张三", "age": 30, "sex": "男", "job": "CLERK", "salary": 1000}); db.emps.insert({"name": "李四", "age": 28, "sex": "女", "job": "CLERK", "salary": 5000}); db.emps.insert({"name": "王五", "age": 26, "sex": "男", "job": "MANAGER", "salary": 6000}); db.emps.insert({"name": "赵六", "age": 32, "sex": "女", "job": "MANAGER", "salary": 7000}); db.emps.insert({"name": "孙七", "age": 31, "sex": "男", "job": "CLERK", "salary": 2000}); db.emps.insert({"name": "王八", "age": 35, "sex": "女", "job": "PRESIDENT", "salary": 9000});
范例:按照职位分组,取得每个职位的人名
第一步:编写分组的定义(即Map函数):
var jobMapFun = function(){ emit(this.job, this.name); //emit()函数表示每行数据按照job分组,取出name };
分析:
- Map函数调用emit(key, value),遍历emps中所有的记录,将key与value传递给Reduce函数进行处理,
- Map函数必须调用emit(key, value)返回的键值对。
- emit()函数是按数据的分组走,比如第一组:{key : "CLERK", values : [姓名, 姓名,...]}
第二步:编写 reduce 操作:
var jobReduceFun = function(key, values){ return {"job": key, "names": values}; }
分析:reduce统计函数,reduce函数的任务是将key-values变为key-value,也就是把values数组变为一个单一的值value。
第三步:进行操作的整合:
db.runCommand({ "mapreduce": "emps", "map": jobMapFun, "reduce": jobReduceFun, "out": "t_job_emp" });
分析:out:统计结果存放集合(不指定则使用临时集合,在客户端断开后自动删除)
执行后所有结果都保存在"t_job_emp"集合里面。
或者将上面第三步换成如下第三步和第四步
第三步:针对MapReduce处理完成的数据实际上也可以执行一个最后的处理
var jobFinalizeFun = function(key, values){ if(key == "PRESIDENT"){ return {"job": key, "names": values, "info": "公司的老大"}; } return {"job": key, "names": values}; }
第四步:进行操作的整合
db.runCommand({ "mapreduce": "emps", "map": jobMapFun, "reduce": jobReduceFun, "out": "t_job_emp", "finalize": jobFinalizeFun });
现在执行之后,所有的处理结果都保存在 “t_job_emp” 集合里面,通过如下命令查看
db.t_job_emp.find()
范例:统计出性别的人数、平均工资、最低工资、雇员姓名。
var sexMapFun = function(){ // 定义好了分组的条件,以及每个集合要取出的内容 emit(this.sex, {"ccount" : 1, "csal" : this.salary, "cmax" : this.salary, "cmin" : this.salary, "cname" : this.name}) }
var sexReduceFun = function(key, values){ var total = 0; //统计 var sum = 0; //计算总工资 var max = values[0].cmax; //假设第一个数据是最高工资 var min = values[0].cmin; //假设第一个数据是最低工资 for (var x in values){ // 表示循环取出里面的数据 total += values[x].ccount; //人数增加 sum += values[x].csal; //就可以循环取出所有的工资,并且累加 if (max < values[x].cmax){ //不是最高工资 max = values[x].cmax; } if (min > values[x].cmin){ //不是最低工资 min = values[x].cmin; } names[x] = values[x].cname //保存姓名 } var avg = (sum / total).toFixed(2); //toFixed(2)设置成两位小数 //返回数据的处理结果 return {"count" : total, "avg" : avg, "sum" : sum, "max" : max, "min" : min, "names" : names}; }
db.runCommand({ "mapreduce" : "emps", "map" : sexMapFun, "reduce" : sexReduceFun, "out" : "t_sex_emp" })
现在执行之后,所有的处理结果都保存在“t_sex_emp”集合里面,通过如下命令查看:
db.t_sex_emp.find()