HBase+MapReduce案例

        目前停车场现金统计报表使用日表,数据采用累加方式,存在冗余数据,保存数据时需先查询出对应条目加上这次数据累计再进行更新,高并发下存在问题,且无法按具体时间查询,代码逻辑也存在大量重复性代码,代码结构较差;统计类型数据不存在关联查询,适合使用非关系型数据库,可提高查询性能,这里选择HBASE。

        1.阅读目前代码,梳理业务逻辑,重新设计实体类

        统计数据包括包期充值、包期续费、包期退款、账户充值、账户退款、临时车缴费(称作收支类型)操作产生的交易行为。

        收支方式包括现金、账户、第三方、支付宝、微信、无感支付。

        临时车缴费操作中,按收费来源分为岗亭缴费、中央缴费、自助缴费、手持终端缴费(收费宝)、手机H5缴费、第三方缴费。        

        车辆包期退款记录、账户充值退款记录、临时车缴费记录都有自己专门的表,为了快速地查询出统计数据的日报表、月报表、年报等报表,把这些数据另外统一记录在了一张表中,专门用于统计数据的展示。

        原先:每种收支类型和收费来源占用一个字段,同一天相同停车场和收支类型或收费来源的数据叠加在一条记录上,保存数据时需先查询出对应条目加上这次数据累计再进行更新,高并发下存在问题,使用锁的话会导致性能下降。且这样的逻辑下不能按具体时刻来查询统计数据,最小单位只能以天来进行查询。原先实体类中还有一些不插入数据库的应属dto的字段,也需进行分离。

       修改后:收支类型单独占一个字段,收费来源单独占一个字段,收支方式占一个字段。

       原先的29个字段(22个数据库字段)改为10个字段。

 

Integer reportType;        //收支方式  1现金 2账户 3第三方 4支付宝 5微信 6无感支付 EnumChargeStyleType
Integer incomeOutcomeType; // 收支类型 
String parkId;              //停车场ID
Integer reportItem;        //收费来源
Date costTime;              //缴费时间
String indexCode;           //系统编号
BigDecimal amount; //金额
String regionIndexCode;
String userIndexCode;        //用户IndexCode
String selfPosId;           //自助缴费机ID
BigDecimal reductPay;       //优惠金额

 

        一是按收支方式(现金等)、收支类型(包期充值等)分组查询,然后按收支类型分为收入和支出,分别统计不同收支方式的金额,供收费报表统计环形图展示。

        二是按收支方式(现金等)、停车库、收支类型(包期充值等)分组查询,然后按收支类型分为收入和支出,供收费报表统计列表展示。

        三是收支类型为临时车缴费,按收支方式(现金等)、收费来源(岗亭缴费等)分组查询,然后将不同收费来源的数据展示在一条数据上,供临时车缴费统计展示。

        四是收支类型为临时车缴费,按收支方式(现金等)、停车库、收费来源(岗亭缴费等)分组查询,然后将同一停车场不同收费来源的数据展示在一条数据上,供临时车缴费列表展示。

 

HBase应用场景:https://blog.csdn.net/u011598442/article/details/89891926

        “HBase作为一种kv数据库,能够很好的面对高吞吐率的在线数据读写服务,尤其是写操作,但是在非rowkey多条件查询、数据分析、统计等场景下,HBase表现的就不是很好了,这些场景下就比较适合来用MapReduce来计算。”

        所以,我们这里使用HBase存储数据,使用MapReduce对数据进行简单的统计和计算,以满足需求。

 

Hbase表结构

表名:Report

rowkey:costTime_indexCode   //缴费时间_系统编号

列族(列族怎么分?):

info:reportType;                  //收支方式  1现金 2账户 3第三方 4支付宝 5微信 6无感支付
info:incomeOutcomeType; //收支类型
info:parkId;                         //停车场ID
info:reportItem;                   //收费来源
info:amount;                      //金额
info:regionIndexCode;       //
info:userIndexCode;          //用户IndexCode
info:selfPosId;                   //自助缴费机ID
info:reductPay;                  //优惠金额

 

        一是按收支方式(reportType,现金等)、收支类型(incomeOutcomeType,包期充值等)分组查询,然后按收支类型分为收入和支出,分别统计不同收支方式的金额,供收费报表统计环形图展示。

 

