业务目标
为用户产品团队提供各个业务场景一站式分析诊断能力,驱动用户产品增长。
架构图
数据消费側
模块图
时序图
数据生产側
标签生产流程图
数据模型
基本概念
标签
从业务语义上分为用户属性和行为事件
- 用户属性:用户基本属性,如性别、年龄、居住地等。一个用户对应一个属性
- 行为事件:用户整个业务周期触发的事件,如直播间观看、点赞、下单、退货等。一个用户对应多个行为事件
从标签值上分为静态可枚举和动态不可枚举
- 静态可枚举:如性别
- 动态不可枚举:如直播间。每天都有直播间开播和关闭。
指标
从计算方式上分为原子指标和组合指标
- 原子指标:不涉及内存计算逻辑,如GMV
- 组合指标:涉及内存计算逻辑,如客单价 = GMV / 支付UV
从聚合函数上分为非去重指标和去重指标
- 非去重指标:如GMV,聚合函数为 sum
- 去重指标:如UV,聚合函数为 count distinct。实现方式如下:
- 精确值:使用bitmap计算
- 近似值:使用uniqCombined聚合函数
注:不支持使用AVG,因为AVG不支持上钻、下卷。使用 sum / count 代替。
指标预聚合表
schema
非去重指标
列名 | 角色 | 描述 |
gender | 过滤条件 | 性别 |
age | 过滤条件 | 年龄 |
…… | ||
gmv | 指标 | 支付gmv |
pay_count | 指标 | 支付人数 |
…… |
非精确去重指标
列名 | 角色 | 描述 |
object_id | 主键 | 用户id |
gender | 过滤条件 | 性别 |
age | 过滤条件 | 年龄 |
entrance | 过滤条件 | 下单入口(一个用户可以从多个入口下单) |
…… | ||
pay_count | 指标 | 支付人数 |
…… |
精确去重指标
列名 | 角色 | 描述 |
tag_code | 标签id | 标签id |
tag_value | 标签值 | 标签值 |
pay_count | bitmap | 该标签值下支付用户集合 |
…… | | |
查询模式
总览(clickhouse)
查询女性的全体GMV:
select sum(gmv)
from indicate_table
where gender = 'femail'
查询女性全体下单人数:
select
bitmapCount('(1&2)')(tag_idx, uid_bitmap) as totalCount
FROM
(
(
SELECT
toInt64(1) AS tag_idx,
uid_bitmap
FROM
indicate_distinct_table
WHERE
(
tag_code = 'gender'
and tag_value = 'femail'
)
)
UNION ALL
(
SELECT
toInt64(2) AS tag_idx,
uid_bitmap
FROM
indicate_distinct_table
WHERE
(
tag_code = 'is_buy'
and tag_value = '1'
)
)
)
洞察(clickhouse)
查询女性不同年龄下的GMV分布:
select age, sum(gmv) from indicate_table
where gender = 'femail'
group by age
order by sum(gmv) desc
码表
用来对动态不可枚举标签值进行解释说明
schema
列名 | 角色 | 是否分词 |
identity | 主体 | 否 |
name | 主体名称 | 是 |
extra_1 | 额外信息 | 否 |
…… | | |
查询模式
获取描述(elasticsearch)
根据identity获取name、extra,使用mget
获取标签值(elasticsearch)
根据name获取identity、extra,使用码表search
消费側核心设计
领域模型
标签
public class Tag {
private String tagId; // 标签id
private String name; // 标签名称,如性别
private String description; // 标签描述
private List<Entity> values; // 标签值,如男、女
private Map<String, Field> fieldMapping; // 标签映射
private Field defaultField; // 默认标签映射
private Map<String, String> externalTagIdMapping; // 外部系统标签id映射
}
public class Field {
private String db; // 库
private String table; // 表
private String column; // 列
}
其中:
- 标签映射。由于不同业务底层indicate_table不同,一个逻辑上的标签可能对应不同底表的不同字段。其中:key: db + table;
- 外部系统标签id映射。部分功能依赖外部系统,需要将tagId转换到外部系统的标签id
指标
public class Indicate {
private MathOperateEnum mathOperate; // 计算符
private List<Indicate> indicates; // 子节点
private Aggregation aggregation; // 叶子节点
}
public class Aggregation {
private Field field; // 物理存储中的某一列
private Double constant; // 常数项
private AggOperateEnum aggOperate; // 聚合函数
}
例如:
indicate:
mathOperate: MULTIPLICATION
indicates:
- mathOperate: IDENTIFY
aggregation:
aggOperate: CONSTANT
constant: 1000.0
- mathOperate: DIVISION
indicates:
- mathOperate: IDENTIFY
aggregation:
aggOperate: SUM
field:
db: 7114867950292582693
table: search_clickhouse_app_360_search_monitor_di
column: all_order_cnt
- mathOperate: IDENTIFY
aggregation:
aggOperate: SUM
field:
db: 7114867950292582693
table: search_clickhouse_app_360_search_monitor_di
column: search_pv
表示的是 1000.0 * sum(all_order_cnt) / sum(search_pv)
条件
public class Condition {
private String tagId;
private LogicOperateEnum logic; // 条件运算符,如大于、小于
private List<String> values; // 条件值
}
分组
public class Grouping {
private String tagId;
}
统一计算代理
指标拆分
对于组合指标,将其拆分至叶子结点进行查询,并在内存中进行组合,可以获得性能提升:
- 指标拆分 12s
SELECT
ecom_age,
uniqCombined(product360_guess_cube_di.pay_user_id),
uniqCombined(product360_guess_cube_di.show_user_id),
uniqCombined(product360_guess_cube_di.click_user_id),
sum(product360_guess_cube_di.guess_pay_gmv),
uniqCombined(
product360_guess_cube_di.guess_valid_visit_user_id
)
FROM
ecom_product360.product360_guess_cube_di
WHERE
(
(gender in ('female'))
AND p_date between '2022-06-30' and '2022-07-13'
)
GROUP BY
ecom_age
- 不拆分 15s
SELECT
ecom_age,
uniqCombined(product360_guess_cube_di.pay_user_id),
sum(product360_guess_cube_di.guess_pay_gmv),
sum(product360_guess_cube_di.guess_pay_gmv) / uniqCombined(product360_guess_cube_di.pay_user_id),
uniqCombined(product360_guess_cube_di.guess_valid_visit_user_id),
uniqCombined(product360_guess_cube_di.show_user_id),
uniqCombined(product360_guess_cube_di.click_user_id) / uniqCombined(product360_guess_cube_di.show_user_id),
uniqCombined(product360_guess_cube_di.guess_pay_gmv) / uniqCombined(product360_guess_cube_di.show_user_id)
FROM
ecom_product360.product360_guess_cube_di
WHERE
(
(gender in ('female'))
AND p_date between '2022-06-30' and '2022-07-13'
)
GROUP BY
ecom_age
标签预测
有时,一次查询的指标会存在于多张表,或不同的数据服务(如领航者、DMP);同时,我们还需要从标签的fieldMapping中,找出对应的物理存储列。这些数据都可以根据指标推导得出。
- 将拆分后的指标,根据数据服务、表名进行聚合。
- 对于每组聚合后的指标,根据库表名预测条件与分组中的tagId对应的field
- 异步查询所有分组的数据
- 汇总查询结果
内存计算
对于组合指标,支持 +、-、*、/、常数 五种计算模式,可任意配置指标计算。
配置网关
条件
public class ConditionConfig {
private String id; // 配置id
private List<String> tagIds; // 标签id
private Map<String, List<String>> defaultTags; // 默认选中项
}
分组
public class GroupingConfig {
private String id; // 配置id
private List<String> tagIds; // 标签id
private List<String> defaultTags; // 默认选中项
}
指标
public class IndicateConfig {
private String id; // 配置id
private List<IndicateTreeNode> indicates; // 指标树
}
public class IndicateTreeNode {
private Boolean isLeaf; // 是否叶子结点。
private String name; // 节点名称
private String description; // 描述
private String code; // 节点编码
private Indicate indicate; // 指标,isLeaf = true
private IndicateUnitTypeEnum unit; // 指标单位,isLeaf = true
private List<IndicateTreeNode> children; // 子节点,isLeaf = false
private Boolean isDefault; // 默认选中项
}
码表
public class DictConfig {
private String dictId; // 码表id
private List<Entity> fields; // schema
}
数据查询网关
为每个模块定义一个公用的请求结构体,模块化前端组件
卡片+趋势图
ppublic class CardLineChart {
private Map<String, UnitEnum> unit; // y轴单位
private List<Card> dataHeader; // 表头
private List<GraphX> dataResult; // 数据
private List<String> groupSelected; // 选中的组
private String title; // 标题
}
public class Card {
private String indexDisplay; // 指标名称
private String indexName; // 指标编码
private String indexTip; // 提示
private Value value; // 值
private Value extraValue; // 额外值
}
public class Value {
private Double value; // 值
private UnitEnum unit; // 单位
}
public class GraphX {
private String horizontal; // 指标x轴值
private List<LinkedHashMap<String, Double>> y; // 指标y轴值, 每个y轴为一个map, 同一个y轴共用一个map
}
散点图
public class ScatterPlot {
private Map<String, UnitEnum> unit; // 单位
private String horizontalName; // x轴描述
private String verticalName; // y轴描述
private String horizontalCode; // x轴code
private String verticalCode; // y轴code
private List<Point> dataResult; // 数据
private List<PointMeta> dataHeader; // 数据头
private LinkedHashMap<String, String> verticalHeader; // 属性code映射
private Map<String, Map<String, String>> verticalValueHeader; // 属性值映射
private String title; // 标题
}
public class Point {
private Double horizontal; // x轴
private Double vertical; // y轴
private Map<String, String> attr; // 属性
private String displayName; // 分组
}
public class PointMeta {
private String displayName; // 分组
private Boolean active; // 是否默认展示
}
折线图 / 柱状图
public class LineChart {
private Map<String, UnitEnum> unit; // y轴单位
private Map<String, String> dataHeader; // 表头
private List<GraphX> dataResult; // 数据
private String title; // 标题
}
public class GraphX {
private String horizontal; // 指标x轴值
private List<LinkedHashMap<String, Double>> y; // 指标y轴值, 每个y轴为一个map, 同一个y轴共用一个map
}
表
public class Table {
private Map<String, UnitEnum> unit; // 指标单位
private LinkedHashMap<String, String> dataHeader; // 表头
private List<TableL> dataResult; // 数据
private Map<String, OrderTypeEnum> order;// 排序标记
private PageResult pageResult; // 分页
private String title; // 标题
}
public class TableL {
private Map<String, String> identity; // 主列数据
private Map<String, Double> indicate; // 指标值, 每个y轴为一个map, 同一个y轴共用一个map
private Integer index; // 排名
private String uniqKey; // 唯一主键
private Map<String, String> conditions; // 该条记录的标签条件
private Boolean hasChildren = false;
private Integer level = 0; // 当前层级,从0开始
}
public class PageResult {
private Integer pageNo; // 当前页
private Integer pageSize; // 每页大小
private Integer total; // 数据总条数
}