MapReduce

MapReduce

简介

MapReduce是聚合工具中的明星。Countdistinctgroup能做的上述事情Mapreduce都能做。他是一个可以轻松并行化到多个服务器的聚合方法。它会拆分问题,再将各个部分发送到不同的机器上,让每台机器都完成一部分。当所有机器都完成的时候,再把结果汇集起来形成最终完整的结果。

MapReduce需要几个步骤。最开始是映射(map),将操作映射到集合中的每个文档。这个操作要么“无作为”,要么“产生一些键和X个值”。然后就是中间环节,称作洗牌(shuffle),按照键分组,并将产生的键值组成列表放到对应的键中。化简(reduce)则把列表中的值化简成一个单值。这个值被返回,然后接着进行洗牌,直到每个键的列表只有一个值为止,这个值也就是最终结果。

使用mapreduce的代价就是速度:group不是很快,mapreduce更慢,绝不要用在“实时”环境中。要作为后台任务来运行MapReduce,将创建一个保存结果的集合,可以对这个集合进行实时查询。

实例:

对消息体统的一个统计:

 public List<MsgAnalysisByDate> mapreduceAndroidPushedByDate(Date timeBegin, Date timeEnd) {

        log.info("--------------安卓推送统计开始mapreduceAndroidPushedByDate-----begin");

        MsgMapReduceBo msgMapReduceBo = new MsgMapReduceBo(timeBegin, timeEnd, bcandroidpushrecordMongoDBClient, MsgFlagTypeEnum.PUSHED.getCode(), msgIdName, titleName);

        return doMapReduce(msgMapReduceBo);

    }

    private List<MsgAnalysisByDate> doMapReduce(MsgMapReduceBo msgMapReduceBo) {

        int type = AppTypeEnum.JD.getCode();     //掌上京东

        String outputTarget = livenessMapreduce.buildOutputTarget("tmp_liveness_d_" + type);

        List<MsgAnalysisByDate> result = new ArrayList<MsgAnalysisByDate>(200);

        String map = buildMapForBC(msgMapReduceBo.getMsgIdName(), msgMapReduceBo.getTitleName());

        String reduce = buildReduceForBC(msgMapReduceBo.getMsgIdName());

        QueryBuilder query1 = new QueryBuilder();

        query1.and("created").greaterThanEquals(msgMapReduceBo.getTimeBegin()).lessThan(msgMapReduceBo.getTimeEnd());//统计安装量,用created

        DBCollection collection = msgMapReduceBo.getMongoDBClient().mapReduce(map, reduce, outputTarget, query1).getOutputCollection();

        log.info("domapReduce结束:客户端:"+msgMapReduceBo.getMongoDBClient());

        DBCursor cursor = collection.find();

        while (cursor.hasNext()) {

            DBObject item = cursor.next();

            //如果主键为空,忽略记录

            DBObject value = (DBObject) item.get("value");

            Double count = (Double) value.get("count") == null ? 0 : (Double) value.get("count");//总量

            String title = (String) value.get("title") == null ? "" : (String) value.get("title");

            String msgId = (String) value.get(msgMapReduceBo.getMsgIdName());

            String platformReal = (String) value.get("platform") == null ? "" : (String) value.get("platform");

            if (value.toMap().size() == 2) {

                //如果返回的value尺寸为2,说明只有一条记录,因为只有一条记录时没有进行reduce,结果集是map的,

                String _id = (String) item.get("_id");

                int index = _id.indexOf("-");

                msgId = _id.substring(0, index);

                int idex2 = msgId.indexOf("_");

                msgId = msgId.substring(idex2 + 1, msgId.length());

                platformReal = _id.substring(index + 1, _id.length());

            }

            MsgAnalysisByDate tmp = new MsgAnalysisByDate();

            tmp.setMsgid(msgId);

            tmp.setTile(title);

            tmp.setType(type);

            tmp.setFlag(msgMapReduceBo.getFlag());

            if (PlatformEnum.getByValue(platformReal) != null) {

                tmp.setPlatform(PlatformEnum.getByValue(platformReal).getKey());

            } else {

                log.error("msgId=" + msgId + "platformReal=" + platformReal);

            }

            if (msgMapReduceBo.isRead()) {

                tmp.setReadAmount(count.intValue());

            } else {

                tmp.setAmount(count.intValue());

            }

            result.add(tmp);

        }

        collection.drop();

        return result;

    }

    /**

     * 注意这里的key,用横杠分割,用于处理多个goupby 字段

     *

     * @return

     */

    private String buildMapForBC(String id, String title) {

        return "function()" +

                "{" +

                "var key1=this." + id + ";" +

                "var prefix=\'-\';" +

                "var key2=this.platform;" +

                "emit(key1+prefix+key2,{title:this." + title + ",count:1});" +

                "};";

    }

    /**

     * 注意冗余title

     *

     * @return

     */

    public String buildReduceForBC(String id) {

        return "function(key,values)" +

                "{" +

                "var total = 0;" +

                "var title=values[0].title;" +

                " var locate = key.indexOf(\'-\');" +

                " var platform = key.indexOf(\'-\');" +

                "var platform=values[0].title;" +

                "for(var i=0;i<values.length;i++){" +

                "total += values[i].count;" +

                "}" +

                "return {" + id + ":key.substr(0,locate) ,title: title, platform:key.substr(locate+1,key.length), count : total};" +

                "};";

    }

说明:

1. Map阶段:{key1+prefix+key2}作为key{title:this." + title + ",count:1}作为value,遍历每一个doc,生成一堆上述(keyvalue)对的文件

2. Shuffle阶段:把上述的doc文件。Value合并,变成(keyvalues{value1,value2...valuen};

3. Reduce阶段:把上述的结果文件进行reduce处理,具体逻辑就是reduce函数,本例中就是遍历values,取count,汇总总数,值得注意的是这里用了一个技巧:key用了多个字段拼接,用于返回多个值。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值