便利店老板小王有六家分店,每天卖出上百种商品。他总被合伙人追问:“上个月A店的可乐到底赚了多少?”每次都得翻厚厚一摞流水账,翻到天黑才能报出一个数。后来他想了个妙招:把账本做成一个“魔方”——一个轴是时间(哪一天卖的),一个轴是门店(哪家店卖的),一个轴是商品(卖的是什么)。魔方里的每一个小格子,就填上对应的销售额。再有人问“1月A店卖了多少可乐”,直接找到那个格子,一秒钟读出答案。这个“魔方”就是OLAP Cube,这种从任意角度快速翻找答案的本事,就是多维分析。可魔方越做越大,全是空格子太浪费;天天重新拼魔方也太慢。于是小王学会了只存有数据的格子、每天打烊后增量更新,还给高频问题提前抄“答案小抄”。这篇文章,就带你把这座魔方账本从搭建到提速完完整整走一遍。

岗位关联

掌握OLAP Cube的建模与聚合取舍,是BI工程师与数据分析师搭建秒级响应报表体系的看家本领,也是理解Kylin、Doris等分析引擎底层加速思路的基石。

目录
  1. 可视化演示
  2. 核心概念
  3. 多维建模
  4. 多维操作
  5. 存储方案
  6. 稀疏处理
  7. 增量计算
  8. 性能优化
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

核心概念

小王的第一版魔方只有三个轴:时间、门店、商品。每个轴上都有刻度——时间的刻度是“1月、2月、3月”,门店的刻度是“A店、B店……”,商品的刻度是“可乐、薯片……”。这些刻度在数据分析里叫维度成员,而轴本身叫维度。三个刻度一交叉,就定位到魔方里的一个小格子,格子填的数字就是度量——比如销售额。整个魔方,就是Cube。

技术语言里,Cube不是一个真的立方体,而是一种“多维数据模型”:它用多个维度作为坐标轴,把所有可能的维度成员组合都映射到一个度量值上。维度一般来自业务分析视角,比如时间维度、地理维度、产品维度;度量则是你关心的数值,比如销售额、订单数、库存量。维度成员的一个组合,比如(1月,A店,可乐),就唯一确定一个单元格,里面存着对应的聚合结果。

理解了这三个概念,你就掌握了魔方的“骨架”:轴是维度,刻度是成员,格子是单元格,格子里的数字是度量。后面所有上卷、下钻、预计算,都是在这个骨架上做文章。

Cube=D时间×D门店×D商品→度量值\text{Cube} = D_{\text{时间}} \times D_{\text{门店}} \times D_{\text{商品}} \to \text{度量值}
这个公式意思是:Cube 就是把“时间的所有取值”和“门店的所有取值”和“商品的所有取值”做组合,每一种组合对应一个存数字的格子。
  1. 维度集合每一个维度都是一串成员的有序集合,相当于魔方的一根轴上的所有刻度。
    D时间={1月,2月,3月,… }D_{\text{时间}} = \{1月, 2月, 3月, \dots\}
  2. 笛卡尔积把各维度成员全配对,产生所有可能的坐标组合,这就是“有多少个格子”的总数来源。
    D时间×D门店×D商品D_{\text{时间}} \times D_{\text{门店}} \times D_{\text{商品}}
  3. 度量映射每个坐标组合对应一个数值,即该组合下的聚合度量(如销售额合计)。
    →度量值\to \text{度量值}

多维建模

要搭魔方,小王得有原材料:一摞原始流水账,上面逐行记录每一笔销售的日期、门店、商品、金额。这张巨长的流水表,在数仓里叫事实表,它存的是业务发生的明细事件,里面主要两类字段:外键(指向维度表的编号)和度量(金额、数量)。另外,小王还整理了几本“资料册”:商品目录(商品ID→品类、包装规格)、门店名册(门店ID→城市、面积)。这些资料册就是维度表,负责描述每个维度的成员长什么样。

事实表和维度表怎么摆?最常见的是星型模式:事实表在中间,维度表像星星一样围在四周,每张维度表通过主外键与事实表相连。优点是结构简单、查询时表连接次数少。如果小王把“商品目录”再拆成“商品表+品类表”两级,维度表也被规范化拆细了,就成了雪花模式。雪花模式更省存储,但查询时要多连几张表,分析变慢、建模变繁琐。

