1. 转换
1.1. 转换与查询不同
-
1.1.1. 查询是根据过滤和连接逻辑从各种来源检索数据
-
1.1.2. 转换将结果持久化,供其他转换或查询使用
- 1.1.2.1. 结果可以被短暂地或永久地保存
-
1.1.3. 除了持久性,转换区别于查询的另一个特点是复杂性
- 1.1.3.1. 你可能会建立复杂的数据管道,结合来自多个来源的数据,并为多个最终输出重复使用中间结果
1.2. 转换化在很大程度上依赖于本书中的一个主要底层设计:编排
1.3. 转换是数据管道的核心
-
1.3.1. 转换是为业务增加价值和投资回报率的地方
-
1.3.2. 本质上是围绕流数据获取重新配置数据栈,并使数据转换工作流更接近源系统应用程序本身
1.4. 请思考技术作为实现组织目标的工具
1.5. 把实时数据看作为了技术而技术的工程团队将重复大数据时代的错误
1.6. 雇用工程师不是为了玩最新的技术,而是为了服务客户
- 1.6.1. 使用令人兴奋的转换技术为利益相关者服务是可行的
1.7. 数据工程师还在这个系统中实现数据模型
1.8. 重点是尽可能多地创造价值,无论是在完善系统功能还是构建可靠的和值得信赖的数据方面
2. 批量转换
2.1. 批量转换在离散的数据集上运行,而流式转换是在数据到达时连续处理
-
2.1.1. 批量ETL是一种可以追溯到关系数据库早期的广泛使用的转换模式
-
2.1.2. 传统的ETL依赖于一个外部系统来拉取、转换和清洗数据,同时为目标模式做准备
-
2.1.3. ELT概念是随着数据湖的出现而普及的
-
2.1.3.1. 数据在加载时并没有被转换
-
2.1.3.2. 事实上,数据可以在没有准备和任何使用计划的情况下加载
-
2.1.3.3. 其假设是,转换步骤将在未来某个未确定的时间发生
-
2.2. 为了支持报表、数据分析和机器学习模型,批量转换会在固定的时间运行
2.3. 分布式连接
-
2.3.1. 分布式连接的基本思想是将一个逻辑连接(由查询逻辑定义的连接)分解成更小的节点连接,节点连接在集群中的各个服务器上运行
-
2.3.2. 广播连接
-
2.3.2.1. 广播连接的数据通常是不对称的,一个大表分布在各节点上,一个小表可以很容易地加载到单个节点
-
2.3.2.2. 查询引擎将小表广播到所有节点,它在每个节点都被连接到大表的一部分
-
2.3.2.3. 广播连接的计算量远小于洗牌哈希连接
-
-
2.3.3. 洗牌哈希连接
-
2.3.3.1. 如果两个表都小到可以放在一个节点上,查询引擎将使用洗牌哈希连接
-
2.3.3.2. 这种分区与连接键没有关系
-
2.3.3.3. 通常会使用连接键哈希的方式重新划分数据
-
2.3.3.4. 洗牌哈希连接通常比广播连接耗费更多资源
-
2.4. 查询优化器的首要任务之一是连接重排
2.5. 转换工具
-
2.5.1. 自从Hadoop平台上引入Hive后,SQL已经成为大数据生态系统中的一等公民
-
2.5.1.1. Spark SQL是Apache Spark的一个早期功能
-
2.5.1.2. Kafka、Flink和Beam等流优先框架也支持SQL,其特点和功能各不相同
-
-
2.5.2. SQL是声明式的,但它仍然可以建立复杂的数据工作流
-
2.5.2.1. SQL作者用集合理论语言规定他们最终数据的特征,而不是编码数据处理程序
-
2.5.2.2. SQL编译器和优化器决定将数据置于这种状态所需的步骤
-
2.6. 业务逻辑和衍生数据
-
2.6.1. 数据转换最常见的用例之一是将业务逻辑翻译成代码
-
2.6.2. 衍生数据
- 2.6.2.1. 从存储在系统中的其他数据计算出来的数据
2.7. MapReduce
-
2.7.1. MapReduce是大数据时代标志性的批处理数据转换方式,它仍然影响着许多数据工程师今天使用的分布式系统,而且对于数据工程师来说,理解它的基本概念是很有用的
-
2.7.2. 磁盘容量和带宽非常便宜,所以简单地使用大量磁盘处理数据以实现超快速查询是有意义的
-
2.7.3. 云服务商是更广泛地采用内存缓存的驱动者之一
3. 数据更新
3.1. 由于转换会持久化数据,我们经常会在原地更新持久化后的数据
3.2. 更新数据是数据工程团队的一个主要痛点,尤其是在数据工程技术之间过渡时
3.3. 更新模式
-
3.3.1. 清空和重新加载
-
3.3.1.1. 清空是一种更新模式,不更新任何东西
-
3.3.1.2. 只是简单地擦除旧数据
-
3.3.1.3. 一个表被清除了数据,重新运行转换任务然后把结果并加载到这个表中,有效地生成了一个新版本的表
-
-
3.3.2. 仅插入
-
3.3.2.1. 仅插入插入新记录而不改变或删除旧记录
-
3.3.2.2. 可用于维护最新数据的视图
3.3.2.2.1. 插入新版本的记录而不删除旧记录
-
3.3.2.3. 一个查询或视图可以通过主键找到最新的记录来呈现当前的数据状态
3.3.2.3.1. 列式数据库通常不强制要求表有主键
-
3.3.2.4. 缺点是在查询时找到最新记录的计算成本极高
-
3.3.2.5. 可以使用一个物化视图,一个维护全部记录的仅插入表,以及一个保持服务数据最新状态的清空和重新加载表
-
-
3.3.3. 建议以定期微批或批处理的方式加载数据
3.4. 针对不频繁写入的一个例外
- 3.4.1. BigQuery和Apache Druid使用的增强型Lambda架构,它将流缓冲器与列存储混合在一起
3.5. 删除
-
3.5.1. 当源系统需要删除数据以满足最新的监管变化时,删除就是非常重要的功能
-
3.5.2. 在列式系统和数据湖中,删除比插入成本更高
-
3.5.3. 硬删除是将一条记录从数据库中永久删除
- 3.5.3.1. 当你因为性能原因需要删除数据时,或者有法律或合规的原因需要这样做时,硬删除很有用
-
3.5.4. 软删除则是将该记录标记为“已删除”
- 3.5.4.1. 当你不想永久地删除一条记录,但又想把它从查询结果中过滤掉时,就可以使用软删除
-
3.5.5. 插入式删除
- 3.5.5.1. 插入式删除插入一条带有deleted标志的新记录,而不修改该记录的先前版本
3.6. upsert/merge
-
3.6.1. upsert和merge模式一直是给数据工程团队带来最大麻烦的模式,特别是对于从基于行的数据仓库转换到基于列的云系统的人来说
-
3.6.2. 分布式列式数据系统支持本地更新命令,合并也是有代价的:更新或删除单一记录的性能影响可能相当大
-
3.6.3. 和插入一样,要注意你的更新或合并频率
3.7. 模式更新
-
3.7.1. 数据是会发生变化的,而且变化可能会在你无法控制或没有你许可的情况下发生
-
3.7.2. 在数据仓库中作为一等公民提供的半结构化数据非常灵活,为数据分析师和数据科学家提供了新的机会,因为数据不再受限于行和列
- 3.7.2.1. 当数据工程师必须从具有频繁变化模式的应用程序文件存储中获取数据时,这种方法效果非常好
4. 数据整理
4.1. 数据整理将混乱的、畸形的数据,变成有用的、干净的数据
4.2. 数据整理一直是数据工程师的主要痛点和工作的价值点
4.3. 自动化工具允许数据工程师把时间花在更有趣的任务上
-
4.3.1. 整理工具也可以让工程师把一些解析和获取的工作交给分析师
-
4.3.2. 图形化的数据整理工具通常在一个可视化的界面中展示数据样本,包括推断的类型、统计数据,包括分布、异常数据、异常值和空值
5. 物化视图、联邦查询和数据虚拟化
5.1. 视图
-
5.1.1. 为物化视图做个铺垫,让我们回顾一下视图
-
5.1.2. 视图是一个数据库对象,我们可以像其他表一样从中查询
-
5.1.3. 当我们从一个视图中查询时,该数据库会创建一个新的查询,将子查询与我们的查询相结合,然后,查询优化器对完整的查询进行优化
-
5.1.4. 视图可以用来帮助展示去重后的数据
-
5.1.5. 视图可以用来展示常见的数据访问模式
5.2. 物化视图
-
5.2.1. 非物化视图的一个潜在缺点是它们不做任何预计算
-
5.2.2. 物化视图提前进行了部分或全部的视图计算
-
5.2.3. 根据不同的数据库,物化视图也可以起到重要的查询优化作用,即使是不直接引用它们的查询
-
5.2.4. 物化视图不允许组合,也就是说,一个物化视图不能从另一个物化视图中查询数据
5.3. 可组合的物化视图
5.4. 联邦查询
-
5.4.1. 联邦查询是数据库的一种功能,它允许OLAP数据库从外部数据源查询数据
-
5.4.2. Snowflake支持在S3桶上定义的外部表
5.5. 数据虚拟化
-
5.5.1. 数据虚拟化与联邦查询密切相关,但通常需要一个不在内部存储数据的处理和查询系统
-
5.5.2. Trino(如Starburst)和Presto是比较优秀的例子
-
5.5.3. 任何支持外部表格的查询/处理引擎都可以作为一个数据虚拟化引擎
-
5.5.4. 数据虚拟化最重要的考虑因素是支持外部数据源和性能
-
5.5.5. 一个密切相关的概念是查询下推
-
5.5.5.1. 询下推的目的是将尽可能多的工作转移到源数据库
-
5.5.5.2. 计算引擎可能会设法将过滤条件推送到源系统的查询中
-
5.5.5.3. 它减少了虚拟化层的计算量,利用了源系统的查询性能
-
5.5.5.4. 减少了必须通过网络推送的数据量,这是虚拟化性能的另一个关键瓶颈
-
-
5.5.6. 数据虚拟化可以作为数据获取和处理管道的一个组成部分
-
5.5.7. 数据虚拟化可以被看作一种通过去除组织间数据孤岛并将数据湖扩展到更多的数据源的工具
6. 流转换和处理
6.1. 流查询动态地呈现数据的当前状况
6.2. 流转换的目的是为下游消费准备数据
6.3. 转换和查询是一个连续的过程
6.4. 在批处理中,转换和查询之间的界限很模糊,但在流数据领域,两者的区别变得更细微
6.5. 流式有向无环图
- 6.5.1. 一个与流数据增强和连接相关的术语是流式有向无环图
6.6. 微批处理
-
6.6.1. 一种将面向批处理的框架应用于流的方式
-
6.6.2. 一个微批处理可能以每两分钟到每秒钟的频率运行
- 6.6.2.1. 一些微批处理框架(如Apache Spark Streaming)就是为这种用例而设计的,在适当分配资源的情况下,较高的批处理频率性能会很好
6.7. 真正的流处理系统(例如Beam和Flink)一次只处理一个事件
6.8. 领域知识和实际测试是无可替代的
7. 上游利益相关者
7.1. 上游利益相关者可以分成两大类:控制业务定义的人和控制系统生成数据的人
7.2. 当与上游利益相关者就业务定义和逻辑进行交流时,你需要了解数据源
- 7.2.1. 它们分别是什么,它们如何被使用,以及涉及的业务逻辑和定义是什么
7.3. 数据工程师需要参与到数据模型的设计中,并在以后因为业务逻辑变化或新的流程而进行更新数据模型
7.4. 上游利益相关者希望确保你的查询和转换对他们的系统影响最小
8. 下游利益相关者
8.1. 转换是数据开始向下游利益相关者提供服务的地方
8.2. 下游利益相关者
-
8.2.1. 数据分析师、数据科学家、机器学习工程师和业务人员
-
8.2.2. 与他们合作,确保你提供的数据模型和转换是高性能且有用的
8.3. 在性能方面,查询应该以最具成本效益的方式尽可能快地执行
8.4. 分析师、数据科学家和机器学习工程师应该能够查询数据源,并确信数据具有最高的质量和完整性,能被集成到他们的工作流和数据产品中
8.5. 业务人员应该能够相信转换后的数据是准确的和可使用的
9. 底层设计
9.1. 安全
-
9.1.1. 查询和转换将不同的数据集组合成新的数据集
-
9.1.2. 如果有人确实可以访问一个数据集,则要继续控制谁可以有数据集的列、行和单元格级别的访问权限
-
9.1.3. 在查询时要注意针对你的数据库的攻击向量
-
9.1.3.1. 对数据库的读写权限必须进行严格的监视和控制
-
9.1.3.2. 对数据库的查询访问的控制,必须以你的组织对系统和环境的访问控制相同的方式进行
-
-
9.1.4. 保证身份验证信息的隐蔽性
-
9.1.5. 避免复制和粘贴密码、访问令牌或其他凭证到代码或未加密的文件
-
9.1.6. 不要让不安全或未加密的数据通过公共互联网传输
9.2. 数据管理
-
9.2.1. 尽管数据管理在源系统阶段(以及数据工程生命周期的其他每个阶段)是必不可少的,但在转换阶段尤其关键
-
9.2.2. 让所有的利益相关者参与到数据模型和转换中来并管理他们的期望是至关重要的
-
9.2.3. 正确的命名约定应该反映在易于理解的字段名中
-
9.2.4. 用户也可以在数据目录中查看,以更清楚地了解字段创建时的含义,谁在维护数据集以及其他相关信息
-
9.2.5. 对定义的准确性进行解释是转换阶段的关键
-
9.2.6. 数据转换使你很难知道一个数据集是如何从同一行衍生出的
-
9.2.6.1. 当我们转换数据时,数据血缘工具变得非常有价值
-
9.2.6.2. 数据血缘工具既可以帮助数据工程师在创建新的转换时了解以前的转换步骤,也可以帮助分析师在运行查询和建立报告时了解数据的来源
-
9.3. DataOps
-
9.3.1. 向基于SaaS的分析数据库的转变改变了数据消费的成本状况
-
9.3.2. 以实际用量为收费基础的云数据仓库的数据工程师需要专注于成本管理和成本优化
- 9.3.2.1. FinOps
9.4. 数据架构
-
9.4.1. 构建健壮的能够处理和转换数据系统的同时保证稳定性
-
9.4.2. 对数据获取和存储的技术选型将直接影响你执行可靠的查询和转换
-
9.4.3. 如果获取和存储适合你的查询和转换模式,你就不会有什么问题
9.5. 编排
-
9.5.1. 数据团队经常使用简单的基于时间表的方式来管理他们的数据转换管道
-
9.5.2. 使用基于依赖关系的编排工具来管理复杂的管道
-
9.5.3. 编排工具也是一种黏合剂,使我们能够组装跨越多个系统的管道
9.6. 软件工程
-
9.6.1. 数据工程师负责设置分析师和数据科学家使用的代码库和CI/CD管道
-
9.6.2. 使用一个基于图形用户界面的低代码工具,你可以得到转换工作流的可视化展示
- 9.6.2.1. 了解幕后的代码将有助于调试和性能优化
-
9.6.3. 虽然简单地在数据集上投入更多的处理资源可以在一定程度上解决问题,但知道如何编写干净的、高性能的代码是一个更好的方法