DDIA 读书分享 第三章(下):TP AP 和列存

本文涉及的产品
云原生数据仓库AnalyticDB MySQL版,基础版 8ACU 100GB 1个月
简介: DDIA 读书分享 第三章(下):TP AP 和列存

DDIA 读书分享会,会逐章进行分享,结合我在工业界分布式存储和数据库的一些经验,补充一些细节。每两周左右分享一次,欢迎加入,Schedule 在这里[1]。我们有个对应的分布式&数据库讨论群,每次分享前会在群里通知。如想加入,可以加我的微信号:qtmuniao,简单自我介绍下,并注明:分布式系统群。

事务型还是分析型

术语 OL(Online)主要是指交互式的查询。

术语事务( transaction )由来有一些历史原因。早期的数据库使用方多为商业交易(commercial ),比如买卖、发工资等等。但是随着数据库应用不断扩大,交易\事务作为名词保留了下来。

事务不一定具有 ACID 特性,事务型处理多是随机的以较低的延迟进行读写,与之相反,分析型处理多为定期的批处理,延迟较高。

下表是一个对比:

属性 OLTP OLAP
主要读取模式 小数据量的随机读,通过 key 查询 大数据量的聚合(max,min,sum, avg)查询
主要写入模式 随机访问,低延迟写入 批量导入(ETL)或者流式写入
主要应用场景 通过 web 方式使用的最终用户 互联网分析,为了辅助决策
如何看待数据 当前时间点的最新状态 随着时间推移的
数据尺寸 通常 GB 到 TB 通常 TB 到 PB

一开始对于 AP 场景,仍然使用传统数据库。在模型层面来说,SQL 足够灵活,能够基本满足 AP 查询需求。但在实现层面,传统数据库在 AP 负载中的表现(大数据量吞吐较低)不尽如人意,因此大家开始转向在专门设计的数据库中进行 AP 查询,我们称之为数据仓库(Data Warehouse)。

数据仓库

对于一个企业来说,一般都会有很多偏交易型的系统,如用户网站、收银系统、仓库管理、供应链管理、员工管理等等。通常要求高可用低延迟,因此直接在原库进行业务分析,会极大影响正常负载。因此需要一种手段将数据从原库导入到专门的数仓

我们称之为 ETL:extract-transform-load

image.png

                                    提取 转换 导入

一般企业的数据量达到一定的量级才会需要进行 AP 分析,毕竟在小数据量尺度下,用 Excel 进行聚合查询都够了。当然,现在一个趋势是,随着移动互联网、物联网的普及,接入终端的类型和数量越来越多,产生的数据增量也越来越大,哪怕初创不久的公司可能也会积存大量数据,进而也需要 AP 支持。

AP 场景下的聚合查询分析和传统 TP 型有所不同。因此,需要构建索引的方式也多有不同。

同样接口后的不同实现

TP 和 AP 都可以使用 SQL 模型进行查询分析。但是由于其负载类型完全不同,在查询引擎实现和存储格式优化时,做出的设计决策也就大相径庭。因此,在同一套 SQL 接口的表面下,两者对应的数据库实现结构差别很大。

虽然有的数据库系统号称两者都支持,比如之前的 Microsoft SQL Server 和 SAP HANA,但是也正日益发展成两种独立的查询引擎。近年来提的较多的 HTAP 系统也是类似,其为了 serve 不同类型负载底层其实有两套不同的存储,只不过系统内部会自动的做数据的冗余和重新组织,对用户透明。

AP 建模:星状型和雪花型

AP 中的处理模型相对较少,比较常用的有星状模型,也称为维度模型

image.png

                                 星状建模

