把多维分析的结果提前算好存下来,查询时直接命中结果,这是 Apache Kylin 的工作方式,也是它和明细扫描型引擎之间最本质的区别。明细引擎是查询来了才去扫数据,Kylin 是数据还没到就已经把答案算完。这个差别落到服务器租用上,会推翻一大半默认的配置习惯:真正吃掉 CPU 的不是白天查数据的人,而是夜里那批 Cube 构建作业;真正决定硬盘容量的不是原始数据量,而是 Cuboid 的个数。本篇按 Apache Kylin 真实的资源剖面来拆配置,把构建节点和查询节点分开算账,并给出明确的退出条件,说清楚哪几类场景压根不该选择它。
先看 Apache Kylin 在生产环境里跑起来之后由哪几块组成。元数据存储通常是一张普通的 MySQL 或 PostgreSQL 表,负责保存 Cube 定义、Segment 状态、任务历史;作业引擎负责调度构建任务;构建引擎负责真正跑,常见的实现是 Spark,早期版本主要依靠 MapReduce,也有 Flink 方向的实现;查询引擎是 Query Server,一个对外提供 JDBC、ODBC、REST 接口的 Java 服务。此外还有存储层,早期是 HBase,新版本可以直接落 Parquet 文件。
这四块里,资源消耗排第一位的没有任何悬念,就是构建作业。一次 Cube 构建是标准的批处理:从 Hive 或者别的数据源读事实表,关联维表生成宽表,对每个维度建字典,然后按层级把所有 Cuboid 聚合出来,最后转成存储层需要的格式写进去。整个过程 CPU 密集,磁盘顺序写密集,中间还有 shuffle 的网络流量。构建跑起来的时候,CPU 使用曲线基本是贴着顶的。查询侧完全是另一种画风:Query Server 收到一条被 Cube 覆盖的 SQL,通过元数据找到命中的 Cuboid,去存储层读一小段已经聚合好的结果,做一次简单的二次汇总,返回。单条查询占用的 CPU 时间很少。
这种剖面带来两个方向的误配。第一种是按白天能扛多少查询来确定机器的 CPU,选出来一台核数很猛的服务器,结果夜里全量构建跑了六个小时还没收尾,第二天早上业务看不到数据。第二种是反过来,为了让构建跑得爽快,把 Query Server 也塞到构建节点上共享那块大 CPU,白天用户侧的响应延迟从几十毫秒跳到几秒,抖动毫无规律。这两种误配的根子是同一个:把构建和查询当成了同一种负载来估。
把构建作业拆开按阶段看,每一段要的资源并不一样。第一步生成宽表,从数据源抽列并关联维表落到分布式存储上,这一阶段要的是磁盘顺序写和读取带宽。第二步构建维度字典,对每个维度做去重并且生成映射关系,这里要的是单节点内存。第三步构建最细粒度的那一层 Cuboid,输入就是第一步的宽表。第四步往上逐层聚合,Apache Kylin 提供了逐层构建的 By-layer 策略,也有依靠 Cube Planner 自动做选择的策略,这一阶段是纯 CPU 与网络 shuffle。第五步把算好的 Cuboid 转成 HFile 或者写成 Parquet,CPU 和磁盘混合消耗。第六步更新元数据并且清理临时数据。
第三步和第四步吃掉绝大部分的 CPU 时间。换句话说,你在构建节点上多加的那几个核,买的是后面几层聚合能跑多快。所以选型时第一句要问的不是查出多少条业务数据需要多少查询能力,而是一次全量构建允许跑多久。构建窗口定了,核数、内存、磁盘才有推算的起点;这个问题不回答,后面所有配置数字都是拍脑袋。
Cuboid 指的是某几个维度的组合上的预聚合结果。有 N 个维度参与建模,理论上就有 2 的 N 次方个 Cuboid,因为每个维度在组合里只有出现和不出现两种状态。三个维度 A、B、C 会产生八个组合:空集、A、B、C、AB、AC、BC、ABC。这个数字直接决定 Cube 的最终体积,也直接决定构建作业要处理多少行数据。
维度从 10 个到 20 个,Cuboid 从 1024 个变成 1048576 个;中间到 15 个维度时是 32768 个。每加一个维度翻一倍,十到二十之间差了整整三个数量级。业务评审会上轻描淡写地加一个商品二级类目字段,落到资源账上就是 Cuboid 数量乘以二,跟着 CPU 小时数和磁盘容量一起往上走。如果不做剪枝,二十个维度的 Cube 基本上没有动手的意义。
存储之所以跟着膨胀,是因为每个 Cuboid 都是一张实际存在的聚合结果集。粒度粗的 Cuboid 行数少,粒度细的行数多,全部加起来才是 Cube 的真实体积。业界一般用膨胀率来衡量这件事,也就是 Cube 总大小除以源宽表大小。这个比值取决于维度基数的分布,不存在通用常数,最可靠的看法是打开 Cube Planner 或者读构建日志里的 Cuboid 行数统计。按经验(预估),把膨胀率压在十倍以内是比较舒服的区间,一旦超过这个量级,正确做法是回到模型设计重新剪枝,而不是去加硬盘。
估行数有个粗糙但管用的办法。单个 Cuboid 的行数上限是它包含的各维度基数的乘积,实际值会低于这个上限,因为源表里真实存在的组合不一定打满,也会受到单个 Segment 的时间范围限制。举个例子(纯示意,按自身数据核对):事实表每天五千万行,城市维度基数 300,品类维度基数 2000,渠道维度基数 20,那么城市加品类加渠道这个 Cuboid 的行数上限是 1200 万,实际会低于这个数。但如果参与组合的维度里混进来一个基数两千万的用户标识字段,这个上限立刻变成天量,单是这一个 Cuboid 就能吃掉整个 Cube 的存储预算,这类组合必须在建模阶段就剪掉。
构建时间方面,构建作业要为每一个 Cuboid 做一次聚合计算,CPU 时间大致与 Cuboid 行数总和成正比。所以 2 的 N 次方不是一个抽象的风控术语,它会原样出现在你的云账单上:Cuboid 数量翻一倍,硬盘容量与 CPU 小时数就跟着翻一倍。
2 的 N 次方是什么都不做时的上限。Apache Kylin 提供了四种手段把这个数字压下来:必要维度 Mandatory Dimension、层级维度 Hierarchy Dimension、联合维度 Joint Dimension、聚合组 Aggregation Group。四者的压缩强度和操作顺序并不一样,用错了顺序,事后回滚的成本会很高。
必要维度指的是每一条查询都会带上的维度,典型是统计日期或者租户标识。一旦某个维度被标成 Mandatory,所有不包含它的 Cuboid 都不必生成,总量直接除以二。这是成本最低、收益最确定的一步,只要能够确认某个维度在全部查询里必然出现,就应该第一时间把它标上。它的代价写得很清楚:万一真有查询不带它,那条查询命中不了 Cuboid,只能退化到运行时聚合或者查询下推。
层级维度处理天然包含关系的维度,比如国家到省到市,或者年到季度到月到日。三个维度摊开产生八个组合,但业务上不存在只查市不查省的需求。声明层级关系之后只保留三条:国家、国家加省、国家加省加市。三个维度从八个组合压到三个,直接砍掉六成多。
联合维度适用于总是成对出现的维度,例如订单状态与订单状态名称、商品标识与商品名称。把 k 个维度绑成一个联合维度,它们的组合状态从 2 的 k 次方降到 2,要么一起出现要么都不出现。收益取决于 k 的大小,k 越大这一刀切得越划算。
聚合组是强度最高的一刀。它把全部维度切成若干组,不同组之间的维度不做交叉组合。以十二个维度均分成两组为例,理论 Cuboid 从 4096 降到两个组各自 64 个再加交叉部分,压缩幅度在三十倍这个量级(粗略口径,实际还要计入必要维度与交叉维度的影响)。代价同样直接:跨组的联合查询就找不到可用 Cuboid 了。所以分组依据必须是真实存在的查询模式,不能按维度表在逻辑上怎么归来切。
实操顺序建议是必要维度、层级维度、联合维度、聚合组,走完这一轮再开 Cube Planner,让它基于真实的查询统计做第二轮收敛。这个排序的理由是前三种手段的语义约束更强,误伤范围清晰,出了问题容易回滚;聚合组属于分区切割,一旦切错,整类查询全部退化到运行时聚合。
最后一句实话:如果团队拿不出几十条稳定的查询模板,就不要做十五个维度以上的 Cube。没有查询模板就没有剪枝依据,没有剪枝依据就只能全量构建,全量构建十五个维度是 32768 个 Cuboid,这个 Cube 一定会炸。
Apache Kylin 默认对维度值做字典编码,把一个可能很长的字符串映射成一个整数标识。Cube 里存下来的不再是一串几十字节的文本,而是几个字节的整数,体积降下来,分组比较也从字符串比较变成整数比较。这是 Cube 能被压到可接受体积的关键手段。
瓶颈出在字典的构建步骤。这个步骤要把某个维度在本次构建范围内所有出现过的值去重、排序、建成映射表,再交给后续步骤使用。它的资源特征是单节点内存密集:无论 Spark 集群里有多少个执行器,这一步的常驻压力集中在有限的任务或进程上。各个大版本对字典的处理方式存在差异,Apache Kylin 也提供全局字典 Global Dictionary 这类分布式字典方案,具体行为以你实际部署版本的官方文档为准。
灾难来自用户标识、订单号、设备号、会话标识这类高基数维度。一亿个互不相同的用户标识,光是把这一亿条映射放进内存并完成排序,占用轻松到几十 GB 量级(预估,取决于标识字符串的长度与实际实现)。单个容器或者执行器给的内存扛不住,结果就是容器因为超出内存限额被资源框架杀掉,或者直接抛出堆内存溢出错误,构建作业在这个步骤整体失败,Cube 那一天都出不来。
识别起来并不难。构建作业的其它步骤跑得飞快,唯独卡在建字典这一步,Spark 界面里看到个别任务出现数据溢出甚至失败重试,重试几次之后整个作业中止。这时候去看构建日志里各步骤的耗时分布,如果建字典占了大头,基本可以锁定到某一两个高基数维度。
处理路径有四条,按推荐程度排序。第一条是把这个字段从维度列表里移除:用户标识通常不会被拿来做分组,真正的诉求往往是去重用户数,那就改成度量,用 HyperLogLog 或者位图做近似计数。第二条是换编码方式:维度编码除了字典之外还有定长、整数、日期、时间、布尔等多种可选形式(可用清单以所选版本文档为准),定长编码不做字典、按固定字节存放,绕开了建字典的内存瓶颈,代价是 Cube 体积变大、扫描变慢。第三条针对精确去重计数,要精确值就得上全局字典,成本高一个量级;如果可以接受百分之一上下的误差(预估),近似算法明显更划算。这是建模阶段必须当场拍板的取舍,事后改动意味着全量重建。第四条是缩小 Segment 粒度:字典按 Segment 构建与复用,日级拆成小时级之后单个 Segment 内的去重基数下降,压力随之下降,代价是 Segment 数量膨胀带来的元数据与查询侧合并开销。
一句话的判据:任何基数超过千万并且还在持续增长的字段,都不要作为字典编码的维度进 Cube。要么换个角色去做度量,要么换编码,要么别在这个引擎里用它。
构建窗口的定义是从上游数据就绪到 Cube 可以被查询之间允许的最长时长。这是一个由业务决定的硬约束,不是技术指标。日级 Cube 的典型窗口是凌晨的两到六小时;小时级 Cube 的窗口被压到三十到四十五分钟以内,否则永远追不上数据到达的速度。
窗口时长拆开是五段:数据源读取与宽表落地、字典构建、各层 Cuboid 聚合、格式转换写存储、元数据更新与清理。其中第三段占大头,同时它也是最可并行的一段,所以在构建节点上买的核数本质上买的就是这一段的并行度。
CPU 的估法可以这样走(预估口径,需要用自己的构建日志校正):估算单个 Segment 的源行数,乘以剪枝后的 Cuboid 数量,得到有效聚合行数的总和;再按历史构建日志里的经验产出率,也就是每核每小时大约能处理多少聚合行,做一次除法,得到所需的核小时数;最后除以你能接受的构建小时数,就得到要买多少核。注意 Cuboid 数量翻倍,核小时数同步翻倍,这是一条直线关系。
内存分两处给。作业管理端要装下元数据和部分字典,给它一个充裕的堆;执行端按核来配,Spark 上常见的起点是每个执行核配 4 到 8GB(预估,依据溢出情况调整)。核数多了之后要注意单个执行器不要塞太多核,单个执行器控制在 4 到 8 核比较稳妥(预估),核太多会让同一个进程里的任务并发过高,垃圾回收压力集中在一个进程里。
磁盘要按三份算:Cube 的最终大小、构建过程中的中间层临时数据、清理发生之前两者共存的那一段。常见做法是给 Cube 最终大小的二到三倍做余量(预估),因为临时数据是在作业成功之后才开始清理的。写一半因为盘满失败,比一开始就把余量留够要麻烦得多。下面这张表把构建侧与查询侧的资源账放在一起对照。
| 节点角色 | 主要资源瓶颈 | 配置判断依据 | 常见误配 | 扩容优先项 |
|---|---|---|---|---|
| 构建作业执行节点 | CPU 与磁盘顺序写带宽 | Cuboid 行数总和除以可接受的构建小时数 | 按查询并发选核数,完全忽略 Cuboid 膨胀 | 先加核数与并行度 |
| 字典构建步骤 | 单节点集中占用的内存 | 最高基数维度的去重值个数与取值长度 | 核数给足但内存偏紧,卡在建字典 | 先加内存或改维度编码 |
| 存储层(HBase 或 Parquet) | 随机读 IOPS 或顺序吞吐 | Cuboid 总行数乘以单行宽度再乘副本数 | 按原始数据量估算容量 | 先扩容量并复查列裁剪 |
| Query Server 查询服务 | 堆内存与垃圾回收停顿 | 并发查询数乘以单查询返回行数 | 与构建节点混布共享同一份 CPU | 横向加实例副本 |
| 元数据库 | 单点可用性与备份策略 | 元数据条目数与 Cube、Segment 数量 | 单机部署不做主从与异地备份 | 先补主从与备份再谈性能 |
Apache Kylin 的存储后端经历过一次换代。早期版本把 Cube 落到 HBase 表里,行键由 Cuboid 编号加上各维度的编码值拼接而成,查询时按行键前缀做范围扫描或者点查,由 RegionServer 提供缓存与服务。较新的版本方向去掉了对 HBase 的强制依赖,Cube 直接以 Parquet 文件落在分布式存储或者对象存储上,查询由 Spark 侧的引擎做列式扫描。两条路线对磁盘的要求完全不同。
HBase 这一派的查询特征是大量小块随机读。命中的数据落在少数几个 Region 上,先在块缓存里找,没有命中就去底层分布式文件系统取。这决定了它对随机读 IOPS 敏感,机械盘组合在这种访问模式下的延迟很难看,而 RegionServer 的堆外读缓存给多大直接决定命中率。写路径上是批量加载:构建作业先生成 HFile 再挂载进表,期间预写日志与合并 compaction 都在消耗磁盘。结论是要给 SSD 或者 NVMe,容量可以不大,随机读能力必须给足。
Parquet 这一派的顺序扫描特征明显。一次查询只需要读取命中的那几列,列式格式天然支持列裁剪,配合谓词下推与行组级别的统计裁剪,实际读取量远小于文件体积。这个模式吃的是顺序吞吐而不是随机 IOPS,机械盘也能跑起来,NVMe 的主要价值落在构建阶段的 shuffle 溢出与临时写上面。磁盘选型的重心因此从高并发小块随机读转向单位容量的顺序带宽与单价。
网络账也有区别。HBase 路径下查询要在多个 RegionServer 之间拉数据回传;Parquet 路径下扫描在存储节点本地完成一部分聚合,再把聚合后的中间结果 shuffle 回计算侧。因为结果已经聚合过,回传的数据量通常不大,这一点比明细扫描型引擎温和得多,算是预计算带来的附带好处。
怎么选以自己的情况为准。新起集群并且团队里没有人愿意维护 HBase,就走 Parquet 路线,少养一套分布式数据库,资源配置的精力也能集中到 Spark 集群上;现成有稳定的 HBase 集群并且团队熟悉它,迁移动力主要来自运维成本与版本支持节奏,而不是绝对性能。这条不比优劣,取决于你愿意长期维护多少套系统。
Query Server 是常驻的 Java 服务,对外提供 JDBC、ODBC 与 REST 接口,内部用 Calcite 做 SQL 解析与优化,再翻译成对存储层的访问计划。它要常驻的内容包括元数据缓存、维度字典缓存、连接池以及查询结果。
单条查询的开销分三段:解析与生成执行计划、去存储层取数据并做运行时二次聚合、结果序列化回传。第一段很轻,真正的内存压力在第二段,也就是这条查询要在堆里 hold 住多少中间结果。
估法可以直接用乘法:预估堆内存等于并发查询数乘以单查询的中间结果集大小,再加固定开销。这里的关键是中间结果集看的是分组聚合之后返回的行数,不是底层扫过的数据行数,这是预计算引擎与明细扫描引擎的核心差别。一个返回一万行二十列的聚合查询,中间对象的量级在几 MB 到几十 MB;如果查询模板要返回几十万行,单个查询就是百 MB 级,十个并发就是 GB 级起步。固定开销主要是元数据与字典缓存,取决于 Cube 数量与字典大小。
为什么它能配得比较瘦?因为 Cuboid 里已经存着聚合结果,Query Server 通常不需要做大表关联和全局排序。同样一张千亿行的事实表,明细引擎在查询时要扫几十亿行,Apache Kylin 只需要从命中的 Cuboid 里读几千行。这是这个引擎存在的全部意义。
两种情形会打破上面的乐观估计。一是查询没有命中任何 Cuboid,这时会退化到从更细粒度的 Cuboid 做运行时聚合,扫描量与中间结果同时上涨,原本几十毫秒的查询可能变成几秒。二是查询下推:Apache Kylin 支持把命中不了的查询下推给 Spark 或者 Hive 执行,一旦走这条路,Query Server 只做中转,压力转移到了下推集群,但 JDBC 连接会被长时间占用,连接池要给更多位置,超时时间也要相应放宽。
堆不要往机器上限定。堆越大,完全垃圾回收的停顿越长,而看板类场景最受不了的就是偶发几秒停顿。通常的做法是把单进程堆控制在十几到几十 GB(预估,取决于垃圾回收器版本与对象存活情况),堆用满就横向加 Query Server 实例,前面挂一层负载转发。剩下的内存留给系统页缓存与堆外结构,这两块也是实打实的消耗。垃圾回收器在较新的 Java 版本上一般选 G1GC,监控重点看完全回收的次数与单次最长停顿,只看平均值会被骗。
最后一条判断:Query Server 的 CPU 通常是比较闲的,查询变慢先去看垃圾回收日志与堆使用曲线。在这个角色上盲目加 CPU 核,收益接近于零。
构建作业的画像是短时、峰值、吞吐优先,跑起来时 CPU 与磁盘写带宽双双打满,跑完之后整台机器空着。Query Server 的画像是全天在线、平缓、延迟敏感,CPU 常年低水位,内存常年占着。两条曲线看着像互补,拼在一起似乎能把利用率填满,但落到用户体验上就是互相伤害。
最直观的是延迟。构建跑到逐层聚合那几步时,磁盘顺序写带宽会被占满,同一台机器上的 Query Server 要去读 Cuboid,读写冲突在同一块设备上直接变成延迟上涨。原本几十毫秒的查询在构建窗口内可能涨到秒级,涨跌幅度完全无法预判。业务早上打开看板时恰好赶上昨夜的构建没跑完,得到的反馈就是一句系统很慢。
页缓存的损失更隐蔽。操作系统会把空闲内存拿来当文件缓存,构建作业的顺序大块读会迅速把这些页挤出去,Query Server 依赖的热 Cuboid 被刷出缓存,命中率下降。这个损失在构建结束之后还要持续一段时间才能恢复。
垃圾回收的抖动会叠加。构建侧的执行器与 Query Server 是两个不同的进程,它们在同一台机器上争抢 CPU 时间片。一边在做完全回收,另一边的停顿也会被时间片调度放大;CPU 使用率高的时候回收线程同样排不上队,停顿时间被进一步拉长。
故障域耦合是更要命的一点。构建作业是公认容易出现内存溢出的负载,超限之后资源管理框架会杀掉容器,内存限额配置不当时会波及同机进程;构建失败后清理临时文件的那段时间磁盘压力也不小。把查询服务和构建作业放进同一个故障域,等于给了夜里那批批处理作业拖垮白天服务的能力。
扩容粒度同样不支持混布。构建资源的需求是窗口内的临时爆发,查询资源的需求是按并发长期常驻。混布之后没法分别扩,只能整体换更大的机器,成本按两者叠加的峰值支付,而这个峰值组合在绝大多数时间里并不会同时出现。分开之后构建可以走队列按小时弹性,查询节点按实际并发长期租用。
可以混布的场景只有一种:数据量很小,日增量在百万行以内;Cuboid 数量只有几十个;构建一天一次并且十分钟跑完;查询并发是个位数。这时省下一台机器的钱是划算的,代价是接受那十分钟里查询有抖动。除此之外,至少要做资源池或者队列层面的隔离,更好的是实例级隔离。
第一类是维度结构频繁变动的业务。加一个维度,Cuboid 数量翻倍,Cube 的模型要重新设计,历史 Segment 要跟着重建。业务侧一周提两个加字段的需求,数据团队的精力就全耗在重构与重跑上了。这类场景交给明细存储加运行时聚合的引擎会舒服得多,加一个字段就是加一列。判据很直接:过去半年里看板的维度发生过两次以上结构变更,先把结构稳定下来,再来谈要不要做 Cube。
第二类是需要明细级查询的场景。查某个订单的完整链路状态、导出一批原始行、分析师拖任意维度组合做自助探索,这些 Apache Kylin 做不了。Cube 里存的是聚合结果,根本没有明细行。硬要做也不是不行,保留一个包含全部字段的最细 Cuboid 就行,但那等于把原始宽表复制了一份还额外加了编码开销,完全不划算。遇到导出明细、查单条记录这类需求要能明确划线:这部分交给明细引擎,Kylin 只服务固定口径的聚合看板。
第三类是数据新鲜度要求到秒级的场景。Cube 构建是批处理,做到分钟级的微批次已经相当紧张,秒级做不到。Apache Kylin 的历史上出现过面向流式数据的方案,但它的主战场始终是 T+1 或者小时级刷新。要秒级看到刚发生的事情,去用针对实时摄入设计的引擎,不要在这个方向上硬抠。
再补一条软性的退出条件:查询模板沉淀不出来。预计算的价值是用夜里的批处理换白天的低延迟,前提是知道该算什么。如果使用者是一群分析师,每天问的题目都不一样,那无论怎么剪枝都会有一部分查询落到 Cuboid 外面,退化成本很高。这种情况先用明细引擎跑三个月,把真实出现的高频 SQL 收集起来,攒出几十条稳定模板,再回来讨论要不要上 Cube。
把话说全,适合它的画面是这样的:维度结构稳定,几个月不变;查询模式可以枚举,几十种固定口径的固定看板;维度基数可控,没有亿级的标识类字段参与分组;数据量在 TB 级以上,明细扫描的成本已经吃不消;要求亚秒到秒级响应,但可以接受 T+1 或者小时级刷新。这几条同时成立的时候,预计算的性价比才真正体现出来。
前面的分析可以收成一句话:构建节点和查询节点按两套配置分别租,先看构建窗口,再看查询并发,中间不要让它们共用同一台机器。下面是三档画像,全部为推算的量级,落到具体机型之前务必用自己的 Cube 跑一次构建并按日志校正。
内部看板这一档:日增量在百万行以内,参与建模的维度八到十个,剪枝后 Cuboid 数量几十个,单个 Cube 最终存储在几十 GB 量级(预估)。构建侧给一台十六到三十二核、六十四到一百二十八 GB 内存的机器足够,通常能在一小时内收尾;还想压窗口,加核比加内存管用。查询侧两台八到十六核、三十二到六十四 GB 的机器,单实例堆给八到十六 GB,前面挂一层转发。磁盘按 Cube 最终大小的三倍预留,给临时数据留出地方。
部门级分析平台这一档:日增量从千万到亿行,维度十到十五个,剪枝后 Cuboid 数量数百,单个 Cube 存储在几百 GB 到 TB 量级(预估)。构建侧需要的是一个队列而不是单台机器,总量在六十四到两百核、二百五十六到五百一十二 GB 内存之间,具体切成几个执行器由单核内存配额决定。查询侧两到四台十六到三十二核、六十四到一百二十八 GB,堆给十六到三十二 GB。磁盘优先看顺序吞吐与容量单价,固态盘主要用在构建阶段的中间数据落地。
企业级多 Cube 这一档:日增量十亿行以上,多个 Cube 并行构建,维度十五个以上。这一档不要试图用一台机器解决:按 Cube 拆构建队列,每个 Cube 单独排队、单独定优先级,构建总核数在数百量级(预估);查询侧四台起步并做水平扩容,堆按实测并发调整;元数据库单独部署并配主从与异地备份,它是整条链路里最容易被忽略的单点。
还有一个经常被忽略的变量,就是构建节点离数据源的距离。事实表存在某一个地域的对象存储里,而 Kylin 集群部署在中国香港或者其它地域,跨地域读取源表的时间会实打实计入构建窗口,还要额外付一份流量费用(需询价,公开渠道差异较大,以实时报价为准)。构建节点与数据源放在同一个地域是最省事也最省钱的选择;只有当用户分布需要覆盖多个区域时,才考虑把查询节点摊开部署,构建与元数据仍然留在数据源那一侧。
关于费用本文不给任何具体金额。不同机房、不同带宽口径、不同付费周期(预估)之间的差距可以拉得很开,公开渠道报价差异较大,以实时报价为准,实际以咨询为准。一万网络深耕 19 年(成立于 2007 年),在这类批处理加常驻服务混合负载的配置评估上可以给出具体的机型组合与带宽方案,前提是拿到你自己 Cube 的构建日志,属于询价范围。
本篇涉及的技术口径包括 Cuboid 数量与维度个数的 2 的 N 次方关系、四种剪枝手段、构建作业的六个阶段划分、维度字典编码在建构建流程中的位置,这些均来自 Apache Kylin 官方文档中关于 Cube 设计与构建流程的说明。不同大版本在构建引擎与存储后端的实现上差异较大,具体参数名、可用编码清单与默认策略以你实际部署版本的官方文档和 Release Note 为准。
文中出现的全部核数、内存容量、磁盘余量倍数与耗时量级,都是按 Cuboid 行数总和乘以单位处理成本再除以可接受窗口时长这类方法推算出来的,没有引用任何实测基准数据,每一处都做了(预估)标注。它们的作用是给配置定一个起点量级,不能直接作为采购依据。
核对的办法只有一条:跑一次真实的全量构建,把 Spark 界面里的任务耗时分布、溢出大小、shuffle 读写量,和 Apache Kylin 构建日志里每个步骤的起止时间对起来。构建日志会给出每个步骤的真实耗时,Cube Planner 与元数据中的 Cuboid 统计会给出每个 Cuboid 的行数与体积,这两个数字才是配置真正的依据。
费用相关的口径重申一次:机型、带宽、存储的月租需询价,公开渠道差异较大,以实时报价为准。本篇不提供任何具体金额,也不对实时报价做任何推算。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品