对小王的账房来说,星型模式就是“流水账+几本资料册”分开放,查询时翻资料册补全信息。先把这层地基打牢,魔方才能搭得稳。

事实表⋈维度表1⋈维度表2⋈⋯\text{事实表} \bowtie \text{维度表}_1 \bowtie \text{维度表}_2 \bowtie \cdots
公式里的"⋈"是关系代数的自然连接,表示事实表按外键把各张维度表的描述信息拼接在一起,拼完就能得到任意维度组合下的明细宽表。
  1. 事实表本体它是整个多维分析的中心,每一行是一次业务事件。
    事实表(时间ID, 门店ID, 商品ID, 金额)\text{事实表(时间ID, 门店ID, 商品ID, 金额)}
  2. 外键连接事实表的维度ID与维度表主键相等,于是通过连接操作把维度属性带进来。
    事实表.门店ID=门店表.门店ID\text{事实表.门店ID} = \text{门店表.门店ID}
  3. 多表自然连接多张维度表依次连接后,明细行就携带了全部维度描述,Cube即可由此构建。
    事实表⋈商品表⋈门店表⋈时间表\text{事实表} \bowtie \text{商品表} \bowtie \text{门店表} \bowtie \text{时间表}

多维操作

魔方搭好后,小王最常用的动作有三个:拧、切、转。上卷是把粒度变粗:原来按天看销售额,现在按季度看,就是沿时间维度的层级从“天”爬到“季”,把每天的格子合并成一个季度的格子,度量值全部相加。下钻正好相反,从季度展开到每一天,粒度越来越细。切片是固定一个维度的一个成员,比如只看“A店”这一个截面,魔方瞬间被切掉一大块,剩下一张二维表。切块则是固定多个维度的某段范围,比如“第一季度+A店+饮料类”,相当于从魔方里挖出一小块。旋转就是换个视角看同一组数据,把维度的行列位置交换,相当于把魔方转个方向。

上卷和下钻就像望远镜的变焦:拉远看全局趋势,拉近看细节异常。切片切块则是“聚焦”:只看关心的那部分,其余视线全部略去。这些操作组合起来,让小王能从“全部门店全年总销量”一路追查到“某店某天某商品的单笔销售”。

vg=∑i∈gviv_{g} = \sum_{i \in g} v_{i}
这个式子是上卷的本质:把细粒度组 $g$ 里的所有单元格值相加,得到粗粒度汇总值 $v_g$。
  1. 细粒度组 $g$上卷前属于同一个粗粒度分组的那些明细单元格。
    g={1月1日,1月2日,1月3日}g = \{\text{1月1日}, \text{1月2日}, \text{1月3日}\}
  2. 逐项累加把组内每个单元格的度量值都加起来。
    ∑i∈gvi\sum_{i \in g} v_{i}
  3. 汇总结果累加得到粗粒度单元格的新值。
    vg=v1月1日+v1月2日+v1月3日v_{g} = v_{\text{1月1日}} + v_{\text{1月2日}} + v_{\text{1月3日}}

存储方案

魔方有了,怎么“存”又让小王犯了难。他有两个选择:一是把所有可能被问到的问题答案全部提前算好,写进一本本“答案册”,问哪个翻哪页——这叫MOLAP(多维联机分析处理),存的是预计算的Cube。二是啥也不提前算,店里放着一摞原始明细账,每次被问到就现场翻账本加总——这叫ROLAP(关系联机分析处理),直接用SQL去查关系型明细表。

MOLAP 的好处是快,查询命中预计算的聚合结果,几乎是常数时间;坏处是“答案册”太占仓库,维度一多组合爆炸,而且一旦有新数据就得重印答案册,更新慢。ROLAP 的好处是数据永远最新、不用额外存储,因为查的就是原始明细;坏处是每次都要全表扫描,数据量大时慢得让人抓狂。HOLAP(混合联机分析处理)折中一下:把高频的粗粒度汇总预计算,细粒度的临时现算。