如上图所示,星状模型通常包含一张事件表(fact table和多张维度表(dimension tables。事件表以事件流的方式将数据组织起来,然后通过外键指向不同的维度。

星状模型的一个变种是雪花模型,可以类比雪花(❄️)图案,其特点是在维度表中会进一步进行二次细分,将一个维度分解为几个子维度。比如品牌和产品类别可能有单独的表格。星状模型更简单,雪花模型更精细,具体应用中会做不同取舍。

在典型的数仓中,事件表可能会非常宽,即有很多的列:一百到数百列。

列存

前一小节提到的分维度表事实表,对于后者来说,有可能达到数十亿行和数 PB 大。虽然事实表可能通常有几十上百列,但是单次查询通常只关注其中几个维度(列)。

如查询人们是否更倾向于在一周的某一天购买新鲜水果或糖果

SELECT
  dim_date.weekday,
  dim_product.category,
  SUM(fact_sales.quantity) AS quantity_sold
FROM fact_sales
  JOIN dim_date ON fact_sales.date_key = dim_date.date_key
  JOIN dim_product ON fact_sales.product_sk = dim_product.product_sk
WHERE
  dim_date.year = 2013 AND
  dim_product.category IN ('Fresh fruit', 'Candy')
GROUP BY
  dim_date.weekday, dim_product.category;

由于传统数据库通常是按行存储的,这意味着对于属性(列)很多的表,哪怕只查询一个属性,也必须从磁盘上取出很多属性,无疑浪费了 IO 带宽、增大了读放大。

于是一个很自然的想法呼之欲出:每一个列分开存储好不好?

image.png

                                  列式存储

不同列之间同一个行的字段可以通过下标来对应。当然也可以内嵌主键来对应,但那样存储成本就太高了。

列压缩

将所有数据分列存储在一块,带来了一个意外的好处,由于同一属性的数据相似度高,因此更易压缩。

如果每一列中值阈相比行数要小的多,可以用位图编码( bitmap encoding[2]。举个例子,零售商可能有数十亿的销售交易,但只有 100,000 个不同的产品。

image.png

                                位图编码,游程编码

上图中,是一个列分片中的数据,可以看出只有 {29, 30, 31, 68, 69, 74} 六个离散值。针对每个值出现的位置,我们使用一个 bit array 来表示:

  1. bit map 下标对应列的下标
  2. 值为 0 则表示该下标没有出现该值
  3. 值为 1 则表示该下标出现了该值

如果 bit array 是稀疏的,即大量的都是 0,只要少量的 1。其实还可以使用 游程编码[3](RLE, Run-length encoding) 进一步压缩:

  1. 将连续的 0 和 1,改写成 数量+值,比如 product_sk = 299 个 0,1 个 1,8 个 0
  2. 使用一个小技巧,将信息进一步压缩。比如将同值项合并后,肯定是 0 1 交错出现,固定第一个值为 0,则交错出现的 0 和 1 的值也不用写了。则 product_sk = 29  编码变成 9,1,8
  3. 由于我们知道 bit array 长度,则最后一个数字也可以省掉,因为它可以通过 array len - sum(other lens) 得到,则 product_sk = 29  的编码最后变成:9,1

位图索引很适合应对查询中的逻辑运算条件,比如:

WHERE product_sk IN(30,68,69)

可以转换为 product_sk = 30product_sk = 68product_sk = 69这三个 bit array 按位或(OR)。

WHERE product_sk = 31 AND store_sk = 3

可以转换为 product_sk = 31store_sk = 3的 bit array 的按位与,就可以得到所有需要的位置。

列族

书中特别提到列族(column families)。它是 Cassandra 和 HBase 中的的概念,他们都起源于自谷歌的 BigTable[4] 。注意到他们和列式(column-oriented)存储有相似之处,但绝不完全相同:

  1. 同一个列族中多个列是一块存储的,并且内嵌行键(row key)。
  2. 并且列不压缩(存疑?)

因此 BigTable 在用的时候主要还是面向行的,可以理解为每一个列族都是一个子表。

内存带宽和向量化处理

数仓的超大规模数据量带来了以下瓶颈:

  1. 内存处理带宽
  2. CPU 分支预测错误和流水线停顿[5]

关于内存的瓶颈可已通过前述的数据压缩来缓解。对于 CPU 的瓶颈可以使用:

  1. 列式存储和压缩可以让数据尽可能多地缓存在 L1 中,结合位图存储进行快速处理。
  2. 使用 SIMD 用更少的时钟周期处理更多的数据。

列式存储的排序

由于数仓查询多集中于聚合算子(比如 sum,avg,min,max),列式存储中的存储顺序相对不重要。但也免不了需要对某些列利用条件进行筛选,为此我们可以如 LSM-Tree 一样,对所有行按某一列进行排序后存储。

注意,不可能同时对多列进行排序。因为我们需要维护多列间的下标间的对应关系,才可能按行取数据。

同时,排序后的那一列,压缩效果会更好。

不同副本,不同排序

在分布式数据库(数仓这么大,通常是分布式的)中,同一份数据我们会存储多份。对于每一份数据,我们可以按不同列有序存储。这样,针对不同的查询需求,便可以路由到不同的副本上做处理。当然,这样也最多只能建立副本数(通常是 3 个左右)列索引。

这一想法由 C-Store 引入,并且为商业数据仓库 Vertica 采用。

列式存储的写入

上述针对数仓的优化(列式存储、数据压缩和按列排序)都是为了解决数仓中常见的读写负载,读多写少,且读取都是超大规模的数据。

我们针对读做了优化,就让写入变得相对困难。

比如 B 树的原地更新流是不太行的。举个例子,要在中间某行插入一个数据,纵向来说,会影响所有的列文件(如果不做 segment 的话);为了保证多列间按下标对应,横向来说,又得更新该行不同列的所有列文件。

所幸我们有 LSM-Tree 的追加流。

  1. 将新写入的数据在内存中 Batch 好,按行按列,选什么数据结构可以看需求。
  2. 然后达到一定阈值后,批量刷到外存,并与老数据合并。

数仓 Vertica 就是这么做的。

聚合:数据立方和物化视图

不一定所有的数仓都是列式存储,但列式存储的种种好处让其变得流行了起来。

其中一个值得一提的是物化聚合(materialized aggregates,或者物化汇总)

物化,可以简单理解为持久化。本质上是一种空间换时间的 tradeoff。

数据仓库查询通常涉及聚合函数,如 SQL 中的 COUNT、SUM、AVG、MIN 或 MAX。如果这些函数被多次用到,每次都即时计算显然存在巨大浪费。因此一个想法就是,能不能将其缓存起来。

其与关系数据库中的视图(View)区别在于,视图是虚拟的、逻辑存在的,只是对用户提供的一种抽象,是一个查询的中间结果,并没有进行持久化(有没有缓存就不知道了)。

物化视图本质上是对数据的一个摘要存储,如果原数据发生了变动,该视图要被重新生成。因此,如果写多读少,则维持物化视图的代价很大。但在数仓中往往反过来,因此物化视图才能较好的起作用。

物化视图一个特化的例子,是数据立方(data cube,或者 OLAP cube):按不同维度对量化数据进行聚合。

image.png

                                       数据立方

上图是一个按日期和产品分类两个维度进行加和的数据立方,当针对日期和产品进行汇总查询时,由于该表的存在,就会变得非常快。

当然,现实中,一个表中常常有多个维度,比如 3-9 中有日期、产品、商店、促销和客户五个维度。但构建数据立方的意义和方法都是相似的。

但这种构建出来的视图只能针对固定的查询进行优化,如果有的查询不在此列,则这些优化就不再起作用。

在实际中,需要针对性的识别(或者预估)每个场景查询分布,针对性的构建物化视图。

参考资料

[1]DDIA 读书分享会: https://docs.qq.com/sheet/DWHFzdk5lUWx4UWJq

[2]bitmap encoding: https://en.wikipedia.org/wiki/Bitmap_index

[3]游程编码: https://zh.wikipedia.org/zh/%E6%B8%B8%E7%A8%8B%E7%BC%96%E7%A0%81

[4]BigTable: https://en.wikipedia.org/wiki/Bigtable

[5]流水线停顿: https://zh.wikipedia.org/wiki/%E6%B5%81%E6%B0%B4%E7%BA%BF%E5%81%9C%E9%A1%BF

相关实践学习
AnalyticDB MySQL海量数据秒级分析体验
快速上手AnalyticDB MySQL,玩转SQL开发等功能!本教程介绍如何在AnalyticDB MySQL中,一键加载内置数据集,并基于自动生成的查询脚本,运行复杂查询语句,秒级生成查询结果。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
相关文章
|
存储 缓存 5G
时域结构 | 带你读《5G 空口设计与实践进阶 》之十七
在时域,NR 支持基于符号灵活定义的帧结构,以满足各种时延需求。
时域结构 | 带你读《5G 空口设计与实践进阶 》之十七
|
6月前
|
算法 Java vr&ar
保持无损连接和函数依赖的3NF合成算法(详细简介)期末必备
保持无损连接和函数依赖的3NF合成算法(详细简介)期末必备
70 0
|
5G 索引
频域结构 | 带你读《5G 空口设计与实践进阶 》之十九
在频域,为满足多样带宽需求,NR 支持灵活可扩展的 Numerology。这相应也决定了 NR 在频域资源上的物理量度是可变的。
频域结构 | 带你读《5G 空口设计与实践进阶 》之十九
|
SQL 关系型数据库 MySQL
深聊MySQL,从入门到入坟之:如何优化数据导入?
深聊MySQL,从入门到入坟之:如何优化数据导入?
204 0
深聊MySQL,从入门到入坟之:如何优化数据导入?
|
算法 程序员 数据库
《数据库系统概论》十一章汇总--基于《数据库系统概论》第五版王珊一书|第九章--关系查询处理和查询优化
系列的博客都是基于《数据库系统概论》第五版王珊一书,进行的知识总结和课后习题汇总,从第一章到第十一章,如果觉得不错记得收藏点个赞吧~你的小小支持,是我的大大动力!
181 0
|
SQL 架构师 关系型数据库
小胖问我:group by 怎么优化?(上)
哈喽,我是狗哥,好久不见呀!是的,我又又换了工作。最近一直在面试这几天刚好整理下在面试中被问到有意思的问题,也借此机会跟大家分享下。
小胖问我:group by 怎么优化?(上)
|
SQL 存储 关系型数据库
小胖问我:group by 怎么优化?(下)
小胖问我:group by 怎么优化?
小胖问我:group by 怎么优化?(下)
|
分布式计算 Hadoop 开发者
DN 工作机制(面试重点)| 学习笔记
快速学习 DN 工作机制(面试重点)
116 0
DN 工作机制(面试重点)| 学习笔记
|
SQL 运维 算法
DTCC 2020 | 阿里云梁高中:DAS之基于Workload的全局自动优化实践
第十一届中国数据库技术大会(DTCC2020),在北京隆重召开。在12.23日性能优化与SQL审计专场上,邀请了阿里巴巴数据库技术团队高级技术专家梁高中为大家介绍DAS之基于Workload的全局自动优化实践。 SQL自动优化是阿里云数据库自治服务重要自治场景之一,该服务支撑阿里巴巴集团全网慢SQL的自动优化,目前已累计自动优化超4900万慢SQL。阿里在构建这一能力过程中有经验也有教训,期望从基于Workload的全局优化能力构建历程、智能化自动优化闭环实践两个方面和大家分享。
4246 2
DTCC 2020 | 阿里云梁高中:DAS之基于Workload的全局自动优化实践
|
5G 定位技术 虚拟化
空域结构 | 带你读《5G 空口设计与实践进阶 》之二十二
在 NR 物理层中,来自上层的业务流进行信道编码后的数据,称之为码字(Code Word)。不同的码字可以区分不同的数据流,其目的是通过 MIMO 发送多路数据,实现空分复用。由于码字数量与发射天线数量不一致,需要通过层映射和预编码将码字流映射到不同的发射天线上。层映射首先按照一定的规则将码字流重新映射到多个层(新的数据流),预编码再将数据映射到不同的天线端口上,再在各个天线端口上进行资源映射,生成 OFDM 符号并发射。
空域结构 | 带你读《5G 空口设计与实践进阶 》之二十二