一:均衡层次结构和非均衡层次结构
先来看看均衡层次结构和非均衡层次结构的定义吧。
- 均衡层次结构:纬度有多个递归层次,比如说商品的类目级别有:叶子类目,五级类目,四级类目,三级类目,二级类目,一级类目,有固定的层级,这种称之为均衡层次结构。
- 非均衡层次结构:与均衡层次结构类似,但是有不固定的层级。
关于这种存在均衡层次结构和非均衡层次结构的纬度属性在平时的需求计算会遇到什么挑战?举个例子,我现在需要统计一级类目下所有子类目的gmv(可以简单理解为销售额),这个该咋统计呢,是不是觉得有点麻烦,因为现在整个商品类目结构是树状的,需要递归遍历出每个父类目下的子类目,直到是叶子类目,并且要将所有遍历出来的类目和销售流水表关联,这样才能得到这个指标。目前oracle是可以实现这样的递归遍历的,其它数据库针对这种固定层次问题也可以通过join,嵌套等方式也可以做到,但终归来说太复杂,太麻烦了,有违我们设计纬度表但初衷。如果换成非均衡层次结构那就更麻烦了,所以这里探究一下,针对这种情况,我们的纬度表该如何设计才好?
扁平化解决均衡层次结构方式:
首先先说有固定层级的这种,我们一般可以在做ETL的时候,就把所有叶子类目就给遍历出来,放到一张类目表里面去,专业来讲就是扁平化,如果觉得用sql比较难加工,可以coding,还是挺简单。大概加工出来就是这样的格式(需要仔细理解一下这个表的含义):
类目id | 类目级别 | 是否为叶子节点 | 一级类目 | 二级类目 | 三级类目 |
---|---|---|---|---|---|
21 | 1 | N | 21 | null | null |
50026576 | 2 | N | 21 | 50026576 | null |
50026579 | 3 | Y | 21 | 50026576 | 50026579 |
121456022 | 2 | Y | 21 | 121456022 | null |
... | ... | ... | ... | ... | ... |
图10.1
现实情况可能存在不是所有类目都刚好有五级,不足的就补空。表格加工成这样,计算一级类目是21的下所有子类目的gmv其实就很简单了,只要拿这张类目表的类目id和销售流水表的类目id进行关联,并筛选一级类目为21,在加上聚合函数就可以得到结果。
之前那个问题,其实是一个下钻的问题,是可以完美解决的,但是针对上钻的问题却还有点问题,比如说我现在要统计三级类目为121456022以及其父类目的gmv,根据上述表格的描述,并不存在三级类目为121456022的数据节点,这样在实际统计的时候就会遗漏掉这条数据,但其实这条数据的二级类目就是
121456022,应该是符合我们需求的,针对这种情况,我们可以这么处理。
类目id | 类目级别 | 是否为叶子节点 | 一级类目 | 二级类目 | 三级类目 |
---|---|---|---|---|---|
21 | 1 | N | 21 | 21 | 21 |
50026576 | 2 | N | 21 | 50026576 | 50026576 |
50026579 | 3 | Y | 21 | 50026576 | 50026579 |
121456022 | 2 | Y | 21 | 121456022 | 121456022 |
... | ... | ... | ... | ... | ... |
图10.2
可以看出图10.2和10.1的区别,其实就是将为空的数据进行一个回填,这样就可以避免之前出现的问题。
扁平化处理非均衡层次结构方式:
扁平化的处理可以解决包含固定层级的类目,对于非均衡层次结构,可以采用预留级别的方式来解决,预留级别这个纬度属性就存一个json,但是这样的扩张性就较差。比如说这样的结构:
类目id | 类目级别 | 是否为叶子节点 | 一级类目 | 二级类目 | 三级类目 | 其它类目 |
---|
采用桥接表的方式处理均衡层次结构/非均衡层次结构:
使用桥接表的方式更加的灵活,可以同时解决上钻,下钻,回填等问题,下面就解释一下桥接表是如何实现的,见下图:
引用于《大数据之路》
稍加解释,图中的类目表就是我们10.2提到的表,交易事实表就是一张流水表,里面记录了每个商品的交易记录。类目桥接表是之前没有提到过的,它的格式大概是这样的(需要好好理解一下):
父类目id | 子类目id | 类目层级间隔 |
---|---|---|
21 | 21 | 0 |
21 | 50026576 | 1 |
21 | 121456022 | 1 |
21 | 50026579 | 2 |
50026576 | 50026576 | 0 |
50026576 | 50026579 | 1 |
121456022 | 121456022 | 0 |
几张表格已经介绍完毕了,那么可下来来说明一下,用桥接表如何实现上钻和下钻,先解释一下下钻,步骤是先拿类目表的类目id和类目桥接表的父类目id进行关联,然后用类目桥接表的子类目id和交易事实表进行关联,就可以实现我们的需求来。在来解释一下上钻,步骤是拿类目表的类目id和桥接类目表的子类目id进行关联,然后拿父类目id和交易事实表的类目id进行关联,这样就实现来上钻的需求。是不是很简单,但是存在双重join,桥接表加工复杂等原因,这种方式其实使用起来更加麻烦。
二:事实衍生纬度
事实衍生纬度是一种特殊的纬度,为什么是事实衍生,因为是从事实表当中的度量值产生的,比如说销售额,如果把它当作一个数值,用来做累计,平均等操作,那么它就是度量值,如果分组离散,那么其就可以当作纬度属性用做来筛选,所以关键要看如何使用,要记住纬度属性的定义是:描述事实表中度量环境的值,就是属性。一般存在以下几种事实衍生纬度:
- 另一个维度的过去行为:
如买家最近一次访问的时间、买家最近一次发生交易的时间等。 - 快照事实行为维度:
如买家从年初截至当前的交易金额、买家信用分值、卖家信用分值等 - 分组事实行为维度:
将数值型事实转换为枚举值。如买家从年初截至当前的交易金额按照金额划分的等级、买家信用分值按照分数划分得到的信用等级等。 - 复杂逻辑事实行为维度:
通过复杂算法加工或多个事实综合加工得到。如前面提到的卖家主营类目,商品热度根据访问、收藏、 加入购物车、交易等情况综合计算得到。
实施的原则:
1.避免维度过快增长。比如对商品表进行了极限存储,如果将 商品热度加入现有的商品维表中,则可能会使每日商品变更占比过高, 从而导致极限存储效果较差。
2.避免耦合度过高。比如卖家主营类目,加工逻辑异常复杂, 如果融合进现有的卖家维表中,那么过多的业务耦合会导致卖家维表刷 新逻辑复杂、维护性差、产出延迟等。
三:多值纬度处理
什么是多值纬度,比如说一个订单里面有包含多个商品,每个商品在商品纬度表都有对应的一条记录,那么这样就存在一对多的关系。多值纬度带来的直接影响就是无法和纬度表关联,无法进行更细粒度的筛选。一般处理方式有如下几种:
- 降低事实表的粒度,订单表进行拆单,让每个子订单只对应一个商品。但是实际情况往往不可使。
- 使用多字段进行保存,但是考虑到扩展性,会保留一个预留字段,如果多值纬度有固定的数量,那就不需要预留字段了。
- 使用桥接表,这种方式会更加灵活一点,简单举个例子,还是订单表,在不进行拆单的情况下,一个订单会对应多个商品,订单号是primary key,我们可以这样处理,新建一张桥接表,这张表是订单号+商品号才唯一的,还剩下一张表就是商品的纬度表,在使用的时候,可以用订单表的订单号关联桥接表的订单号,在用桥接表的商品号关联商品纬度表,这样也可以达到我们的目的,但是由于订单和商品是一对多关系,会存在join放大记录的情况,如果现在要统计该订单的销售额,其实就是被放大的。但是这个也有可能是被允许的,要根据具体的业务需求。
四:多值属性处理
什么是多值属性,在纬度表当中某个字段存在多个属性值,就称之为多值属性,这种情况在实际应用当中还是很常见的,比如说一个商品对应多个sku_id,多个属性,多个标签等。一般可以这样处理:
- 第一种方式就是保持纬度主键不变,将多值属性放在一个字段里面,以json格式存储。
- 第二种方式也是保持纬度主键不变,将多值属性放在多个字段里面,如果不确定层级就增加一个预留字段,但是这样扩展性就较差。
- 第三种方式就是改变纬度主键,保存多条记录,比如说一个商品存在多个sku_id与之对应,那么每个商品的每个sku_id都产生一条记录,主键就用商品id+sku_id,但是这种方式要考虑数据量会不会剧烈膨胀。