# 大数据体系全景图

# 消息队列

所谓消息队列,其实就是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。

通过提供消息传递和消息排队模型,它可以在分布式环境下提供应用解耦、弹性伸缩、冗余存储、流量削峰、异步通信、数据同步等等功能,其作为分布式系统架构中的一个重要组件,有着举足轻重的地位。

特点
采用异步处理模式:消息发送者可以发送一个消息而无须等待响应。消息发送者将消息发送到一条虚拟的通道(主题或队列)上,消息接收者则订阅或是监听该通道。

一条信息可能最终转发给一个或多个消息接收者,这些接收者都无需对消息发送者做出同步回应。整个过程都是异步的。

应用系统之间解耦合

  • 发送者和接受者不必了解对方、只需要确认消息。
  • 发送者和接受者不必同时在线。

比如在线交易系统为了保证数据的最终一致,在支付系统处理完成后会把支付结果放到消息中间件里,通知订单系统修改订单支付状态。两个系统是通过消息中间件解耦的。

# 分析引擎

  • 批式分析:Spark,Hive,MapReduce
  • 实时分析:Flink
  • 交互分析:Presto,ClickHouse,Doris

# 为什么 SQL 如此流行?

为什么以 SQL 开篇? One SQL rules big data all

  • SQL 难度小,用户面广,处理数据方便(有大量数据科学家和数据分析师等不太会编程语言但又要使用数据的人)
  • 有 MySQL,Oracle 之类使用 SQL 作为交互语言的数据库
  • SQL 被很多系统所支持接口(JDBC,ODBC)
  • 多个大数据计算引擎都支持 SQL 作为更高抽象层次的计算入口

    众多的分析引擎中 Spark,MapReduce,Flink 本身不提供 SQL 接口,只提供了一些编程框架(RDD),API,后来逐渐形成自身的模块,比如 MapReduce 有 Hive SQL,Spark 有 Spark SQL,Flink 有 Flink SQL;而交互分析中的三个引擎本身就提供 SQL 接口。

# SQL 的一生

# Parser

AST 是 abstract syntax tree 的缩写,也就是抽象语法树。涵盖编译原理前两个阶段:词法分析和语法分析。其中,词法分析,这里是对某些段 SQL 代码解析,成一个个的 token;首先要定义 token 的结构,要考虑解析出来的 token 的类型(关键词,数值常量,字符串常量,运算符号……),下面读取一个 char,判断是否为注释,如是则直接跳过,否则进一步判断是否是字符串,是否是小数,输出一个个有意义的 token 词块。

