1.概述 (p21-32) :
(1) 数据表或者数据模型上所有字段只有两种分类: 维度或者度量, 度量可以被聚合 .
是数据分析中的两个基本概念.
维度指审视数据的角度, 通常是数据记录的一个属性,例如时间,地点等.
度量是基于数据所计算出来的考量值,通常是一个数值, 如总销售额,用户数等. 在一个sql中, GROUP BY的属性通常就是维度, 而所需要计算的值就是度量.
(2) 对于N维度来说, 组合的所有可能为2^N种. 对于每一种维度的组合, 将度量做聚合运算. 将结果保存为一个物化视图,成为Cuboid, 所有维度Cuboid作为一个整体=Cube
(3) 1.5版本时,数据以关系表的形式输入, 且必须符合星形模型(Star Schema) (更复杂的雪花模型1.5时还不支持,需要使用视图转换为星形模型,在使用kylin)
(4) 对于查询引擎下方的路由选择, 最初设计时考虑kylin不能执行的查询引导去hive, 但是会导致查询速度期望不一致, 默认关闭.
(5) 可扩展, 数据源,构建引擎和存储引擎都可扩展和替换,进行二次开发
(6) OLAP查询语言 MDX(MultiDimensional eXpressions)
(7) Apache Zeppelin插件
(8) 大数据产品一致采用的优化技术: 1)大规模并行处理 2) 列式存储: 提高单位时间IO吞吐率(存储介质善于连续访问),跳过不需要访问的列 3)索引: 利用索引跳过不需要访问 的数据块,仅扫描需要扫描的数据内容. 4) 压缩: 压缩数据然后存储,使得数据的存储密度更高,有限IO速率下, 单位时间内读取更多的数据记录.
(9) Kylin 在数据集规模上的局限主要在于维度的个数和基数. 一般由数据模型来决定,不会随着数据规模的增长而现行增长. 例如一维的商品维度, 如果商品的种类没有增加, 每天的销售记录增长10倍,对于商品维度的物化视图, 数据量不发生变化, 不受原数量的影响.
维度的基数(Cardinality) 指该维度在数据集中出现不同值的个数;例如国家是一个维度,如果有200个不同值,则维度的基数=200. 如果维度基数过大,如用户ID,超过了一百万,就需要设计注意了. 如果一个Cube有好几个超高基数维度,那么这个Cube膨胀的概率非常大
2. 快速入门 (p35- 72)
(10) 数据仓库, OLAP和BI
数据仓库: 一种信息系统的资料存储理论, 存入的资料必定包含事件属性,且不会随着时间发生变动.
OLAP : (Online analysis process) 联机分析处理, 又叫多维度数据分析. (OLTP 联机交易处理, 偏重日常事务处理,包括数据的增删改查等)
BI : (Business Intelligence) 商务智能, 用先带数据仓库技术,在线分析技术,数据挖掘和数据展现技术进行数据分析以实现商业价值.
(11) 事实表和维度表
事实表(Fact Table): 存储有事实记录的表, 如系统日志,销售记录等; 事实表的记录在不断等动态增长,所以他的体积通常远远大于其他表.
维度表(Dimension Table), 与事实表相对应的一种表,保存了维度的属性值,可以和事实表做关联; 相当于将事实表上经常重复出现的属性抽取,规范出来用一张表进行管
理. 常见维度表有 日期表(存储与如期对应的周,月,季度等属性), 地点表等. 使用维度表的好处:
1) 缩小了事实表的大小
2) 便于维度的维护和管理,增加删除和修改维度属性,不必对事实表进行大量的记录修改
3) 维度表和为多个事实表重用,减少重复工作.
(12) Cube, Cuboid 和Cube Segment
Cube (Data Cube) 即数据立方体, 一种常用于数据分析和索引的技术;可以对原始数据建立多维度的索引.
Cuboid在kylin中指某一维度组合下所计算的数据
Cube Segment: 针对数据源中的某一个片段,计算出来的Cube数据. 通常数据仓库中的数据会醉着时间而增长,而Cube Segment也是按时间顺序构建的.
(13) 维度表越小越好,因为维度表会加载到内存中以供查询,过大的表不适合作为维度表,默认阀值300MB
维度表最好不好是视图, 虽然1.5.3支持这种情况,但是每次都需要将视图物化,从而增加额外时间开销
(14) 衍生维度(Derived Columns) : 衍生维度必须来自某个维度表,一次可以选择多个列,但是事实上这些列都可以从维度表的主键值中衍生出来,因此没必要加入Cube的计 算,只需要把事实表中的对应的外键(FK) 加入Cube计算即可.
(15) kylin默认把所有维度都放在一个聚合组中,如果维度数较多(例如:>10),建议用户根据查询习惯,将维度分为多个聚合组,以此降低Cube中Cuboid数量.例如, 一个Cube有(M+N)个维度,默认会有2^(M+N)个Cuboid; 如果分成两个不相交的聚合组, 那么Cuboid的数量将会减少为2^M+2^N
(16) Mandatory Dimensions: 强制维度,指的是一定会出现在Where或者Group by语句中的维度; 通过指定维度为强制维度,kylin就可以不用计算那些不包含此维度的Cuboid,从而减少计算量.
(17) Hierarchy Dimensions:垂直维度指的是一组包含层级关系的维度. 例如'国家','省','市', 国家是最高级别的维度,省,市依次为低级别的维度. 用户会按照高级别维度查询,也会按照低级别维度查询, 但是不会孤立的审视低级别的维度,例如查询省的时候肯定会带上国家,模式为'国家'+'省'或者 '国家'+'省'+'市', 不会跨越国家 Group by '省'或者'市', 据此, kylin可以省略不满足此模式的Cuboid.
(18) Joint Dimensions: 将多个维度组合成一个维度,出现在两种情况: 1) 总是会一起查询的维度 2)基数很低的维度
(19) Kylin以key-value的方式将Cube存储到Hbase中. HBase的key,也就是Rowkey, 是由各维度的值拼接而成.(经过编码压缩,高效存储)
(20) 各维度在RowKeys中的顺序,对于查询的性能会产生较明显的影响.这里根据用户的查询模式和习惯,调整维度的顺序.从而提高查询效率. 原则是, 过滤频率高的在低的之前,基数高的列放在基数低的列之前, 能够充分利用过滤条件来缩小在HBAse中扫描的范围,提高查询效率.
1) 在查询中被用作过滤条件的维度有可能放在其他维度前面
2) 将经常出现在查询中的维度放在不经常出现的维度前面
3) 对于基数高的维度,如果查询会有这个维度上的过滤条件,往前调整, 否则往后调整, 最好结合 聚合组一起使用
(21) Cube两种 构建方式: 全量构建和增量构建 (Cube Segment为增量构建时按照时间产生的,查询时扫描所有Segment合并查询结果,Segment可以合并,防止过多)
(22) Kylin的查询语句为标准的SQL, 屏蔽了后台的Cube, 只有当查询的模式和Cube的定义相匹配时,Kylin才能使用Cube的数据完成查询. Group BY和 Where条件里的列,必须是在Dimension中定义的列,而SQL中的度量,应该跟Cube中的度量相互一致.
(23) Kylin使用开源SQL引擎 -- Apache Calcite , 提供标准SQL解析,多种查询优化和连接各种数据源的能力,已被众多项目使用
3. 增量构建 (p73-100 )
(24) 每个Cube Segment用起始时间和结束时间标识 代表一段时间内元数据的计算结果.
(25) 增量构建必须至少包含一个时间维度,分割时间必须是事实表上的列
(26) 管理员可以手动合并Segment,也可以设定 Auto Merge Thresholds , 合并的时候从最大的时间长度开始尝试合并,如 7 days 和 28 days , 每当有一个新的Segment产生的时候,都会触发合并检测,若加上新的Segment满足了28days, 则会合并为28days的大Segment, 否则检测是否满足 7 days. Segment最大区间长度28 days.已经为28 days的Segment不会再参与合并, 不会出现半年,一年等更大的Segment. 合并的最大区间最好不要超过 1 years .
4. 流式构建(p101-121 )
(27) Margin = 数据延迟, INTERVAL = 微构建批次间隔时间. Segment构建延迟Delay = Margin+ Margin. 例如Margin=10 min , INTERVAL = 5min ,最早1:10 才能查 询到00:55的数据
(28) 取数据时,为了防止丢数据, t1~ t2时间内的数据会扩展范围, 变为 t1-Margin ~ t2+Margin , 然后过滤掉不属于 t1~t2之间的数据, 在进行Cube的构建
(29) 无法保证100% 在Margin延迟内数据完全到达,所以仍然存在丢失数据的风险. Margin设置过大,会导致延迟大大增加,(同时个人觉得,过滤前的数据量也会过大,增加过滤操作的处理时间,影响性能),Margin设置过小,数据丢失的概率就会大大增加
(30) Kylin 目前(1.5版本)不支持Web GUI上触发流式构建,需要执行命令行$KYLIN_HOME/bin/streaming_build.sh CUB_NAME INTERVAL DELAY. DELAY至少 =INTERVAL+MARGIN
(31) 自动化触发需要自己使用任务调度系统循环调用, 例如新增 Cron Job.
(32) 流式构建一旦某一个单次微构建失败, 会导致Cube中缺少某个Segment,在连续的Segment中形成一个Gap, 会阻碍自动合并的进行,导致用户查询得到的结果不完整.
(33) 一旦自动合并出错,会阻碍CUbe上所有的合并操作, 因为Cube同时只允许有一个未完成的构建操作. 新产生的Segment会迅速堆积, Cube的查询性能会迅速下降
(34) 目前(1.5版本) 流式构建使用了一个特殊的流式构建引擎,单进程, 当数据量变大的时候,整个系统出错频率会大大增加,设计还比较初级,同时还不提供对Exact-Once的语义,如果数据延迟超过Margin,数据可能会丢失
5. 查询和可视化(p122-161 )
(35) 1) web gui 查询 2) Rest Api 查询 3) ODBC 4) JDBC 5) 通过Tableau访问 kylin 6) Zeppelin集成
(36) Tableau是一款应用比较广泛的商业智能软件,有着很好的交互体验,可基于拖拽式生成各种可视化图表, 需要安装ODBC
(37) Zeppelin是Apache顶级项目的数据分析平台,后端以插件形式支持多种数据处理引擎,如Spark,Flink,Lens等, 同时还提供Notebook式的UI进行可视化相关操作
6. Cube优化 (p161 )
(38) 优化方式:1) 使用衍生维度 2) 使用聚合组 3) 并发粒度优化 4) RowKey优化 5) 降低度量精度 6) 及时清无用Segment
(39) Kylin的核心思想是根据用户的数据模型和查询样式对数据进行预计算,并在查询时直接利用预计算的结果返回查询结果.
(40) Cube的优化主要为减少Cube的数据量以及计算量, 例如, 层级(Hierarchy) 是描述若干维度之间存在层级关系的优化工具,以此跳过预计算的多余步骤,一些不会被查询到的预计算结果都应当被跳过. Cube的优化目的始终两个: 空间优化和时间优化
(41) 维度灾难: 如果没有任何优化措施, Kylin会对每一种维度的组合进行预计算,每种维度的组合的预计算结果被称为Cuboid. 假设M个维度, 或有 2^M个Cuboid. 一般维度远远大于个位数, 加入M=10 , 2^10 = 1024个Cuboid; 若 M=20 , 2^20 = 1048576个Cuboid . 单单只是数量, 就能想到这样的Cube对构建引擎,存储引擎来说压力有多大. 所以需要Cuboid的剪枝优化
(42) kylin提供工具用以检测哪些Cuboid最终被计算了, 又称其为被物化(Materialized)的Cuboid, 只要至少有一个Segment, 就能使用如下命令检查
bin/kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader CUBE_NAME
返回结果中, 会有 " |---- Cuboid 1111 " 等字段, 其中 "1111"数字长度为4, 代表有四个维度, 对应位置为1 ,则表明该维度在Cuboid中存在, 如上 "1111" 表明本Cuboid中存在所有的维度. 除了顶端Cuboid, 每一个Cuboid都有一个父节点, 且比父节点少一个1,表示子节点由父节点减少一个维度聚合而来. 顶端base Cuboid都为1,包含所有维度
(43) Cuboid状态中,Shrink 值表示这个Cuboid的行数和父节点的对比. 如果该值接近一百, 表明虽然这个Cuboid比父节点少一个维度,但是并没有比父节点Cuboid少很多行数据,即使没有这个Cuboid,我们查询其父节点,也不会有太大代价.
(44) 理论上所有能用Cuboid查询的请求都可以用Base Cuboid代替. 每个Cuboid其实都代表着一种查询样式. 如果每一种样式都需要精确匹配,则会变得很奢侈,所以可以考虑牺牲一部分查询样式的精确匹配Cuboid. 这个不精确匹配的Cuboid可能是它的父亲,或者其依次向上第一个存在的祖先. 使用不精确匹配, 需要做更多的查询时聚合计算; 如果Cube优化得当, 这样是很好的.如果Cuboid的Shrink值接近一百,这个Cuboid就可以被优化掉, 查询时使用其父节点.
(45) Cube剪枝优化: 1) 使用衍生维度 2) 使用聚合组
(46) 并不是所有维度表上的维度都应该变为衍生维度, 如果从维度表主键到某个维度表所需的聚合工作量很大,例如 从CAT_DAT到 YEAR_BEG_DT基本上需要365:1的聚合量, 所以把YEAR_BEG_DT作为普通的维度更好.
(47) rowkey编码优化: 选用适当的编码方式,减少rowkey中对于维度的存储长度, 例如选用 integer编码, 长度设定为4, 对于INt32的整数很合适. 甚至可以设置2 ,1. Date类型的数据将使用三个字节进行编码, 支持 0000-01-01 ~ 9999-01-01
01-01-0001 = 11110110100101010001 = 20 Bits
12-31-9999 = 101110111111110011111111 = 24 Bits
Total Value Range between 01-01-0001 and 12-31-9999 , 3 Bytes = 2^24 = 16,777,216