Map程序

        ReportAnalyzeMapper 继承HBase的TableMapper基类,把incomeOutcomeType_reportType看做文本,金额数是BigDecimal,用Text格式保存,所以key-value输出类型是<Text,Text>。

        根据收支类型属于收入还是支出来区分,收入则生成in_reportType,同时统一生成in_all,支出则生成out_reportType,同时统一生成out_all。这样mapper就能根据in_all计算出总收入,根据in_reportType计算出收入中每种收支方式的金额(比如总收入100元,现金收入20元,支付宝收入40元,微信收入40元),根据out_all计算出总支出,根据out_reportType计算出支出中每种收支方式的金额(比如总支出100元,现金支出40元,支付宝支出30元,微信支出30元)。

/*

*

*

* 按收支方式(现金等)、收支类型(包期充值等)分组查询,然后按收支类型分为收入和支出,分别统计不同收支方式的金额,供收费报表统计环形图展示。

* key time_indexcode

* info:reportType;                  //收支方式  1现金 2账户 3第三方 4支付宝 5微信 6无感支付

* info:incomeOutcomeType;           //收支类型 1账户充值 2包期充值 3包期续费 4免费放行 5临时车缴费 6账户退款 7包期退款

* info:parkId;                      //停车场ID

* info:reportItem;                  //1岗亭缴费 2人工缴费 3收费宝 4第三方

* info:amount;                      //金额

* info:regionIndexCode;       //

* info:userIndexCode;          //用户IndexCode

* info:selfPosId;                   //自助缴费机ID

* info:reduct;                  //优惠金额

*

* incomeOutcomeType_reportType作为key   in/out_收支方式(现金等)作为key

* 1_1 1.2

* 2_3 5.0

*/

public class ReportAnalyzeMapper extends TableMapper<Text, Text> {

    @Override

    protected void map(ImmutableBytesWritable key, Result value, Mapper<ImmutableBytesWritable, Result, Text, Text>.Context context)

            throws IOException, InterruptedException {

        try {

            String reportType = "";

            String incomeOutcomeType = "";

            String amount = "";

            for (java.util.Map.Entry<byte[], byte[]> val : value.getFamilyMap(Bytes.toBytes("info")).entrySet()){//遍历每一列

                String str = new String(val.getValue());

                if (str != null) {

                    String columnKey = new String(val.getKey());

                    if (columnKey.equals("reportType")) {

                        reportType = str;

                    }

                    if (columnKey.equals("incomeOutcomeType")) {

                        incomeOutcomeType = str;

                    }

                    if (columnKey.equals("amount")) {

                        amount = str;

                    }

                }

            }

            if (null != incomeOutcomeType) {

                switch (incomeOutcomeType) {

                    case "1":

                    case "2":

                    case "3":

                    case "4":
    
                    case "5":

                        //不同收支方式的收入

                        context.write(new Text("in" + "_" + reportType), new Text(amount));

                        //全部的收入

                        context.write(new Text("in" + "_" + "all"), new Text(amount));

                    case "6":

                        //不同收支方式的支出

                        context.write(new Text("out" + "_" + reportType), new Text(amount));

                        //全部的支出

                        context.write(new Text("out" + "_" + "all"), new Text(amount));
        
                    default:break;

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

Reduce程序

        TableAnalyzeReduce继承HBase的TableReducer基类,这里需要把最终聚合后的结果写到目标表中,rowkey对应source,行数对应目标表的column:count列。

        把相同的key的值加起来。

        key为in_all累计得出总收入,key为in_1累计得出总收入中现金收入的金额,key为in_4累计得出总收入中支付宝收入的金额,等等,key为out_all累计得出总支出,key为out_1累计得出支出中现金支出的金额,key为out_5累计得出支出中微信支出的金额,等等。

/*

* 1现金 2账户 3第三方 4支付宝 5微信 6无感支付

* 按收支方式(现金等)、收支类型(包期充值等)分组查询,然后按收支类型分为收入和支出,分别统计不同收支方式的金额,供收费报表统计环形图展示。

* 1_1 3.7

* 2_3 5.0

*

* 收支类型属于收入的,累加

* 收支类型属于支出的,累加

* 收支类型属于收入的,按收支方式累加

* 收支类型属于支出的,按收支方式累加

*

*/

public class ReportAnalyzeReduce extends Reducer<Text, Text, Text, Text> {



    @Override

    protected void reduce(Text key, Iterable<Text> values, Context context)

            throws IOException, InterruptedException {

        BigDecimal i = new BigDecimal(0);

        for (Text val : values) {

            BigDecimal d = new BigDecimal(val.toString());

            i.add(d);

        }

        context.write(key, new Text(i.toString()));

    }



}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值