把小王的账房比作餐厅:MOLAP 是提前把菜做好放保温柜,客人点单秒上菜,但菜可能不新鲜;ROLAP 是客人点了才现炒,新鲜但等得久。聪明的做法是招牌菜提前备好(MOLAP),小众菜现场做(ROLAP),这就是 HOLAP 的智慧。

TMOLAP=O(1),TROLAP=O(N)T_{\text{MOLAP}} = O(1), \quad T_{\text{ROLAP}} = O(N)
$O(1)$ 表示查询耗时是常数,与数据量无关;$O(N)$ 表示耗时随明细行数 $N$ 线性增长。
  1. MOLAP 查询耗时预计算后直接读结果,平均耗时是常数。
    TMOLAP=O(1)T_{\text{MOLAP}} = O(1)
  2. ROLAP 查询耗时需扫描全部 $N$ 行明细做聚合,耗时随 $N$ 增长。
    TROLAP=O(N)T_{\text{ROLAP}} = O(N)
  3. 场景权衡查询频繁、数据变化慢选 MOLAP;数据实时变动、查询灵活选 ROLAP。
    查询频率高→MOLAP;实时性要求高→ROLAP\text{查询频率高} \to \text{MOLAP};\quad \text{实时性要求高} \to \text{ROLAP}

稀疏处理

小王越做越发现,魔方里的大多数格子其实是空的。他有几十家门店,很多偏远门店根本不卖活海鲜、不卖高档红酒,那么“偏远门店×海鲜×某月”这种组合就是零销售。整个魔方几百万个格子,真正有数字的可能不到十分之一。这种大量格子为空的现象,叫稀疏。

如果傻乎乎地给每个格子都占一块存储空间,仓库再大也扛不住。于是小王采取了两招:第一招,默认值填充——空的地方不存值,逻辑上当作 0 处理,查询时返回 0 即可;第二招,压缩存储——只记录“有值的格子”的坐标和数值,比如存一条 (1月, A店, 可乐) = 120,格子越稀疏,这种压缩存储省的空间越多。衡量稀疏程度的指标叫稀疏度:非空格子占比越低,稀疏度越高,压缩收益越大。

稀疏处理就像衣柜收纳:不是把所有衣物按尺寸占满所有抽屉,而是只给实际有的衣服分配位置,其余空间留给未来。维度越多、成员越细,稀疏问题越严重,这也是为什么高维Cube必须用稀疏存储——否则再大的仓库也会被“空气”撑爆。

稀疏度=1−N非空单元格N总单元格=1−N非空∏k=1n∣Dk∣\text{稀疏度} = 1 - \frac{N_{\text{非空单元格}}}{N_{\text{总单元格}}} = 1 - \frac{N_{\text{非空}}}{\prod_{k=1}^{n} |D_k|}
总单元格数是各维度成员数相乘,非空单元格数与总单元格数的差距越大,说明Cube越稀疏。
  1. 总单元格数各维度成员数量连乘得到理论格子总数。
    ∏k=1n∣Dk∣=∣D时间∣×∣D门店∣×∣D商品∣\prod_{k=1}^{n} |D_k| = |D_{\text{时间}}| \times |D_{\text{门店}}| \times |D_{\text{商品}}|
  2. 非空占比实际有数据的格子数除以总格子数。
    N非空N总\frac{N_{\text{非空}}}{N_{\text{总}}}
  3. 取补为稀疏度用 1 减去非空占比,得到空格占比。
    1−N非空N总1 - \frac{N_{\text{非空}}}{N_{\text{总}}}

增量计算

魔方最怕的是数据更新。如果每来一笔新销售,就把整个魔方推倒重拼,那等于天天通宵加班。小王发现,其实每天打烊后只需要处理今天新增的那一小部分流水,把它们聚合后“叠加”进已有的Cube里,这就是增量计算。

具体做法是配合分区刷新:Cube按时间分区,比如按月建分区。新数据只影响当天的分区,重建时只重算这个分区,其他分区的聚合结果原封不动。等月底再把这30个日分区合并成月度分区。合并策略上,常用“小增量先攒着、攒够了再合并”的层级合并法,避免频繁重算大分区。不过增量更新也有坑:如果数据被删除或修改,或者有迟到好几天的“补录单”,简单累加就不准了,这时需要写“冲销记录”(负值抵消)或触发该分区全量重算。而实时Cube则更进一步,用流式计算把增量延迟压到分钟甚至秒级。

