我们在《多维分析预汇总的存储容量》中计算过,如果想做到 O(1) 的复杂度,至少要考虑界面用到的各种维度组合,这在维度总量稍多一点时就不可行了。
这样,我们就只能放弃 O(1) 复杂度的期望了,不把每种可能出现的维度组合都预汇总出来,只能预汇总部分维度组合。在查询时,对于已经有预汇总的数据则可以直接返回,而如果碰到没有预汇总的维度组合时,则仍然从原始 CUBE 遍历聚合出来,这时的计算复杂度要么 O(1) 要么 O(n)。
还可以做得更聪明一点:从某个已有的中间 CUBE 聚合。比如,如果保存了维度组合 [A,B,C] 的预汇总数据,那么维度组合 [A,B] 或[B,C]的查询就可以从这个中间 CUBE 再聚合出来了,而不必从原始 CUBE 聚合,计算量将会大大降低。有时可能会有多个中间 CUBE 都能聚合出目标查询,比如组合 [A,B,C] 和[B,C,D]都可以再聚合出组合[B,C],这时优先选择数据量较小的那个中间 CUBE 即可。
那么,我们怎么知道在初始状态时该预先汇总哪些组合呢?
可以动态生成这些组合。在查询时,无法从现有中间 CUBE 聚合出来的组合只能从原始 CUBE 聚合,我们可以在聚合完成后将结果保存起来作为一个新的中间 CUBE。发现新组合时第一次访问会有延迟感,以后基于这个组合的查询或者可由该组合聚合出来的查询就都可以较快返回了。
其实,也不是只要能从现有中间 CUBE 聚合出来的组合就总是临时聚合。多维分析性能优化的目标是前端反应速度,如果中间 CUBE 仍然很大,那么再聚合也会比较慢,这时候,这些再聚合的结果也可以作为一些新的中间 CUBE 保存起来。
另外,在过程中我们还可以记录每个中间 CUBE 的使用频率,在空间总量限制下,删除那些使用率较低的中间 CUBE,从而更有效地利用有限的空间。
经过这些处理后,我们虽然无法完全做到 O(1) 复杂度,但常常也能把计算性能从全量硬遍历提高几十倍甚至上百倍,这对于大多数多维分析场景已经足够了。
我们还在《多维分析预汇总的功能盲区》中说过几种情况无法通过预汇总来提高性能。其中非常规聚合和组合聚合本质上仍然是个数据量的问题,而对于临时产生的条件测度和时间段统计,就不是数据量的问题了,我们无法预测用户使用时才输入的参数,也就不可能把所有参数对应的数据都事先预汇总出来。
理论上还可以使用上面的办法:碰到新的参数就计算并保存下来。但与维度组合不同的是,测度参数常常是连续量,其取值及组合情况不可枚举,重复利用的可能性不大。
预汇总对于条件测度确实难有好的效果,不过,对于时间段统计,还是有点招的。我们可以将数据按更高的时间维度层次预汇总,在查询时就可以减少遍历计算量。
假如原始 CUBE 是按日存储的数据,那么我们可以按月把数据先做好汇总成中间 CUBE,当需要针对一个时间段统计时,可以将时间段跨过的整月数据从中间 CUBE 中遍历,再加上时间段两头那两段不构成整月的日期的数据,即可以获得查询目标。这样,可以将长时间段统计的计算量减少十倍甚至更多。
比如,我们要查询 1 月 22 日到 9 月 8 日区间的某种统计值,而我们事先已经按月做过预汇总。那么我们只要基于预汇总数据计算 2 月到 8 月的聚合值,再使用原始 CUBE 计算 1 月 22 日到 1 月 31 日和 9 月 1 日到 9 月 8 日的聚合值,涉及的计算量是 7(2 月 -8 月)+10(1 月 22 日 -1 月 31 日)+8(9 月 1 日 -9 月 8 日)=25,而如果使用原始数据聚合,其计算量是 223(从 1 月 22 日到 9 月 8 日的天数),几乎减少了 10 倍。