多维分析预汇总的功能盲区
在进一步讨论如何在有限空间内实现多维分析的预汇总之前,我们有必要再了解一下预汇总方案还有什么功能上的不足,也就是要搞清还有什么查询需求很可能无法通过预汇总数据获取。
1. 非常规聚合
预汇总方案是将测度聚合值先计算好并存储起来,那么,显然,在预汇总阶段没有想到的测度聚合值就无法直接从预总汇的数据中查询出来了。比如,如果我们只存储了销售额的合计值,而没有存储最大值,那就无法直接查询出来了。
SQL 提供了五个最常见的聚合运算:计数、合计、平均、最大、最小。这几种聚合值一般都会在预汇总阶段被考虑到。但是,还有些太常规的聚合值,比如唯一计数(count distinct)、中位数、方差等,这些聚合值当然都有业务意义,也很可能被查询到,但却很容易被遗漏。这些聚合值也无法从其它聚合值简单计算出来,未被预存储时就只能临时遍历计算了。
理论上有无数中聚合运算,不可能都被想到。而聚合运算种类涉及得太多,又会使我们上次所说的容量问题变得更恶劣。
2. 组合聚合
聚合运算还可能组合。比如,原始 CUBE 的数据粒度是按天存储的(即每天有一条记录),而我们可能关心月平均销售额,这个值的意思是将每天的销售额按月合计后再求平均。它并不是单纯的合计和平均,而是两种聚合运算在不同维度层次上的组合。类似这种运算还有很多,比如月最大销售额、地区人员收入中位数的均值、…。甚至有可能出现涉及三个或更多聚合运算组合的情况。
显然,这些组合的聚合值是有业务意义的,但是常规的预汇总方案只会针对单一的聚合运算,一般不会考虑这种组合情况,也就不会事先预汇总了。即使在预汇总阶段刻意考虑了组合情况,也还会发生容量过大的问题。一个有 30 个维度的 CUBE,按五种基本聚合运算来组合,只考虑两组合的情况,理论上也会有 C(30,2)*5*4=8700 种情况,如果把这些值都预存储起来,存储空间就会再多几个数量级。
3. 条件测度
测度在统计时还可能带有条件。比如,我们想了解一下交易金额大于 100 元以上的订单销售额合计。
这个信息一般也无法在预汇总阶段处理。我们只能预汇总销售额,不能预汇总 100 元以上的销售额,除非事先想到了这种可能的查询,把大于 100 的销售额先统计出来。这相当于新造了一个衍生测度:if(销售额 >100, 销售额,0)。但是,这里的 100 经常是个参数,在交互查询阶段才临时输入进来,这就不可能事先预汇总了。
条件维度还可能出现多个关联的情况,比如统计销售量超过 10 件的那些订单的销售额。不过导致的问题和处理方案也都是一样的。
类似地,还可能有由测度临时产生的维度。比如按年龄段统计平均收入,而年龄段的划分规则是由参数传递进来的(如果是事先确定的分段规则,是真地可以先定义出一个维度出来)。
我们要再说“显然”:这种条件测度(或临时维度)的查询是有业务意义的,甚至可以说是非常常见的,而预汇总方案对此却无能为力。
条件测度还可能和组合聚合值混合出现,读者可以自行脑补一些有业务意义的例子。
4. 时间段统计
时间是多维分析中特别重要的一种维度。一般的维度只能以枚举(统计时针对某些特定的维度值)方式切片,而且时间维度却很特殊,它即可以枚举、也可以采用连续区间的方式来做切片。
我们可能关心两个起止日期之间的统计值,比如 5 月 8 日到 6 月 12 日之间的销售额合计,这个起止时间点也是查询时作为参数传递进来的,具有很强的随意性。
要再一次说“显然”了:这种统计有很强很强的业务意义;而且,也再一次说,预汇总方案对此无能为力。
按维度的预汇总,要么针对每天,要么针对每个月,都是确定的维度(或其层次),而 5 月 8 日到 6 月 12 日这种区间则不是任何维度和层次能描述的。
仔细思考一下会发现,时间段统计也可以理解成是一种条件测度,但因为它太常见、而且工程上的实现方案(以后再讲)也和条件测度不一样,所以被我们单列出来了。
时间段统计还可能有多个组合关联的情况,比如看看 5 月 8 日到 6 月 12 日间销出的、生产日期在 1 月 9 日到 2 月 17 日之间的货品总额。只考虑一个时间维度的区间,还可以一定程度地利用预汇总数据,基于中间 CUBE 去遍历聚合。但如果涉及多个时间维区间组合查询时,这个问题也会变得非常繁琐,即使有预汇总数据也还会面临多维度切片的问题,想要快速响应,不仅要冗余存储按维度预汇总的数据,还可能需要有冗余多种排序方案。这些处理方法我们在后续再仔细研究。
多维分析预汇总方案的功能盲区,不仅仅在于我们常说的明细数据,而还有很多汇总信息也无法解决。采用预汇总用空间换时间,确实能一定程度地提高性能,但只能解决一小部分最简单的需求,而且还面临存储量巨大的问题。把多维分析的效果寄希望于预汇总方案是很不靠谱的。要做好多维分析,硬遍历的功夫是基本的,即使有了预汇总数据,也要在优秀的硬遍历能力辅助下才能发挥更大的作用。