增量计算就像记账本不用每天重抄一遍,只需在昨日余额后面写上“今日入账 +520”。只有月底结账或发现错账时,才把整页重新核对一次。有了这套机制,小王每天打烊后十分钟就能让魔方“长”出最新数据,第二天一早照常秒答合伙人的问题。

Cubet=Cubet−1⊕Δt\text{Cube}^{t} = \text{Cube}^{t-1} \oplus \Delta^{t}
这个公式表示:当前时刻的Cube等于上一时刻的Cube,叠加(⊕)上这段时间新增产生的增量聚合 $\Delta^t$。
  1. 旧立方体快照上一时刻已经聚合好的结果。
    Cubet−1\text{Cube}^{t-1}
  2. 新增量聚合本批新增明细做完聚合后的结果。
    Δt=聚合(今日新增明细)\Delta^{t} = \text{聚合(今日新增明细)}
  3. 合并操作把增量聚合结果合入旧Cube,得到新Cube。
    Cubet=Cubet−1+Δt\text{Cube}^{t} = \text{Cube}^{t-1} + \Delta^{t}

性能优化

最后,小王想让魔方更快。他发现有些问题合伙人天天问:“本月总销售额”“A店季度品类排行”。与其每次都去Cube里汇总,不如再给这些问题做一份“高频答案小抄”——这就是聚合表:针对业务最高频的维度组合,把汇总结果提前算好存成一张小表。Kylin、Doris 这类分析引擎的加速原理,本质上就是自动管理各类聚合表。

比聚合表更省心的,是物化视图:它像一张有“自动更新协议”的聚合表,数据库会在底层明细表变化时自动维护它,不需要人工干预。配合索引策略,比如对低基数的维度列建位图索引(Bitmap Index),过滤时直接用位运算快速命中,查询又能再快一个量级。这些手段统称为查询加速:核心思想是把“计算”前移为“存储”,拿空间换时间。

把这套思路放回小王的便利店:聚合表就是“本月热销TOP10”贴在收银台,物化视图是请了个自动更新榜单的小助理。魔方本身已经很能打,配上这些“外挂”,真正做到了“问得刁钻,答得飞快”。

加速比=T全量扫描T聚合表/索引查询\text{加速比} = \frac{T_{\text{全量扫描}}}{T_{\text{聚合表/索引查询}}}
加速比衡量优化前后的耗时差距:分子是之前笨办法的耗时,分母是优化后的耗时,比值越大提速越明显。
  1. 优化前耗时不建任何加速结构,直接扫描明细或全Cube。
    T全量扫描=O(N)T_{\text{全量扫描}} = O(N)
  2. 优化后耗时命中聚合表或位图索引后的查询耗时。
    T聚合表/索引查询=O(log⁡M) 或 O(1)T_{\text{聚合表/索引查询}} = O(\log M) \text{ 或 } O(1)
  3. 加速比两者相除,数值越大说明优化越有效。
    T全量扫描T聚合表/索引查询\frac{T_{\text{全量扫描}}}{T_{\text{聚合表/索引查询}}}

AI 应用场景

电商平台销售分析

阶段:数据准备与特征工程

解决的问题:分析师每天要在数亿行订单明细上做多维度 GROUP BY 聚合,写一次 SQL 要等几十分钟,业务方等不及。传统做法是每次现查明细表(ROLAP),数据一多就卡死。用 Cube 预计算后,时间×地区×品类任意组合的聚合结果秒级返回。

具体案例:某电商数仓先用 Hive 做明细存储,再引入 Kylin 构建预计算 Cube。原来需要 40 分钟的“大促日报”跑批,优化后 3 秒出结果;按月增量构建 Cube,每天凌晨自动刷新当日分区。

推荐系统特征聚合服务

阶段:特征工程

解决的问题:推荐模型需要“用户最近 7 天点击某品类商品次数”这类统计特征。每次现算需要扫描该用户上百万条行为日志,无法满足在线毫秒级要求。用 Cube 按(用户×品类×天)预聚合后,在线查询退化为一次 O(1) 读取。

