00:00
前面我们已经知道怎么样去统计当前网站的PV值了,那接下来哎,我们要考虑另外一个指标就是UVUV大家也非常的熟悉,跟PV往往都是我们都放在一起来说,一起来计算的啊,那UV其实统计的方法跟PV是差不多的,也是有一个来一个就就统计一次,来一个就统计一次,就看用户对于网站的访问量啊,或者说那个浏览量,那它的区别在于。当前的这个UV其实是什么呢?哎,大家注意,就是之前PV我们讲的是page view嘛,是页面浏览,那UV的话,注意不是user view,它是unique visitor,就指的是当前网站的独立访客数,对吧?啊,所以言下之意就是说,如果我们一个用户在这个网站上点了很多次,浏览了很多次的话,最终我们统计的时候是要做一个去重,对不对,按按照这个user user ID啊去做一个去重,我们最后不管你浏览多少次,最后给我们的这个UV值只贡献一次UV啊,那所以这个其实在平常使用的过程当中,大家能想到,呃,那有什么样的一个处理思路呢?
01:13
最简单的方式是不是就是把每个用户的UID拿到,然后去直接做一个驱重就好了,对吧?啊,就是用各种各样的那个数据类型去做一个去重啊呃,那这里边涉及到另外一个问题,就是有些场景下,如果要是拿不到这个用户的user ID呢?比方说有些场景下,我们可能用户可以这个匿名登录,对吧,或者有时候我们根本就没有做那个买点买点数据,呃,没有做买点,拿不到那个买点日志,那我们是不是就只能从用户像外部服务器发起的那个网络请求来找了啊,那在这个网络请求里面,我们也拿不到对应的那个信息啊,啊所以在有些场景下,我们可以用IP或者说当前用户的那个cookie去做一个代替,对吧?就如果拿不到user ID的情况下,可以用它来做一个代替。
02:03
那当然了,现在我们的场景没什么问题,我们现在是不是直接就有用用户的uz ID,那直接把所有访问的那个PV行为提取出来,然后以这个uzr ID作为一个作为一个标志,针对uz ID去做一个去重是不是就可以了啊,所以接下来我们要做的事情就是这样啊,那还是新建一个当前包,下边新建一个class,这个我们就叫做啊它UV啊,我们就叫做unique visitor。其实前面这个流程我们就想到跟PV应该差不多对吧?啊,前面都是啊,首先我们把这个没方法创建执行环境,读取数据啊,然后创建一个data stream转换为这样的一个pole,对应的类型分配时间戳和watermark。那这里边是升序啊,所以我们都是这个ending time sta extra直接把它分配出来就完事了,呃,那这里边我们get这个class的时候,当然也不要用那个page view啊,我们用当前这个对吧?Unique visitor去get当前的这个class就可以了,呃,然后后边我们这里边分配哦,这里边啊,这本来是用出来的,对吧。
03:16
然后下边下边这个还没处理完,我们最终肯定是要有一个。Env执行起来的这个过程,我们现在是一个UV count drop,后面还有一个画括号啊,这是大概的一个处理流程,然后所不同的主要就是在这里要。接下来要做开窗统计了,对吧,呃,我们直接。开窗统计UV值,同样我们现在的需求还是统计每个小时之内的那个数据,之前大家记得是这个四万多五万多嘛,我们看看对于uz ID做了去重之后,是不是这个数据量会变小呢?啊,这个是可以考虑到的啊,然后为了后面我们看的更清楚,这个我把全局并行度还是改改回成一吧,看得更清晰一些啊。然后这里边我们开窗统计的时候呢,我可以基于data stream,首先我先做一个filter,这还是一样的,只取当前的那个PV数据,对吧?
04:17
PV,然后equals用data的get behavior把它get出来,然后后边之前我们是先做了一个map,然后去分配K,然后接下来是KBY,然后去开窗去做聚合,那我们现在呢,呃,大家想到如果我要是考虑到并行计算的话,那就是还是刚才那样随机生成K对吧,然后再再做一个分区的统计,那我现在既然是全区并行度设都设了一,那就不需要那么那么复杂了啊,那我可以直接全部都分配到同一个分组里面去,对吧?Map成一个相同的这样一个一个结构,一个同相同的K,那或者其实我们知道这个效果是不是就跟直接。
05:02
Time window time window2是一样的呀啊,所以接下来我干脆直接做一个WINDOW2操作啊,前面我们没用过,这里也直接就给大家说明一下,一般是不推荐用,但是假如说你现在并行就是一的话,那其实也没什么毛病吧,直接探不都二,然后里边当然传的还是一个时间,我们现在是一小时,所以直接给一个time点二子一接下来。哎,那大家想现在我的这个处理思路是什么呢?我的处理思路其实可以非常简单,是不是可以把所有的数据都收集起来,然后是不是我放到一个set里边自动是不是就去去重掉了,因为set数据结构本来就有这样一个特点,对吧,里边的数据是是不可重复的,所以这个思路非常简单,我干脆就直接来一个全窗口函数。把所有的数据都收集齐,然后等到收收集齐的时候,我出发计算的时候怎么样呢?把数据都拿出来,丢到一个set里边,最后set的本身的这个大小,那个size是不是就是我当前user ID的个数啊,也就是当前是不是UV的这个值啊,哎,所以这个其实是整个思路是非常的容易想到的啊啊,那所以我这里边直接来一个apply。
06:23
大家想apply是不是里边就可以传一个全窗口函数啊,所以接下来这里面要传的就是一个window function,对吧?这个是UV count result。我先把这个单独定义出来,那最后这个做完之后啊。我把它定义成一个UV strip。可以把它做一个打印输出,这就是当前的结果啊,那我们关键就在于怎么样去实现这个了,实现自定义全窗口函数public static class啊,我们前面定义的这个叫UV count result。
07:05
啊,大家想到这里面我们要实现的是一个window function,对不对,但是大家要注意啊,这里其实还不是window function,为什么呢?因为我们这里面是time window or。如果要是做了window or操作的话,里边我们应用的那个是不是就叫做一个all window function啊,对吧?这个要稍微的区分一下啊,本来如果说我们直接开窗time window后面apply的话,要传的就是一个window function,对吧?现在你是time window or,所以这里边我们要传的是一个or window function。Or window function,那这里大家看到里边的这个类型是不是还是现在比我们之前的那个window function是不是少了一个类型啊,少了一个K,因为你现在没有KBY嘛,没有做分组对吧,所以就是输入输出和窗口类型,那我现在的输入是。
08:02
这个比较简单,是不是就是user behavior啊?没做过map转换吗?输入的那个破类型啊,然后输出我们现在得到的那个结果,是不是也可以直接用那个配置view count啊,就像我们之前的这个,呃,某一个URL这里边我们就是直接统一叫UV了,对吧,然后每个窗口count值是多少,直接我就借用这个吧,所以还是叫做page view count。哎,那么当前的这个window的类型是时间窗口time window写完这个之后上边,诶,上面这里边主要问题在于本身的这个数据类型是不是不应该是object呀,我们得到的应该是page will count,这样就没问题了。那接下来最关键的要实现的就是一个还是一个apply方法,跟那个window function一样的,然后大家现在再看一下的话,是不是前面就少了一个key啊,还记得之前我们那个apply参数对吧,少了一个key,然后window还是能拿到,然后同样的是不是也有一个able类型的,现在这个也是叫values对吧?啊,所有的这个输入数据啊,之前我们那个应该是叫input啊,现在叫values,就所有的数据,另外用out.collect做输出啊,这就是跟之前的那个window function基本上是一样,好,那现在我们这怎么办呢?那是不是我的想法就是。
09:26
定义一个set结构对吧,一个数据结构,呃,保存所有的。呃,所有窗口中,窗口中的所有数据。呃,其实大家想我不用保存完整的数据,因为我当前那个数据进来的是user behavior,对吧,我只要存什么就行了。是不是我只要存user ID就可以了呀,而且是我只能存user user ID,因为你假如是完整的存这个user behavior的话,最后你放在set里边是不是同样即使是相同的用户,他在那个时间戳都不一样啊,那你最后肯定就不一样吧?诶,所以这里边我们只能存user ID对吧?保存所有的user ID,然后自动去除。
10:17
所以当前我定义一个new,一个哈希set,直接把它定义出来对吧?呃,那当前这个呢,我可以定义里边,对要保存的不就是一个uz ID吗?所以定义一个长整型对吧?哎,那当前我把它叫直接就叫做ID set吧,Uz ID对吧,或者叫u ID set。这就是我们预先的数据结构,先放在这儿,然后接下来,呃,大家其实会发现当前是不是就是这个。呃,就是toable类型的这个这个values,直接把它转成这样一个set就完事了呀啊,当然如果大家嫌那个麻烦,还得引入其他的一些依赖的话,大家知道底层是不是相当于都是一个便利啊,哎,我直接写一个for循环便利就完事了啊呃,那我这里边当然就是把每一个user behavior都要拿出来,比方说这个我叫UB啊,从当前的values里边取出来,然后是不是直接u ID set,然后把它添加进去就完事了啊,注意添加的是当前的user ID对吧?
11:22
那最后我们al.collect输出的是一个你一个page count里边的值。URL我就直接写UV全部的那个UV值嘛,然后接下来window and,那就是window.get下一个当前的这个最后的count值,那应该是什么?对,是不是直接就是u id.size啊啊但是大家会发现这个size啊,得到的是一个int,所以接下来是不是我把它再直接转成一个。一个长整形long就完事了呀,对吧?啊,所以这样就没问题了啊,这就是我们想要的最后的结果,所以接下来我们来运行一下,看看这个效果怎么样。
12:07
当然这个过程大家会发现你可以去改进对吧,首先基于前面的那个并行度啊,我们可以把前面并行度调大,这里边你就不要太蒙多奥,直接在一个分区去做操作了,可以再分开,最后再聚合,跟前面的做法是一模一样的,然后另外还有一个可以值得。呃,值得我们去改进的地方,大家发现这是全窗口函数对吧。是不是所有数据来了之后,我才做了这样的一个便利,然后再生成这个set,然后呃,取取到这个set的这个值啊,那大家想我如果要是做的把这个效率提升一点,是不是也可以做一个增量聚合,怎么样增量聚合呢?是不是你只要把这个set定义成我们的那个中间聚合的状态是不是就可以了呀,然后我每来一个数据之后,是不是直接就是丢到这个这个状态里面,丢到这个set里边做一个保存就完事了,对吧?然后等到最终结束的时候,你假如还想要这个window呃的这个信息的话,我再包装一个呃,Window对应的这个window function是不是就是拿出来当前这个状态里边set所有的那个当前的大小,然后再拿到window and就可以了啊,所以这个你去进一步做改进啊,做优化也是可以的,我们看一下这里面的执行结果吧。
13:22
大家看现在这个驱虫效果其实还是很明显的,同样还是十个窗口,之前大概每一个窗口里边都有四五万,现在大家看到基本上就是两万多三万多对吧?呃,基本上就是3万的这个效果了,之前我们最大的大概有52500多,那现在就是34746对吧,大概34700多啊u uz ID的驱虫效果还是非常的明显的,这就是我们直接在这个当前啊,在这个窗口里边直接用一个set数据结构去做去除,非常简单的实现。
我来说两句