SparkSteaming进行UV统计

     需求:每个一分钟统计一次最近一个小时UV

   思考过程:由于UV是矢量数据,不能每分钟的UV最一个统计,在基于每分钟的计算结果进行累加计算出最近一个小时用户量的求和。

SparkStreaming提供的窗口函数,比较知名的reduceByKeyAndWindow开窗函数,该函数一般用于对标量数据的统计,例如,用户访问量、点击量等;通过对该算子源代码解读,了解到该算子通过重叠两个窗口之间的统计数据来进行计算过程的优化,数据的保存主要采用checkpoint机制。

方案一:key设置成用户ID,通过该算子每隔一分钟进行最近一个小时时间跨度的统计,job执行的batch_interval采用1分钟,窗口设置一个小时,滑动时间设置成1分钟,仅仅传递第一个参数为_+_,这样每隔一分钟能够对最近一个小时窗口的数据进行UV统计,当时间跨度为1个小时时会有59分钟的重复计算;为了尽可能的复用结果,再次仔细研究该算子的源代码,

从下图来简单介绍向前窗口  数据量的计算,以_+_,_-_为例;


      _____________________________

      |  previous window            _________|___________________
      |___________________|       current window                                 | 
                                                   |_____________________________|
     

      |________t1________    |___ t2____|________t3_________|


t1部分对应前一个窗口的特有部分的数据;t2是两个窗口的重叠部分;t3是当前窗口特有的数据;

首先根据key进行分组,然后  把前一个窗口计算出来的该key 的个数保存起来,在计算当前窗口数据量时,采用前一个窗口的总数,减去t1时间段该key的每一个value,然后再加上t3时间段该key对应的每一个value;   再思考UV时间,可以把用户标识作为key,然后通过该函数计算生成的元祖,value为0标识该用户已经不在当前窗口范围中,统计要去掉value为0对应的key(这是该需要最关键的地方),即,源代码是这样的

kafkaStream.reduceByKeyAndWindow((v1:Int, v2: Int) => v1 + v2,(v1:Int, v2: Int) => v1 - v2,Minutes(Integer.parseInt(args.head)),Minutes(1),32, (x: (String, Int)) => x._2 != 0  )



由于SparkSteaming1.6还不支持对数据内容进行判断,为了保证UV统计的正确性,需要保证数据能够尽快的流进来并做及时的统计,不要让kafka中的数据有积压;


优化方案:1)设置kryo序列化机制;2)多个DSTream进行处理,并提高接受数据的并行度;3)调整reduce类型算子并行度;4)调整作业触发时间 变小




调试过程中遇到下面的问题,因为连接池中的链接很久没有使用断开与MySQL服务器的链接。
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure




The last packet successfully received from the server was 23,579 milliseconds ago.  The last packet sent successfully to the server was 0 milliseconds ago.
        at sun.reflect.GeneratedConstructorAccessor40.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.Java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
        at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1118)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3055)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2941)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3489)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1959)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2113)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2568)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2113)
        at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1364)
        at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.execute(NewProxyPreparedStatement.java:989)
        at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java:185)
        at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteQuery(MappedStatement.java:221)
        at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeQueryWithCallback(MappedStatement.java:189)
        ... 36 more
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
        at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2502)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2952)
        ... 47 more

该问题的解决方案:1)每次链接时间都先测试当前链接是否为有效链接;2)catch异常之后,重新创建数据库链接;有限选择方案2),因为数据库链接超时的情况相对还是比较少的;

优化思路:因为每次计算都会涉及到整个时间窗口的RDD数据,在一个小时的时间窗口内任务会越来越慢以致一分钟之内不能计算一个窗口的数据。
其中思考两种优化的思路,通过Redis hyperloglog
其中,一个优化思路就是提前把每一分钟的用户访问量先计算好,但是,在用户数量非常多,例如1分钟每个用户仅访问一次时,该思路起不到优化效果。


思考,面对非结构话的存储或者数据,在key的设计也根据具体的业务常见,类似HBase RowKey设计、reduceByKeyAndWindow之类的key的设计,都要结合不同的业务场景来设计,还有value值的设计,例如,用户本周消费金额的环比增长,可以把上一周的消费金额前加上-,本周的消费金额不变,直接进行累加,就是这一周比上一周增长的总金额。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值