具体案例:某信息流推荐平台用预计算 Cube 服务特征查询,每天离线增量更新当天分区,在线特征服务延迟从 200ms 降到 5ms,模型训练与推理的特征一致性也得到保障。

风控模型监控与指标归因

阶段:模型监控与运维

解决的问题:风控模型上线后要监控交易额、通过率等指标在时间、渠道、产品上的分布变化,一旦异常要快速定位是哪个维度引起的。用 Cube 做上卷下钻,能像小王翻魔方一样,从全局指标一路下钻到“周五晚上+某线上渠道+某类商品”的异常切片。

具体案例:某金融科技公司搭建了按(小时×渠道×产品)聚合的监控 Cube,每天增量更新。一次通过率突降,分析人员 5 分钟内下钻定位到新接入的某个第三方渠道,及时做了规则调整。

Python 演示

python60 行
from collections import defaultdictfrom itertools import product  class MiniCube:    """迷你OLAP Cube: 维度 = 时间 * 门店 * 商品"""     def __init__(self, times, stores, products):        self.dim_sets = [times, stores, products]        self.cells = defaultdict(float)        # 预先生成全部维度成员的笛卡尔积坐标, 值默认为0        for key in product(times, stores, products):            self.cells[key] = 0.0     def load(self, records):        # 明细流水写入Cube: 同一坐标累加度量值        for time, store, product, value in records:            self.cells[(time, store, product)] += value     def query_rolap(self, time=None, store=None, product=None):        """ROLAP实时查询: 现场扫描全部单元格, 按条件过滤求和"""        total = 0.0        for (t, s, p), v in self.cells.items():            if time and t != time:                continue            if store and s != store:                continue            if product and p != product:                continue            total += v        return total     def rollup(self, keep_time=True, keep_store=True, keep_product=True):        """上卷: keep为True保留该维度, 为False则折叠掉并按剩余维度求和"""        agg = defaultdict(float)        for (t, s, p), v in self.cells.items():            key = tuple(x for ok, x in zip(                (keep_time, keep_store, keep_product), (t, s, p)) if ok)            agg[key] += v        return dict(agg)     def sparse_ratio(self):        """稀疏度 = 1 - 非空单元格数 / 总单元格数"""        non_empty = sum(1 for v in self.cells.values() if v > 0)        return 1.0 - non_empty / len(self.cells)  if __name__ == "__main__":    cube = MiniCube(["1月", "2月", "3月"], ["A店", "B店", "C店"],                    ["可乐", "薯片", "泡面"])    cube.load([        ("1月", "A店", "可乐", 120),        ("1月", "A店", "薯片", 80),        ("2月", "B店", "泡面", 60),        ("3月", "C店", "可乐", 45),    ])    print("A店1月可乐销量(ROLAP扫描):", cube.query_rolap("1月", "A店", "可乐"))    print("全部门店可乐总销量:", cube.query_rolap(product="可乐"))    print("按时间上卷(折叠门店/商品):", cube.rollup(True, False, False))    print("稀疏度:", round(cube.sparse_ratio(), 2))
目的:用 27 行核心逻辑实现一个三维迷你 Cube,演示 ROLAP 实时扫描、上卷聚合与稀疏度计算,对应文中“魔方账本”的完整运作流程。
  1. self.cells = defaultdict(float)单元格字典,键是(时间,门店,商品)坐标,值是累计的度量;缺失坐标自动返回 0,天然支持稀疏默认填充。
  2. for key in product(times, stores, products)用笛卡尔积预生成全部理论单元格,对应 Cube 总格子数 = 各维度成员数相乘。
  3. self.cells[(time, store, product)] += value明细流水落到对应格子,同一坐标自动累加,模拟事实表写入 Cube。
  4. query_rolap(...)遍历全部单元格按维度条件过滤求和,就是 ROLAP“现场翻账本”的实时扫描实现。
  5. tuple(x for ok, x in zip(...) if ok)上卷时保留需要聚合的维度键,把折叠维度从键中剔除,是“沿维度归总”的核心。
  6. 1.0 - non_empty / len(self.cells)用非空格子占比的补数算稀疏度,空值占比越高压缩价值越大。
  7. if __name__ == "__main__"可执行入口,直接运行就能看到 ROLAP 查询、上卷结果和稀疏度输出。

