之前在Redis上构建了基于Bitmap索引的存储结构,在Openresty上实现了一个简易的查询引擎,

这里再思考一下数据的来源、采集、实时接入等问题。

日活数据的来源

日活数据最大的可能来源就是浏览日志、登录日志等,数据的内容一般包括时间戳、UID、PageId等,

一般网站或者App统计日活是需要区分模块和功能的,类似友盟、TalkingData等公司的产品,以一个

SDK或者Agent的方式采集和发送数据。

数据接收

客户端采集到的数据会定期发送回来,接收端并没有复杂的业务处理逻辑,基本上就是format。

当接收端收到数据做了基本上的合法性校验之后,可以选择写日志或者发送到队列中(比如Kafka)。

Openresty加Lua可以完成这部分的工作,性能会非常强悍,我比较倾向直接写本地Log,

然后批量发送至Kafka。

关于维度信息的计算

因为我们是要做维度数据统计的,所以在数据处理过程中就需要为访问日志数据添加维度的信息。

维度信息大体可以分为两大类,一类是固定维度,一类是变动维度。固定维度就类似性别、年龄等,

变动维度可以是所在的地理位置等信息。维度的数据经常是需要进行编码的,就是将string转id。

客户端每次上传的数据中是冗余维度信息,还是在流计算中去获取转化丰富数据的维度,这个逻辑

通常就是getOrCreate,如果是有一个Mysql的表来存储这种数据,再加一个Redis的Cache,这种

中心化的设计在数据流量大的时候必然是瓶颈,所以要适当考虑在客户端做一些工作。比如,在客户

端启动时,进行一些基本维度的转化工作,并且将这些转化的数据进行保存,在定期提交数据时,

主动将数据的string转id的工作在客户端完成。当然这样的分散逻辑也导致了维护成本,当你需要进行

一些数据的整理和升级时,就会要跟客户端打交道了,需要设计一下数据的Version和避免过于集中的

数据更新导致后端压力过大。

关于时间窗口与聚合

无论你选择了哪一种流计算框架,都会提供一个Timewindow的功能,提供基本的Map、Reduce功能。

如果我们的浏览访问数据存在极大的聚合利益,可以压缩几倍或者十几倍,就值得一做。

如果考虑到数据的修复和数据Delay等问题,一定要设计一个增量补全的逻辑,比如有put和incr操作。

之前看过一些文章有在Hbase上加协处理器来完成的,当然我们的Redis+Lua也是可以的。

数据的输出结果尽量不要直接写存储,可以再打回Kafka中,做多订阅的处理。Kafka的客户端可以把

单条输出转化为批量,这种设计比较容易规避流计算的一些迭代输出单条的麻烦。

维度数据的存储

每个用户的每次访问都带有20个维度信息,这种沉重的冗余信息会让你的存储不堪重负的。

因为我们的目标是日活,也就是以用户为主体。当某个用户已经被处理过一次了,那么他的固定维度

数据最好就不用再处理了,每次只处理变动信息就好了,表的行数相对比较稳定,跟总用户数有关。

如果只处理日活相对比较简单,如果想要按照时间做区分,比如每小时一个列,相当于行转列。

UID Gender Age 00:00 01:00 02:00 03:00
1 F 12 1 1 1 1
2 M 13 0 0 0 0
3 F 10 1 1 1 1
4 M 19 0 0 0 0

水平扩展

如果单机Redis不能存下所有的数据,就需要有一个分布式的方案,因为我们的计算是列级别的,

所以同一个Uid的所有列必须在一台Redis实例中,于是我们的分布式方案只能按照Uid来分了。

因为我们的UID是使用16K、64K的Block块来划分的,所以我们只要对Block块进行离散就好了。

计算引擎的改动也会很小,这里就是一个简单的MapReduce的过程。