【产品360数据分析系统】整体方案

业务目标

为用户产品团队提供各个业务场景一站式分析诊断能力,驱动用户产品增长。

架构图

数据消费側

模块图

产品360模块图

时序图

产品360时序图

数据生产側

标签生产流程图

标签生产流程图

数据模型

基本概念

标签

从业务语义上分为用户属性和行为事件

  • 用户属性:用户基本属性,如性别、年龄、居住地等。一个用户对应一个属性
  • 行为事件:用户整个业务周期触发的事件,如直播间观看、点赞、下单、退货等。一个用户对应多个行为事件

从标签值上分为静态可枚举和动态不可枚举

  • 静态可枚举:如性别
  • 动态不可枚举:如直播间。每天都有直播间开播和关闭。

指标

从计算方式上分为原子指标和组合指标

  • 原子指标:不涉及内存计算逻辑,如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_countbitmap该标签值下支付用户集合
……
精确去重指标表

查询模式

总览(clickhouse)
总览tab

查询女性的全体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)
洞察tab

查询女性不同年龄下的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;                      // 列
}

其中:

  1. 标签映射。由于不同业务底层indicate_table不同,一个逻辑上的标签可能对应不同底表的不同字段。其中:key: db + table;
  2. 外部系统标签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中,找出对应的物理存储列。这些数据都可以根据指标推导得出。

  1. 将拆分后的指标,根据数据服务、表名进行聚合。
  2. 对于每组聚合后的指标,根据库表名预测条件与分组中的tagId对应的field
  3. 异步查询所有分组的数据
  4. 汇总查询结果

内存计算

对于组合指标,支持 +、-、*、/、常数 五种计算模式,可任意配置指标计算。

配置网关

条件

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;                      // 数据总条数
}
上一篇
下一篇