00:00
我们已经实现的这个例子里边是做了一个PV的统计,那PV统计我们发现了,它整体来讲是比较简单的,就是来一个数据统计一次嘛,但实际应用场景里边我们知道,哎,就是某一个用户可能有大量的这种重复的点击,而且说呃,如果我们遇到有一些这种恶意的刷点击量的这种行为的话,他其实可以跑一个外挂程序给我们去发送请求的,对吧?你在这种情况下,如果所有的请求拿过来之后都做一个PV统计的话,最后的这个访问量可能就不是那么的直观,不是那么的有效,所以在很多情况下,我们可能要结合另外一个指标,就是所谓的独立访客数UV,对吧?呃,UV大家注意,不是所谓的那个user visit对吧?我们说那个PV是page view嘛,那UV并不是说这个user view,或者说user visit,它指的是unique visit,就是当前网站的独立访客数。
01:00
啊,一般情况我们就是比方说也是统计一段时间区间内啊,比方说一个小时或者一天,我们统计呢,就是所有每一个用户来到这个网站的一次访问,一次点击,那我们都要统计进来,但是如果要是当前用户的重复还是同一个用户的第二次,第三次后续的这个点击和访问操作,我们就不再记入了啊就相当于就是每个用户只记一次,对吧?这就是所谓的独立访客数,这个指标大家也非常的呃熟悉啊,一般情况我们对于这个UV行为的统计数据从哪里去?呃,就是摘取这个数据去去做驱重的,那一般情况我们的来源可能就是网站本身的这个web服务器的log和买点日志,那大家会想到如果是web服务器log的话,我们就只能通过IP或者是诶大家知道cookie对吧?啊,就是用户在访问的时候,只要是开启了这个,呃,允许这个cookie的话。
02:00
啊,发送cookie的话,那我们可以收集到当前诶用户,他本身cookie里边是有一个session ID的嘛,我们知道当前这个呃用户到底是什么样的一个状态啊,它后续的访问就可以给它过滤掉,那另外还有就是如果说我们本身已经有了用户的这个买点日志本身就带着用户的ID信息的话,那当然就简单很多了,对于我们现在这个场景其实非常的非常的简单,因为我们现在用的是买点日志本身数据里边已经包含了当前这个user behavior里边已经包含了user ID对吧?啊所以那我们当然就是用这个user ID去做一个去重就完了嘛。呃,那首先我们先来把这个整体的代码框架先搭起来,同样啊,我们在下边去创建一个,新建一个skyla,呃的object单利对象。然后当前的这个我们就叫做uvr unique unique visitor,好,先创建出来,呃,然后接下来本身里边的一些定定定义啊,大家会想到基本上都差不多对吧?那首先可能我们需要有一个输入输据输入输出样例类,输入数据当然还是u.behavior了,跟PV这边一样,我们既然是在同一个包里边,那算了,不定义了对吧?啊,那接下来我们主要是定义这个输出定义输出呃,UV统计样例类,其实这个大家会发现跟前面的这个PV是差不多的对吧,只不过就是PV这里边呢,我们关心的是,呃,就是就是没有去重的所有数据的一个一个值,而我们现在是去重之后的一个值,同样这里边我我也并不关心就是到底哪一个UID对吧,或者哪一个item ID他被点了多少次,我统计的也是一个整个网站的,呃,全全局的一个值,所以说。
03:56
那是不是想要的也是一个window and和count值啊啊,所以甚至大家你想要把这个都统一起来啊,还用之前的那个PV都可以啊,当然我这里边还是单独定义一下吧,我只换个名,管这个叫做UV count对吧?啊,其实跟刚才刚才的那个换汤不换药啊,然后主体的流程的话,我们发现跟PV这边应该也差不多啊,一上来之后我们也是要去创建当前的这个,呃,执行环境对吧?诶,设置时间语义,设置并行度,然后读取文件,呃,读取数据,然后map成样例类,配置这个相对应的提取时间戳,设置water RA对吧?生成water RA,所以整体的流程基本上是差不多的,我们就直接把它都先copy过来啊。这里边有一个问题是大家看到这里边的这个get class,它有可能会直接get,我们那个page view的那个class对吧,这个不要引入,所以这里边我还是重新把这别的东西引入一下啊。
04:52
好,这里边我们用的是这个API scalela data stream对吧,这里边的类型是这样啊,然后前面我们把这个当前的这个环境还是先引入character引入,另外我们这里边的这个哦,这个就直接把这个下划线引入就完事了啊,方便我们后边做这个影视转换操作啊呃,前面我们这里边把这个并行度还是打开吧,因为大家知道前面我们已经做过这个呃,数据并行的优化了,那后面我们就不再详细讲了,大家知道呃,默认我们给这个并行度一方便看那个顺序嘛啊后面的话,如果需要做并行度优化调整的时候,还用那种方法展开就可以了,那既然这里边我们设置了这个全局的并行度是一,那后面干脆我也不要再去做那个,就是复杂的那个K的定义了,对吧?呃,因为你那个定义出来之后,后面我们还要再做聚合嘛,啊,所以这里边我干脆定义一个啊,UV stream。
05:52
就基于当前的data stream啊,然后呃,大家知道就在这里边我还是先要做一个future对吧,当前的behavior还是选选取这个PV行为拿出来,哎,只不过是后面我们要做一个过滤嘛啊,那接下来我就直接ten window2版了,对吧?啊,哪那么麻烦呀,因为既然我都全局并行度设了一了,方便大家看吗?那后边我们知道怎么去并行,实际应用的时候不要这么做就可以了,那现在我干脆直接time window2简单实现,而且之前我们刚好还没有用过这个time window2嘛,给大家相当于也是做一个测试啊,我们看一下time window2里边传的这个参数,跟那个普通的那个窗口定义是一模一样,那当前如果是一小时滚动窗口的话,那还是给一个二一放在这儿就完事了,对吧,我们这里边呃,直接不分组,基于data stream。
06:52
呃,开一小时滚动窗口,这是我们当前的一个开窗操作,然后后续呢,诶,这个后续我干脆就再用一个另外的一个方法,对吧?大家知道我也可以增量聚合aggregate,我定义一个增量聚合函数,然后预聚合对吧?后面如果我需要这个window,不是需要这个window信息吗?Window and的信息吗?那我再去给这里边这个少了一个U啊,那我这里边再去后边追加一个window function,把它做一个这个包装不就完事了吗?哎,这个其实还是一样的过程,那这里还是给大家测一测没有用过的一些,呃,一些这个窗口函数啊,比方说这个apply,大家知道apply里边,我们这里边呢,要传的是一个全窗口函数,就是一个所谓的window function,大家看看你假如没有语句和操作的时候,这里边的window function怎么操作的?哎,那我们自己定义一个啊,这个叫做呃,UV count result把它定义出来。
07:52
后边哎,那当然就是直接把这个UV少了一个括号是吧,把这个UVSTEM做一个print打印输出就可以了,最后不要忘记enna execute执行起来UV轴,好,那最后这里边我们要自定义实现,自定义实现啊,当前这个全窗口函数啊,全全窗口函数class UV can result extend啊然后后边我们要实现的这个东西,大家看就是一个window function对吧?啊,就是我们的这个SKYSC的treat,这个当前之之前大家已经看到过的这个东西,这里大家还要注意一下啊,还不对,为什么呢?你看我们这里,呃,首首先大家可能想到就是这里边window function的话,里边是要有四个参数input output,还有K呢,哎,那这里边我根本就没有定义K啊,这哪来的?
08:52
对呢,因为我现在做的是这个window or对吧?所以传的这个function呢,也不叫做window function,而是叫做or window function对吧?哎,我要的是这个or,不是window stream啊all window function把这个哎treat引入,然后这个大家如果点进去看它的类型的话,就少了一个K的类型,就是输入输出还有窗口类型,定义好就可以了啊,所以这里边我们的输入类型那是样例类,诶在这啊,样例类user behavior,输出的类型呢,也是样例类,诶我们不就是输入输出样例类类型吗?已经定义好了user behavior和u UV count对吧?然后最后还有一个当前的窗口类型,看window,直接把这个定义好,大家看上面就不再报错了,对吧?啊,随时随随地检查一下啊,然后后边这里边必须要实现的是一个apply方法啊,所以这里边我们先做。
09:52
一个UV驱虫啊,做一个简单的实现,那大家想一下,这里面我们要驱虫,然后我现在呢,又定义了一个这里啊,我们又定义了一个,呃,当前定义了一个全窗口函数,它这里面的input是可以拿到,我们说可以拿到当前所有数据的。
10:10
哎,所以呢,这个大家想到这还不简单吗?所有数据都拿到了,我挨个比对一下那个,呃,当前的那个uz ID啊,不就可以做去重吗?但这个有点太麻烦了吧,哎,你你直接把每个数据拿出来,然后再挨个去比对,跟跟所有的元素去做比对,看是不是重复的,然后判断它要不要啊这个就太麻烦了,大家自然就能想到有一种数据结构其实是可以直接去做去重的,那就是我们讲过的set集合类型,对吧?哎,就是在SKYSC里边不是有这个三大集合类型吗?啊,那set就是其中之一,包括这个Java里面定义的时候,Set也是三大集合类型之一,对吧?非常重要的一种集合类型,它的最大的一个特点就是里边的元素无序,而且不会重复,哎,所以我们这里边有一个非常简单的想法,就是数据都到齐了,那怎么样呢?我挨个遍历一遍,把它添加到一个set里边,那是不是它就自动去重了。
11:10
我最后不是要最后的那个countt值吗?我就直接把最后set里边的元素个数对吧?那个size直接拿出来,最后返回不就OK吗?啊所以这就是最简单的一种实现思路,所以这里给大家想写一下啊啊,就是用一个set结构来保存所有的呃,这个数据对吧?数据进行去除,自动去除,这是最简单的一种想法啊啊,那所以这里边我们就直接先定义一个set啊,定义一个set,诶那大家想到我要保存所有数据的话,这个好像没必要对吧,因为大家知道所有数据本身这个数据量就占的很大嘛,我们窗口里边经已经保存了所有数据了,你现在又专门又定义一个set,又要保存所有数据,这个尽管做了去虫是吧,但是这个也也也也也,这个很麻烦,而且你去虫的时候,并。
12:10
不是针对所有数据驱重的,对吧?我们现在是要针对什么呢?啊,针对user ID去驱重就可以了,同一个user的话驱重,因为同一个user它有可能点的是不同的这个页面啊,对吧?点的不同的商品,它不同时间戳这个数据本身是不一样的,所以我们这里面是保存所有的user ID,诶,所以接下来我们定义一个set啊,保存这个所有的user ID啊,然后这个set我们定义成类型,因为里边要动态的去添加这个数据,对吧?呃,这里边我们去定义一个I user user ID set,呃,那直接用这个skyla里边的这个set数据类型啊,里边定义出来我们既然是uz ID嘛,保存的都是uz ID,那里边是长整性,先把它创建出来,然后接下来哎,那就是便利窗口中的所有数据,所有数据。
13:10
元素对吧?啊,然后把user ID添加到set中自动去除,就只要你添加了不用做判断对吧,自己就帮我们去掉这个重复的了啊,所以接下来我们就是user,我定义一个user behavior,因为这是我们的数据对吧,每一个user behavior,在input里边的每一个user behavior,大家知道拿出来都是一个user behavior对吧?啊,那把它要添加到当前的user ID set里边,调这个加等于方法,我要的是user behavior里边的user ID,对吧?来做一个这个操作就OK了,然后最后呢,输出什么out.collect对吧?用这个collector去做一个输出,最后是要包装成一个UV count,然后当前的window end,哎,那现在这不是window全窗函数吗?Window是现成的嘛,那就是window.get end,那当前的这。
14:10
这个count的数量总数是什么呢?当然就是user ID set.size对吧?哎,直接这里边注释一下啊,将set的呃,Size作为去重后的UV值对吧?输出,哎,这就是我们整个的一个处理流程,好啊,这个过程本身还是非常简单的啊,所以我们先来测试一下,看看这个结果是不是符合预期。运行一下。我们看一下现在跑起来代码看一下运行结果,诶大家看到现在每一个窗口按照顺序输出了当前的UV统计值,之前大家还记得每一个窗口里边一个小时之内,大概那个PV值是四万多五万多对吧,而我们现在做了去重之后,效果非常明显,只剩下两万多3万多了,对吧?哎,最大的这个窗口也只有3万啊,34746啊,34700多啊,所以比之前的那个52000多就已经少了1万多的数据,接近2万的数据啊啊,这个效果还是非常的明显啊,这就是关于这个UV的一个简单实现。
我来说两句