常见误区

误区我一直以为 OLAP Cube 就是在代码里造一个 length × width × height 的三维数组,维度超过三个就“装不下”了。所以看到有的系统支持十个维度,觉得很不可思议。

正解Cube 是逻辑概念,不是物理实体。它的本质是“任意维度组合的聚合结果集合”。实现时可以用多维数组,也可以只用一张稀疏表存坐标和值。维度再多,也只是多几个键的字典而已,关键在于稀疏存储。小王那个“魔方”只是个便于理解的比喻,真正的账本是一张张记录坐标与金额的小卡片。

为何常见:市面上所有 OLAP 教程的配图都是三维立方体,看多了自然会以为 Cube 就是三维物理结构。

误区既然预计算查询快,那无脑选 MOLAP 就行,ROLAP 又慢又土,应该被淘汰。

正解MOLAP 的“快”是用存储空间和更新时效换来的。维度多、基数大时,预计算组合爆炸,存储成本极高;明细一更新,Cube 就要重新构建,实时性差。ROLAP 虽然每次现算慢一些,但数据永远最新、扩展性好、不占额外存储。实际工程经常用 HOLAP 混合:高频粗粒度聚合走 MOLAP,低频细粒度现算走 ROLAP。

为何常见:人们容易盯着查询性能这一个指标,忽略存储、更新、维护的全链路成本,就像只看到保温柜上菜快,没看到备菜占厨房。

误区分析要全面,那就把所有字段都加进 Cube 当维度,反正可以预计算。

正解维度数量直接影响单元格总数。每加一个成员数为 $M$ 的维度,总格子数就乘以 $M$,维度一多,稀疏度飞涨、存储爆炸、增量维护成本飙升。设计时应优先选择高价值、低基数、业务常用的维度(时间、地区、品类),把维度控制在十来个以内,而非盲目堆砌。

为何常见:“维度全”和“分析全面”在直觉上容易画等号,忽略了组合爆炸的数学代价。

误区增量更新无非就是“新数据来了,往旧结果上加一加”,写个累加逻辑就完事了,不用考虑其他情况。

正解增量计算只对“纯新增”数据有效。遇到删除记录、修改金额、迟到好几天的补录单,简单累加就会导致 Cube 与事实不一致。工程上要么写负值冲销记录,要么对该分区做局部重算,还要配合分区刷新策略保证一致性。Ralph Kimball 的缓慢变化维理论也强调,维度属性变化时,旧聚合可能失效需要重建。

为何常见:课堂示例通常只演示“追加新行”的理想情况,真实业务里的删除、修改、迟到数据被忽略了。

误区上卷不就是把细粒度数字加起来嘛,SUM 一把梭,销售额、库存、平均单价全都能加。

正解度量值要分可加性。销售额、订单数是可加的,可以任意维度求和;库存余额是半可加的——对产品维度可加,对时间维度只能取期末值或平均值;平均单价、去重用户数这类不可加指标,直接求和会算错,正确做法是“先分别汇总总额和总量,再重新相除”。好比小王算三个门店的客单价,不能把三家的客单价简单平均,得用总销售额除以总客单数。

为何常见:算术直觉告诉我们“汇总=加法”,但统计口径的复杂性让这个直觉在分析场景里经常翻车。

面试题

Q1. Cube 的三个核心组成要素是什么?

单选题 · easy

  • A.维度、度量、单元格← 正确答案
  • B.行列、主键、索引
  • C.表结构、视图、物化
  • D.数据实体、关系、缓存

答案:A。OLAP Cube 用维度定义坐标轴、度量作为落在格子里的数值、维度成员交叉形成唯一单元格。B 是关系表的术语,C 是查询优化的手段,D 是数据库通用概念,都不构成 Cube 的要素定义。

Q2. “只看 A 店这一个门店的所有销售数据”属于哪种多维操作?

单选题 · easy

  • A.固定某一维度成员← 正确答案
  • B.删除某个无用维度
  • C.合并两个相近维度
  • D.对全维做汇总计算

答案:A。固定一个维度取单个成员,看剩余维度构成的面,就是切片操作。比如固定门店维度为 A 店,剩下的时间×商品就摊成一张二维表。B、C、D 描述的都不是切片语义。