SQL 的 Token 可以分为如下几类

  • 关键字(select、from、where)
  • 操作符(+、-、>=)
  • 开闭合标志((、CASE)
  • 占位符(?)
  • 注释
  • 空格

完后,根据语法规则将 token 词块组成 AST node,最终得到一个 AST。

select ... from ... where ... group by ... order by ...

Doris 中,词法分析采用 jflex 技术,语法分析采用 java cup parser 技术,最后生成抽象语法树 AST。

不同类型的查询 select, insert, show, set, alter table, create table 等经过 Parse 阶段后生成不同的数据结构(SelectStmt, InsertStmt, ShowStmt, SetStmt, AlterStmt, AlterTableStmt, CreateTableStmt 等),但他们都继承自 Statement,并根据自己的语法规则进行一些特定的处理。例如:对于 select 类型的 sql, Parse 之后生成了 SelectStmt 结构。

SelectStmt 结构包含了 SelectList,FromClause,WhereClause,GroupByClause,SortInfo 等结构。这些结构又包含了更基础的一些数据结构,如 WhereClause 包含了 BetweenPredicate(between 表达式), BinaryPredicate(二元表达式), CompoundPredicate(and or 组合表达式), InPredicate(in 表达式)等。

AST 中所有结构都是由基本结构表达式 Expr 通过多种组合而成

select name from user where id = 1 and age > 20;

# 实现

  • 递归下降(ClickHouse)
  • Flex(词法分析)和 Bison(语法分析)(PostgreSQL)
  • JavaCC(Flink)
  • Antlr(Presto,Spark)

bison 会给出二义性冲突报告,并全程解析输入时都选定其中一种方式。

# Analyzer

Analyze 主要是对 Parse 阶段生成的抽象语法树 AST 进行一些前期的处理和语义分析,为生成单机逻辑计划做准备。

  • 检查 SQL 中的 Cluster,Database,Table,Column 等元信息,是否合法,确定需要对哪个集群的哪个数据库的哪些表的哪些列进行计算。
  • 检查 SQL 的合法性,比如 min/max/avg 的输入是否为数值
  • 把 AST 转换为逻辑执行计划 Logical Plan

抽象语法树是由 StatementBase 这个抽象类表示。这个抽象类包含一个最重要的成员函数 analyze (),用来执行 Analyze 阶段要做的事。

不同类型的查询 select, insert, show, set, alter table, create table 等经过 Parse 阶段后生成不同的数据结构(SelectStmt, InsertStmt, ShowStmt, SetStmt, AlterStmt, AlterTableStmt, CreateTableStmt 等),这些数据结构继承自 StatementBase,并实现 analyze () 函数,对特定类型的 SQL 进行特定的 Analyze。

例如:select 类型的查询,会转成对 select sql 的子语句 SelectList, FromClause, GroupByClause, HavingClause, WhereClause, SortInfo 等的 analyze ()。然后这些子语句再各自对自己的子结构进行进一步的 analyze (),通过层层迭代,把各种类型的 sql 的各种情景都分析完毕。比如 WhereClause 进一步分析其包含的 BetweenPredicate(between 表达式), BinaryPredicate(二元表达式),CompoundPredicate(and or 组合表达式), InPredicate(in 表达式)等。

参考:【Doris 全面解析】Doris SQL 原理解析

# Logical Plan

主要是根据 AST 抽象语法树生成代数关系,也就是俗称的算子树。树上的每个节点都是一个算子,代表着一种操作。

所谓逻辑计划树,可以理解为逻辑地描述一个 SQL 如何一步步地执行查询和计算,最终得到执行结果的一个分步骤地计划。其实就是根据 AST 抽象语法树生成代数关系,也就是俗称的算子树。树上的每个节点都是一个算子,代表着一种操作。树中每个节点是是一个算子,定义了对数据集合的计算操作(过滤,排序,聚合,连接),边代表了数据的流向,从孩子节点流向父节点。之所以称它为逻辑的,是因为算子定义的是逻辑的计算操作,没有指定实际的算法,比如对于逻辑的排序算子,逻辑计划树里没有指定使用快排还是堆排(TOPN)。

SELECT country.name, SUM(weblog.bytes) as total        #A
FROM country                                           #B
INNER JOIN geoip ON country.id = geoip.country_id      #B
INNER JOIN weblog ON geoip.host = weblog.host	       #B
WHERE weblog.reply = "200" and weblog.host is not null #C
GROUP BY country.name                                  #D
ORDER BY total                                         #E
LIMIT 10                                               #F
#A 查询国家名以及每个国家的总字节数,当成 total
#B 内连接:weblog.host 映射到 geoip.host,后者映射到 country.name 
#C 只考虑 weblog.reply=200 以及有效 weblog.host
#D 按国家聚合
#E 按 total 排序
#F 取 TOP10

如果不进行优化,生成的关系代数下发到存储中执行的代价非常高。

为什么是左深树?

需要尽可能避免通过网络进行数据交换,所以通过将最大的表放在左侧,较小的表放在右边,为什么?

在 HDFS 中,左侧数据不会很大,因为只要扫描表本地 HDFS 块,重要的是要认识到右侧的表不是本地的,必须通过网络进行交换,所以较小的表出现在连接符的右侧。

# 查询优化

SQL 只是一种声明,只告诉数据库我要做什么,查询优化的目标就是为了让 SQL 找到一个启发式的执行计划,查询优化器是数据库的大脑,一般 SQL 越复杂,Join 的表越多,数据量越大,查询优化的意义就越大,因为不同执行方式的性能差别可能有成百上千倍,类比 gcc,如果不指定优化标识,gcc 产生可调试代码,每条指令间相互独立;启用优化标识后,gcc 就会改变程序的结构(保证语义前后等价),以满足运行速度更快,说明编译优化的程序运行效率更高。

# Physical Plan

sql 在分布式环境下的拆分

有了单机的 PlanNode 树后,就需要进一步根据分布式环境,拆成分布式 PlanFragment 树,PlanFragment 用来表示独立的执行单元。

上图 1 所示,这是优化后的逻辑计划,每个分片都用 F# 注释;下面的是物理计划,比如 F#1 分片(扫描 weblog)部署在节点 1,2 上,而分片 F#4(扫描 country 表)仅部署在节点 4 上。

分片的目的是通过最大化数据局部性来最小化节点之间的整体数据传输,每个计划运算符都转换为其分布式版,查询优化器按照最小化网络传输目标把逻辑计划拆分成多个物理计划片段。

# 常见的查询优化器

# 按照树遍历顺序分类

# Top-down

从目标输出开始,由上往下遍历计划树,找到完整的最优执行计划

例子:Volcano/Cascade,SQLserver

# Bottom-up Optimizer

从零开始,由下往上遍历计划树,找到完整的执行计划

例子:System R,PostgreSQL,IBM DB2

# 按照优化的方法分类

# RBO

根据关系代数等价语义,重写查询;基于经验式,启发式规则(Rule-Based Optimizer),依赖前人总结出来的优化规则,简单且能覆盖到大部分优化逻辑;会访问元信息(类似于访问 namenode),不会涉及具体的 data(访问 datanode)。该优化器按照硬编码在数据库中的一系列规则来决定 SQL 的执行计划,只要按照规则来写 SQL 语句,不用管数据表中的内容如何,数据分布如何,都不会影响到执行计划。

RBO 基于关系代数的等价变化,等价交换满足结合律,交换律,传递性

image.png

对于以下 sql 查询

SELECT pv.siteld, user.name
FROM pv JOIN user
ON pv.siteld = user.siteld AND pv.userld = user.id
WHERE user.siteld > 123;

优化之前的逻辑计划为

image.png

以下优化方法有列剪裁,谓词下推,传递闭包,runtime filter,每种优化策略的执行顺序可以自定义

# 列剪裁

当用到一个表时,不需要扫描它的所有列值,对于没用到的列,则没有必要读取它们的数据去浪费无谓的 IO 和内存占用。这一优化一方面大幅度减少了网络、内存数据量消耗便于了后序遍历,另一方面对于列式存储数据库来说大大提高了扫描效率。

image.png

如图,可以看出扫描 pv 表和 user 表,其中分别只用到了 siteID,userID 和 id,siteID,name,SQL 查询该怎么写,还是怎么写,但是算法还是比优化前高明很多,自顶向下把所有算子遍历,某个节点需要用到的列就等于他自己需要用到的列加上他的父节点所需要用到的列,这样遍历下来得到整个 SQL 查询最终涉及到的所有列,那么在读取数据时,只读取需要的列即可。具体的,对于上述例子, SELECT pv.siteld, user.name ,Projection 算子用到 pv 的 siteID 列和 user 的 name 列, ON pv.siteld = user.siteld AND pv.userld = user.id ,除了 Projection 算子用过的列,还有 Join 算子用到的 user 的 siteID 列,ID 列和 pv 表用到的 userID 列,数据源读取数据时,其它列则不用读取,可以裁剪掉。

算子和 SQL 有怎样的映射关系呢?

算子SQL
Selectionwhere 条件
Projection搜索的列
Sort排序 order 列
Join等值连接
Aggregationgroup by 及其它聚合操作

# 谓词下推

image.png

从表观上来看,谓词下推就是将 filter 操作下推到 join 之前进行,为了先过滤,之后再进行 join 时,数据量会得到逐渐减少;join 招谁惹谁了吗?join 算子是一个非常耗时的算子,耗时多少取决于参与 join 的两表大小,所以如何能减少参与 join 的两表大小,即可降低 join 算子的耗时;具体的, WHERE user.siteld > 123; ,由于只有 user 表中有过滤条件,所以 pv 表中的扫描完的数据可以直接参与 join,而 user 表则经过过滤后再参与 join。谓词不管怎样都能下推,因为条件至少会被一张表满足,至少有一边能提前过滤。

# 传递闭包

image.png

原理很直白,因为一些比较操作符具有传递性,可以简化表达式。具体的,因为 ON pv.siteld = user.siteld...WHERE user.siteld > 123 ,所以在 pv 表参与 join 之前,还可以把 pv 表过滤一遍 pv.siteld = user.siteld > 123

# Runtime Filter

image.png

hash join 分为两个阶段,build 构造阶段和 probe 探测阶段,Runtime Filter 基本原理是通过在 join 的 probe 阶段提前过滤掉那些不会命中 join 的输入数据来大幅减少 join 中的数据传输和计算,从而减少整体的执行时间。例如 pv.siteld = user.siteld > 123 ,虽然 pv 表的 siteID 在上一步谓词传递闭包之后,已经得到了过滤,但是如果提前进行过滤掉,还可以减少数据的传输和计算的开销,这样一来 pv 表的数据就经过了两个 filter。

从上图可以看出,加入 runtime filter 之后,在进行 join 的 build 阶段拉取数据的过程中新增了一个 RuntimeFilterBuilder 的一个算子,这个算子的作用就是在运行的过程中收集 build 端的信息形成 runtime filter,并且发送到 probe 端的 scan 节点中去,让 probe 端的节点可以在 scan 的就减少输入的数据,从而实现性能的提升。

  • filter (运行时)
  • min-max(对扫描限制范围),他有什么缺陷?
  • in list 比如包含一些间断的数 0-100,10000,缺陷,如果集合很大,网络传输开销
  • bloom filter 大小不随集合大小而改变,固定大小,给我一个数,构建右侧哈希表时,同时构建一个 bloom filter,每次查询时扫描 bloom filter (在后面存储引擎还会讲)
  • Runtime filter 分支会生成一个 reducer,该 reducer 运行 GroupByOperator,通过定义的 min、max、bloomfilter 三个聚合函数生成三个值。随后的 ReducerSinkOperator 会将这三个值序列化。

# RBO 总结

  • 主流 RBO 实现一般都有几百条基于经验归纳得到的优化规则
  • 优点:实现简单,优化速度快
  • 缺点:不保证得到最优的执行计划
  • 单表扫描:索引扫描(随机 I/O)vs. 全表扫描(顺序 I/O):如果查询的数据分布非常不均衡,索引扫描可能不如全表扫描,这是肯定的,我必须得预先知道大致位置才会用索引来查,就像查字典,但是如果有一本乱序字典,我还不如一页一页翻,没准哪天就翻到了呢?翻查索引在这里毫无价值!
  • 如果查询的数据分布非常不均衡,索引扫描可能不如全表扫描
  • join 的实现:Hash Join vs SortMerge Join
  • 两表 Hash Join:用小表构建哈希表,如何识别小表?如果选择错误的一边构建哈希表容易导致内存溢出
  • 多表 Join:那种连接顺序最优,是否要对每种组合都探索,N 个表连接,仅仅是左深树就得有将近 N! 中连接顺序。

# CBO

Cost-Based Optimizer,顾名思义,基于代价的优化器,该优化器通过优化规则对关系表达式进行转换,生成多个执行计划,然后 CBO 会统计根据统计信息代价模型计算各种可能的执行计划代价 COST,从中选用 COST 最低的执行方法为实际运行方案。

把代价量化了,执行计划的代价等于所有算子的执行代价之和,通过 RBO 得到所有可能的等价执行计划(应该是与 RBO 得出的方案比较 COST)。具体每个算子的代价,影响因素有 CPU,内存,磁盘IO,网络IO等代价

Spark Join算子代价=weight * row_count+(1.0-weight) * size

image.png

# 统计信息

CBO 依赖数据库对象的统计信息,统计信息的准确与否会影响 CBO 做出最优的选择。

  • 原始表统计信息

    • 表或者分区级别:行数、行平均大小、表在磁盘中占用了多少字节等
    • 列级别: min、max、num nulls(无效数)、num not nulls(有效数)、num distinct value (NDV)(不重复值)、histogram(直方图) 等
  • 推导统计信息

    • 选择率 (selectivity) : 对于某一个过滤条件,查询会从表中返回多大比例的数据, 满足条件的行数/总行数
    • 基数(cardinality) :在查询计划中常指算子需要处理的行数,人话就是结果集的行数

基数很重要!远比代价模型本身重要

如何收集统计信息?三种方式:

  • 在 DDL 里指定需要收集的统计信息,数据库会在数据写入时收集或者更新统计信息
  • 手动执行 explain analyze statement,触发数据库收集或者更新统计信息
  • 动态采样,统计行数信息

假设列与列之间相互独立,列值服从均匀分布,统计信息推导规则过滤器的选择率满足集合运算关系:and,or,not,等于,小于 (归一化?)...

存在的问题:在没有收集直方图的情况下,CBO 统计信息的做法是假设列与列之间是独立的,列的值服从均匀分布,很明显这个假设与现实可能不符,对于有数据倾斜的表,这种假设将大大失真。

  • 如果两个具体的属性是相关联的,就像比亚迪和汉,这样可以通过用户指定或者数据库自动识别相关联的列,排除了事件独立性和均匀分布的假设
  • 如果各属性值不服从均匀分布,比如中国人口数据库中,性别,年龄,数量都不是均匀分布,可以用直方图

# 执行计划枚举

回顾之前 RBO 无法解决的问题

  • 单表扫描:索引扫描(随机 IO)vs 全表扫描(顺序 IO)
  • 何种 Join?Hash Join or SortMerge Join?
  • 两表 Hash Join:用小表构建哈希表,和 RBO 一样,如何识别小表,如果反了,内存会溢出吗?
  • 多表 Join:那种连接顺序最优?是否要对每种组合都探索?因为连接顺序可能会很多

通常使用贪心算法或者 ** 动态规划(基于成本最优假设)** 选出最优的执行计划。

简述

从局部最优到整体最优,所以将问题分解为寻找局部最优。

比如如下 SQL 查询

SELECT * from R,S,T
WHERE R.a=S.a
AND S.b=T.b

对于这个自连接,TSR 表如何连接,三表连接拆解成两两连接,连接方式有 Hash Join 和 SortMerge Join,每次选择代价最小的连接方式,完后,逐渐增大问题的规模。

image.png
image.png

image.png
image.png

# 效果

选用 TPC-Q25 这篇文档里展示了使用 CBO 后前后的对于这个查询优化效果。

SELECT
i_item_id,
i_item_desc,
s_store_id,
s_store_name,
sum(ss_net_profit) AS store_sales_profit,
sum(sr_net_loss) AS store_returns_loss,
sum(cs_net_profit) AS catalog_sales_profit
FROM
store_sales, store_returns, catalog_sales, date_dim d1, date_dim d2, date_dim d3,
store, item
WHERE
d1.d_moy = 4
AND d1.d_year = 2001
AND d1.d_date_sk = ss_sold_date_sk
AND i_item_sk = ss_item_sk
AND s_store_sk = ss_store_sk
AND ss_customer_sk = sr_customer_sk
AND ss_item_sk = sr_item_sk
AND ss_ticket_number = sr_ticket_number
AND sr_returned_date_sk = d2.d_date_sk
AND d2.d_moy BETWEEN 4 AND 10
AND d2.d_year = 2001
AND sr_customer_sk = cs_bill_customer_sk
AND sr_item_sk = cs_item_sk
AND cs_sold_date_sk = d3.d_date_sk
AND d3.d_moy BETWEEN 4 AND 10
AND d3.d_year = 2001
GROUP BY
i_item_id, i_item_desc, s_store_id, s_store_name
ORDER BY
i_item_id, i_item_desc, s_store_id, s_store_name
LIMIT 100

优化前

image.png

作为随机连接执行,连接 #1 输出 1.99 亿行。总的来说,当 CBO 被禁用时,查询需要 241 秒。

优化后

image.png

通过基于成本的优化,Spark 创建了一个最佳连接计划,以减少中间数据大小,在这种情况下,Spark 会创建一棵浓密的树而不再是左深树。启用 CBO 后,Spark 首先将事实表与其对应的 date_dim 维度表连接起来(在尝试任何事实到事实连接之前)。避免大型事实到事实连接意味着避免大型的 shuffle。在此查询中,这将中间数据大小减少到大约 1/6(与前一种情况相比)。结果,Q25 只用了 71 秒,加速了 3.4 倍

image.png

  • 关闭 CBO:shuffle 数据量太大,执行效率差
  • 开启 CBO:减少 90% shuffle 数据量,加速 3.4 倍
  • 大约一半的 TPC-DS 基准查询没有显示性能变化。因为即使没有 CBO,Spark 的 Catalyst 优化器中现有的启发式方法也能很好地优化这些查询,对于其余查询,CBO 对查询计划都有很大的影响,将性能提升了 30% 以上。平均加速比 2.2x,最大能到 8x

# 查询优化器的社区开源实践

数据库SQL Optimizer 选型
Hive、Flink、Alibaba MaxCompute 等基于 Apache Calcite,属于 Volcano/Cascade 框架
Greenplum、HAWQ自研 Orca,属于 Volcano/Cascade 框架
Alibaba Hologres(定位 HSAP)基于 Orca,属于 Volcano/Cascade 框架
TiDB自研,属于 Volcano/Cascade 框架
Spark自研,RBO+CBO
Presto自研,RBO+CBO
Doris自研,RBO+CBO
ClickHouse自研,RBO
Alibaba OceanBase自研,RBO+CBO

ClickHouse 只有 RBO 没有 CBO

# Apache Calcite 概览

内置 RBO 和 CBO,支持异构数据模型(关系型,半结构化,流式,地理空间数据),模块化,插件化?(),稳定可靠,One size fits all(统一的 SQL 查询引擎)

image.png

SQL 经过 JDBC server 处理,再经过 SQL Parser 和 Validator 对 SQL 做检验和解析,得到 AST,转换成关系表达式(算子...),Calcite 定位能够对接不同的 Data Processing 系统,提供了表达式 Builder,所以构建关系表达式不一定必须经过解析,完后,进入查询优化器,它会从 Metadata Providers 中拿一些统计信息,表元信息,同时还提供了一个插件化的 RBO 规则,对系统提供更好的兼容性。

# SQL 相关的前沿趋势

# 存储计算分离

我们知道 CPU 是由控制器、运算器和寄存器组成的,我们在运行一段程序的时候我们的指令是存储在我们的存储器的,我们所执行的每一个步骤都和存储分离不开。

计算和存储分离并不是现在才出现的一个新名词,在 20 年前就有 NAS - 网络附加存储这个东西,本质上也就是使用 TCP/IP 协议的以太网文件服务器。当时如果想要大规模的存储,就会让服务器将数据保存到 NAS 这个上面,但是 NAS 价格及其昂贵,并且扩展比较困难,NAS 也就不适用于高速发展的互联网应用。

这个时候谷歌摒弃了之前的观念 “移动存储到计算”,采取了 “移动计算到存储的观念”,将计算和存储耦合了,因为当时的网络速度对比现在来说慢了几百倍,网络速度跟不上我们的需要。在典型的 MapReduce 部署中计算和存储都在同一个集群中进行,比如后续的 hadoop。这里其实也就是用本地 IO 速度来替换网络传输速度

随着技术的进步,我们的网络速度也越来越快,我们的瓶颈不再是网络速度,但是我们的磁盘 I/O 速度却没有明显的速度增长,计算和存储融合的架构缺点也再逐渐暴露,由于计算和存储耦合的缺点越来越多,并且网络速度越来越快,现在架构又在重新向计算和存储分离这一方向重新开始发展。

应用于数据库和消息队列。

参考:MySQL 存算分离解析(杂谈) & 聊聊计算和存储分离

# HSAP, HTAP, HTSAP

HSAP 与 HTAP 都会成为企业数据架构中不可或缺的重要组成部分,而在应对有规模企业,特别是当互联网 / 物联网应用不断扩大时,企业分析查询对大数据有着越来越高的需求,那么这时,HSAP 就有了其更加不可或缺的作用。而对 HTAP 数据库来讲,虽然在技术实现上并不会太简单,但从本质上讲,HTAP 在对其分布式事务能力进行妥协后,应该也有同时具备 HSAP 能力的潜能。

参考:关于 HTAP 与 HSAP

# Cloud Native, Serverless

关于云原生和无服务器之间的联系

云原生比云计算多了一个 native,阿里某位大佬说过:“因云而生的软件,硬件,架构,就是真正的云原生,因云而生的技术,就是云原生技术。”

云原生是一个更加泛的概念,他表示的是在云计算领域中的一部分,是在云且原生的部分。那么 Serverless 架构呢,它本身也是因云而生,成长在云,Serverless 架构是一种技术架构,而云原生是一种泛概念,他的外面还有云计算。

参考:Cloud Native (云原生)和 Severless (无服务器)之间具体有怎样的联系?

# 数据仓库,数据湖,湖仓一体,联邦查询

参考:一文读懂数据仓库、数据湖、湖仓一体

# 数据仓库

Data Warehouse(DW/DWH),早期系统采用关系型数据库来存放管理数据,但是随着大数据技术的兴起,人们对于多方面数据进行分析的需求愈加强烈,这就要求建立一个能够面向分析、集成保存大量历史数据的新型管理机制,这一机制就是数据仓库。

数据仓库通常存储来自不同源的数据,集成源数据以提供统一的视图。这些资源可以包括事务系统、应用程序日志文件、关系数据库等等。

特征

  • 数据仓库的数据是面向主题的
  • 数据仓库的数据是集成的
  • 数据仓库的数据是随时间不断变化的
  • 数据仓库的数据是非易失的

# 数据湖

Data Lake 是一个以原始格式存储数据存储库或系统,它按原样存储数据,而无需事先对数据进行结构化处理。无论是否结构化或半结构化亦或是二进制数据如图形,音频,视频都可以存

特征

  • 容量大
  • 格式多
  • 处理速度快
  • 体系结构,数据湖是由多个组件构成的生态系统

Hadoop 相比于数据湖,虽然 Hadoop 基于分布式,可横向扩展文件系统架构,可以管理和处理海量数据,但是它无法提供数据湖所需要的复杂元数据管理功能。根据数据湖的体系结构,数据湖是由多个组件构成的生态系统,而 Hadoop 仅仅提供了其中的部分组件功能。

# 湖仓一体

数据湖虽然适合数据的存储,但是缺少一些关键功能,不支持事务,缺乏一致性,隔离性,不能保证执行数据质量,这样的短板决定了,让数据湖来承载读写访问、批处理、流作业是不现实的。而且,数据湖缺乏结构性,一旦没有被治理好,就会变成数据沼泽

既然都是拿数据为业务服务,数据湖和数仓作为两大 “数据集散地”,能不能彼此整合一下,让数据流动起来,少点重复建设呢?,于是,Databricks 率先提出了湖仓一体(Data Lakehouse)的概念。

湖仓一体是一种结合了数据湖灵活性和数据仓库规范性优势的新范式,在基于数据湖的低成本存储上,实现与数据仓库中类似的数据结构和数据管理功能。

Data Lakehouse 的出现试图去融合数仓和数据湖这两者之间的差异,通过将数仓构建在数据湖上,使得存储变得更为廉价和弹性,同时 lakehouse 能够有效地提升数据质量,减小数据冗余。

在 lakehouse 的构建中,ETL(Extract-Transform-Load)起了非常重要的作用,它能够将未经规整的数据湖层数据转换成数仓层结构化的数据。

特征

  • 支持事务,可以处理多条不同的数据管道,意味着它可以在不破坏数据完整性的前提下支持并发的读写事务
  • 根据应用需求为绝大多数数据施加 schemas,标准化
  • 报表以及分析应用的支持
  • 数据类型扩展,这一点结合数据湖的优势
  • 端到端的流式支持,支持流式分析,从而能够满足实时报表的需求
  • 计算存储分离,数据存储在一个集群中,而在另一个集群中进行处理
  • 开放性(跨构件,引擎,语言操作)