背景

当某个app的DAU呈45°上扬,从百万DAU,到千万DAU,甚至过亿DAU,无论是组织架构,还是:经典的db +
缓存策略,会变得越来越无法灵活面对纷繁复杂的业务场景。具体表现如下:

专业人做专业事

任何一个团队的成员构成比例,既有资深的,也有新人。一般情况下,都是让新人介入实际业务需求,那么,该新人就得在熟悉业务的前提下,也需熟悉目前的:db +
缓存策略。那么,可否让新人刚开始只关注于:业务。等业务熟悉以后,再去关注:db + 缓存策略呢?

成本优化

过五百万dau以后的业务,功能也是非常多的,此时,db +
cache策略一般都还在业务代码中,面对如此境遇,单靠2、3个所谓的架构师,想要在确保当前功能不变的情况下,做到:性能优化 or
成本优化,其实,还是有很大的挑战的。

多语言下中间件的困境

在很多公司的后端工程领域,都有3种以上的语言(例如:php、java、go)
,每种中间件都必须提供3种语言的sdk版本,这给中间件带来一定的维护成本,也给业务方的学习成本、升级成本。以redis为例,虽然redis提供了相当丰富的功能,但是,站在业务方的角度上说,它需要关心:redis的key是怎么设计的、redis的缓存策略、redis客户端怎么用、对应的sdk使用姿势、实际业务如何适配等等,中间件团队能否提供更多、更丰富的基础的服务,业务方,只需要往这个服务喂数据,类比于:service
mesh,通过sidecar的方式(与应用层解耦,单独发布),解决业务层:服务注册 + 服务治理 + 网络通讯
负载均衡等问题。而:数据集市,是通过独立公共服务,解决的业务层的数据存取的问题.

功能设计

数据集市:将部分特定的、有规律的数据存取,通过指定的方案来解,期许达到:成本 +
性能的最大收益化。再次强调下:沉淀到数据集市的数据,100%是有规律的。接入时,一定得先分析数据,看是否有规律,若一时半会看不出规略,则业务方先采用通用的解决方案来处理。例如:数据集市,将处理以下有规律的数据:

类别 特点 具体场景 备注
必中类 根据key,必有数据,数据变化频率非常小 例如:用户类数据,用户性别、用户昵称、用户备注,通过通用的缓存策略来做,有点:大材小用
空查类 空查率比较高 例如:某个feed,该用户是否点赞过?可构建布隆索引,优化mc的存储量,减轻对mc的访问量
宽表类 kv类,非关系型数据,数据格式杂 例如:媒体库,它关心媒体的相关数据,例如:宽、高、url地址、类别、来源等,但是,像:同款效果、修图配方等,它跟媒体有关,但是,又不太相关。
累计类 对过去一段时间,做累计:count、sum、max、min、distinct、top n 等 统计类,月账、天账、时账、秒账等,提供基于时间窗的查询,例如:过去7day,有多少人给这条feed点赞 假设:每天的明细账上亿,那么,直接过去7day,直接:select count(*),无论是哪款数据库,直接挂了,如果是去重,那就问题更大了
快照类 日志类数据,提供帮助、协助定位问题,可牺牲的,提供临时查询的 例如:dag执行过程中的任务事件表,规则引擎的路径分析、马甲的触发任务信息 mysql的ab库,或者:hbase中的ttl,基于成本考量,定时删除。

站在业务方的视角

站在业务方的角度上说:数据集市,可以简单如此理解:将工程里的db、cache,部分场景抽象出一个通用数据服务来,让业务方无需关心:数据量大、查询量大的双重问题,专注于业务逻辑,而不需要关心以下事情:
a、是否使用关系型数据库,例如:mysql,还是非关系型数据库,例如:hbase,
b、假设是mysql,是否需分库分表?
c、缓存策略是什么?淘汰策略是什么?等
d、是否需要使用:布隆索引?
再次强调下:数据集市,不是为了替换掉业务的db + cache,而是将特定规律的数据存取通过特定的方案来解,期许达到:成本、性能的最大收益化。

站在中间件的角度

站在中间件的角度再次理解下:数据集市=中间件+服务化+部分业务。例如:dbproxy的作用:让业务方无需面对分库分表,而数据集市,就是让业务方无需面对分库分表 +
缓存策略。

模块划分

类别 作用 包括概念 备注
数据接入 将数据标准化接入 数据源(DataSource)、数据列(DataColumn)、数据主键(DataKey)、接入码:AccessCode 注意:这里面的概念,是针对于:设计数据集市的人而言的,并不是:针对于业务方的。通过http接口 或者:kafka消息,能够快速接入数据。
数据取 根据业务方的具体需求,提供它想要的数据视图 取数器(DataPicker)、数据点(DataPoint)、数据集(DataSet)、数据流(DataFlow)、算子(DataFn)、缓存策略、布隆索引策略 这里面的概念,是针对于:设计数据集市的人而言的,并不是:针对于业务方的,业务方只管取.
数据计算 帮忙业务方解决:重复调用、计算耗时长等问题 预计算、合并计算、单个计算、批计算、执行计划、缓存策略、布隆索引策略 业务方只管获取数据。
数据存储 冷热存储、mysql or hbase、过期策略 存储介质的选择、冷热数据存储策略、分库分表、过期策略 业务方无需关心

基础概念

取数器:DataPicker

某种数据的获取方式或者动作,可以通过groovy来定义,也可以直接使用java动态生成,重点在于:它可动态生成,无需改动代码。

数据点:DataPoint

具有业务含义的数据,代表具体某个业务的某个值,例如:用户性别,用户年龄,来源媒体宽等。它跟取数器的关系:一(取数器)对多(数据点),任何一个数据点有且只能对应一个取数器,而一个取数器能够产生多个数据点。数据点有一属性,叫类别,就是为了描述之前提到的:名单类,可信类,宽表类等,因此,计算数据点过程中,需根据类别,做不同的方式的计算。
数据点有两种计算方式:预计算 跟 合并计算。
具体而言:假设一个业务处理过程,需调外部服务(s1、s2、s3、s5)来获取数据:其中,s3是耗时比较长的,而s1需要调用两次(
并非开发人员故意造成的,而是业务不熟悉 + 代码链路太长,不清楚有这个重复调用)。

,那么,如何减少重复计算 + 加速呢?答案是:预计算 + 合并计算。

其中,预先计算:数据点1跟数据点3,而数据点3对应的s3服务,本身耗时比较长,由于提前异步线程:预先计算,因此,当真正业务需要使用到时,它可能已经计算完毕或者业务需要等待的时间比较短,从而缩短耗时。

数据集:DataSet

面向业务方的数据集,它由1~n个数据点的值构成。数据集跟数据点的关系:是多对多,任何一个数据集由多个数据点组成,而任意一个数据点也能被多个数据集引用。

算子:DataFn

对ds的变换操作,称做算子,它包含两类:map型算子,join算子,其中,join型算子,它关注的是:多个ds,聚合成一个新的ds,例如:left
join算子,right join算子、inner join 算子等,而map型算子,它关注的是:一个ds变成另外一个ds,例如:flat map算子,map算子等。

数据流:DataFlow

任何一个纷繁复杂的业务,都可以组成一个dag,它由ds跟算子构成一个DAG,那么,基于业务配置的DAG,可以做以下事情:dag拆分、剪枝、dag引擎执行计划优化等。