Q3. MOLAP 与 ROLAP 的本质区别是什么?

单选题 · easy

  • A.预计算聚合vs实时明细← 正确答案
  • B.都直接查询明细数据
  • C.查询速度没有差别
  • D.存储介质完全不同

答案:A。MOLAP 预先算好所有/部分聚合结果存起来,查询命中聚合;ROLAP 直接对关系型明细表实时聚合计算。B、C 与事实相悖,D 虽然存储介质可能有差异(多维库 vs 关系表),但不是本质区别。

Q4. 上卷(Roll-up)操作的本质是什么?

单选题 · mid

  • A.沿维度层级粗粒度归总← 正确答案
  • B.新增维度细化数据
  • C.固定维度查看切面
  • D.旋转坐标观察角度

答案:A。上卷是沿维度层级向上走,比如从“天”聚合到“月”,从具体商品聚合到品类,是粒度变粗的归总过程。B 是增加维度/下钻的变体,C 是切片,D 是旋转。

Q5. 下列哪个度量指标不能直接 SUM 上卷?

单选题 · mid

  • A.销售额汇总求和
  • B.订单数量求和
  • C.平均单价不可直接相加← 正确答案
  • D.总库存件数相加

答案:C。平均单价是不可加度量,三个门店的客单价平均不等于整体客单价,正确上卷方式是总销售额除以总订单量。销售额、订单数、库存件数都是可加或半可加度量,直接累加在对应维度上是合法的。

Q6. 雪花模式与星型模式的主要区别是什么?

单选题 · mid

  • A.雪花维表拆细更规范← 正确答案
  • B.雪花事实表数量更多
  • C.星型查询一定更慢
  • D.星型不支持维度属性

答案:A。雪花模式把维度表进一步规范化拆分(如商品表拆出品类表),减少冗余但增加连接复杂度;星型模式维度表是反规范化的单层表,查询连接少。B 说反了,C、D 均不准确。

Q7. 处理稀疏 Cube 的常见手段有哪些?

多选题 · mid

  • A.按默认值进行填充← 正确答案
  • B.压缩存储仅保留非空← 正确答案
  • C.额外增加更多维度
  • D.去除零值并记录坐标← 正确答案

答案:ABD。稀疏 Cube 的应对思路是“不存空值”:逻辑上用默认 0 填充查询结果,物理上只存非空单元格的坐标和值(即稀疏编码)。增加更多维度会加剧稀疏而不是缓解,所以 C 错误。

Q8. 增量计算在大维度 Cube 下面临哪些挑战?

多选题 · hard

  • A.数据删除无法简单累加← 正确答案
  • B.迟到数据需要回补修正← 正确答案
  • C.必须重写全部维度字典
  • D.分区刷新要保证一致← 正确答案

答案:ABD。增量更新只适合纯追加场景,删除和修改会产生“增量无法抵消旧值”的问题;迟到数据需要回补到对应分区并重算。分区刷新时要保证新旧分区切换的原子性和一致性。维度字典通常不因增量数据变化而必须重写,C 不成立。

Q9. 大维度 Cube 存储爆炸的根本原因是什么?

单选题 · hard

  • A.维度基数乘积指数放大← 正确答案
  • B.明细数据行不断变多
  • C.查询 SQL 本身写得差
  • D.服务器网卡带宽受限

答案:A。Cube 预计算的总单元格数等于各维度基数连乘,维度基数大、维度多时组合数指数爆炸,即便稀疏存储也会占用大量空间。明细行增多影响的是 ROLAP 扫描成本而非 Cube 预计算爆炸,C、D 属于查询与硬件层面问题。

Q10. 关于半可加性度量在 Cube 中的正确上卷方式,哪些说法正确?

多选题 · extreme

  • A.库存时间维用平均← 正确答案
  • B.库存产品维直接加← 正确答案
  • C.平均价总额除总量← 正确答案
  • D.所有维都取最大值

答案:ABC。库存余额是半可加度量:时间维度上取期内平均值或期末值,产品维度上可以直接求和。平均单价等不可加度量必须总量重算(总金额/总数量)。D“所有维度都取最大值”错误,不同维度应分别设计聚合函数。