├── .nojekyll
├── 分布式调度
├── 调度系统架构
│ ├── README.md
│ ├── 体系结构
│ │ └── 集中式架构.md
│ ├── 两层调度.md
│ ├── 单体调度.md
│ └── 共享状态调度.md
├── README.md
├── 延时任务
│ └── README.md
├── 流程引擎
│ ├── Activiti
│ │ └── README.md
│ ├── BPMN
│ │ ├── BPMN 规范.md
│ │ └── README.md
│ └── README.md
└── 开源框架
│ └── xxl-job
│ └── README.md
├── 流处理
├── 09~流处理数据库
│ └── README.md
├── 08~开源框架
│ ├── Beam
│ │ ├── 部署与配置.md
│ │ ├── README.md
│ │ ├── Dataflow 模型.md
│ │ └── 快速开始.md
│ ├── Spark
│ │ ├── README.md
│ │ ├── 代码开发.md
│ │ └── 环境配置.md
│ └── 流计算框架对比.md
├── 01~流处理系统设计
│ ├── 执行框架
│ │ ├── 反压.md
│ │ ├── 分布式快照.md
│ │ ├── 容错.md
│ │ └── 流式连接.md
│ ├── DAG
│ │ ├── README.md
│ │ └── Dryad.md
│ ├── 编程模型
│ │ └── 时间窗口.md
│ └── 状态存储
│ │ └── 99~参考资料
│ │ └── 2022-流处理系统中状态的表示和存储.md
├── 10~Flink
│ ├── Blink
│ │ └── README.md
│ ├── 快速开始.md
│ ├── README.md
│ ├── Table API.md
│ └── 代码开发.md
├── README.md
└── 99~参考资料
│ └── 2023-吴英俊-重新思考流处理与流数据库.md
├── INTRODUCTION.md
├── 批处理
├── Hadoop
│ ├── MapReduce
│ │ ├── README.md
│ │ ├── WordCount.md
│ │ ├── CRUD.md
│ │ └── 聚合计算.md
│ ├── Hadoop 与数据库对比.md
│ └── README.md
├── 编程模型
│ ├── 图的大规模并行.md
│ ├── 查询与声明式接口.md
│ ├── README.md
│ └── Data Parallelism.md
├── Waltz
│ └── README.md
├── README.md
├── MapReduce
│ ├── README.md
│ ├── 作业输出.md
│ ├── 作业执行.md
│ └── 连接与分组.md
├── 执行框架
│ ├── README.md
│ ├── 高级 API 和语言.md
│ ├── 图与迭代处理.md
│ └── 物化中间状态.md
└── 使用 Unix 工具的批处理.md
├── 计算处理模式
├── 事务处理.md
├── 量子计算
│ └── README.md
├── Lambda 架构.md
├── README.md
└── 批处理与流处理.md
├── .gitignore
├── _sidebar.md
├── README.md
├── index.html
├── header.svg
└── LICENSE
/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/分布式调度/调度系统架构/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/流处理/09~流处理数据库/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/INTRODUCTION.md:
--------------------------------------------------------------------------------
1 | # 本篇导读
2 |
--------------------------------------------------------------------------------
/分布式调度/README.md:
--------------------------------------------------------------------------------
1 | # 分布式调度
2 |
--------------------------------------------------------------------------------
/分布式调度/延时任务/README.md:
--------------------------------------------------------------------------------
1 | # 延时任务
--------------------------------------------------------------------------------
/分布式调度/调度系统架构/体系结构/集中式架构.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/流处理/08~开源框架/Beam/部署与配置.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/分布式调度/调度系统架构/两层调度.md:
--------------------------------------------------------------------------------
1 | # 两层调度
2 |
--------------------------------------------------------------------------------
/分布式调度/调度系统架构/单体调度.md:
--------------------------------------------------------------------------------
1 | # 单体调度
2 |
--------------------------------------------------------------------------------
/批处理/Hadoop/MapReduce/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/批处理/编程模型/图的大规模并行.md:
--------------------------------------------------------------------------------
1 | # 图的大规模并行
2 |
--------------------------------------------------------------------------------
/流处理/08~开源框架/Spark/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/分布式调度/调度系统架构/共享状态调度.md:
--------------------------------------------------------------------------------
1 | # 共享状态调度
2 |
--------------------------------------------------------------------------------
/批处理/编程模型/查询与声明式接口.md:
--------------------------------------------------------------------------------
1 | # 查询与声明式接口
2 |
--------------------------------------------------------------------------------
/流处理/01~流处理系统设计/执行框架/反压.md:
--------------------------------------------------------------------------------
1 | # 反压
2 |
--------------------------------------------------------------------------------
/分布式调度/流程引擎/Activiti/README.md:
--------------------------------------------------------------------------------
1 | # Activiti
2 |
--------------------------------------------------------------------------------
/批处理/编程模型/README.md:
--------------------------------------------------------------------------------
1 | # Programming Models
2 |
3 | # 编程模型
4 |
--------------------------------------------------------------------------------
/分布式调度/开源框架/xxl-job/README.md:
--------------------------------------------------------------------------------
1 | # xxl-job
2 |
3 | # Links
4 |
5 | - https://blog.csdn.net/shu616048151/article/details/108033087 xxl-job 架构源码解析
6 |
--------------------------------------------------------------------------------
/流处理/01~流处理系统设计/执行框架/分布式快照.md:
--------------------------------------------------------------------------------
1 | # 分布式快照
2 |
3 | # Links
4 |
5 | - [简单解释: 分布式快照(Chandy-Lamport 算法)](https://zhuanlan.zhihu.com/p/44454670)
6 |
7 | - [(十)简单解释: 分布式数据流的异步快照(Flink 的核心)](https://zhuanlan.zhihu.com/p/43536305)
8 |
--------------------------------------------------------------------------------
/批处理/Waltz/README.md:
--------------------------------------------------------------------------------
1 | # Waltz
2 |
3 | Waltz 类似于 Kafka 等现有日志系统,同样能够接收 / 持久化 / 传播由大量服务生成 / 消费的交易数据。但与其它系统的区别在于,Wlatz 在分布式应用程序之内提供一套更加便捷的序列化一致性实现机制。它能够在将交易提交至日志之前,首先对其中的冲突进行检测。Waltz 可以充当唯一事实来源,而非普通的数据库,并由此建立极为可靠的、以日志为中心的系统架构。
4 |
5 | # Links
6 |
7 | - https://mp.weixin.qq.com/s/1LWTuNcPFGRPDXsxlVgWYA
8 |
--------------------------------------------------------------------------------
/计算处理模式/事务处理.md:
--------------------------------------------------------------------------------
1 | # 事务处理
2 |
3 | 公司将各种应用程序用于日常业务活动,例如企业资源规划(ERP)系统,客户关系管理(CRM)软件和基于 Web 的应用程序。这些系统通常设计有单独的层,用于数据处理(应用程序本身)和数据存储(事务数据库系统):
4 |
5 | 
6 |
7 | 应用程序通常连接到外部服务或直接面向用户,并持续处理传入的事件,如网站上的订单,电子邮件或点击。处理事件时,应用程序将会读取远程数据库的状态,或者通过运行事务来更新它。通常,一个数据库系统可以服务于多个应用程序,它们有时会访问相同的数据库或表。
8 |
--------------------------------------------------------------------------------
/批处理/README.md:
--------------------------------------------------------------------------------
1 | # 批处理
2 |
3 | 互联网的发展产生了所谓的大数据(TB 或 PB),不可能在一个有限的时间范围内将数据拟合到一台机器上或用一个程序处理它。在批处理中,新到达的数据元素被收集到一个组中。整个组在未来的时间进行处理(作为批处理,因此称为“批处理”)。确切地说,何时处理每个组可以用多种方式来确定,它可以基于预定的时间间隔(例如,每五分钟,处理任何新的数据已被收集)或在某些触发的条件下(例如,处理只要它包含五个数据元素或一旦它拥有超过 1MB 的数据)。
4 |
5 | 
6 |
7 | 通过类比的方式,批处理就像你的朋友(你当然知道这样的人)从干衣机中取出一大堆衣物,并简单地把所有东西都扔进一个抽屉里,只有当它很难找到东西时才分类和组织它。这个人避免每次洗衣时都要进行分拣工作,但是他们需要花费大量时间在抽屉里搜索抽屉,并最终需要花费大量时间分离衣服,匹配袜子等。当它变得很难找到东西的时候。历史上,绝大多数数据处理技术都是为批处理而设计的。传统的数据仓库和 Hadoop 是专注于批处理的系统的两个常见示例。
8 |
9 | 术语 MicroBatch 经常用于描述批次小和/或以小间隔处理的情况。即使处理可能每隔几分钟发生一次,数据仍然一次处理一批。Spark Streaming 是设计用于支持微批处理的系统的一个例子。
10 |
11 | # Links
12 |
13 | - http://dist-prog-book.com/chapter/8/big-data.html
14 |
--------------------------------------------------------------------------------
/分布式调度/流程引擎/BPMN/BPMN 规范.md:
--------------------------------------------------------------------------------
1 | # BPMN 规范
2 |
3 | # Task | 任务
4 |
5 | Task 是一个极具威力的元素,它能描述业务过程中所有能发生工时的行为,它包括 User Task、Manual Task、Service Task、Script Task 等,可以被用来描述人机交互任务、线下操作任务、服务调用、脚本计算任务等常规功能。
6 |
7 | - User Task: 生成人机交互任务,主要被用来描述需要人为在软件系统中进行诸如任务明细查阅、填写审批意见等业务行为的操作,流程引擎流转到此类节点时,系统会自动生成被动触发任务,须人工响应后才能继续向下流转。常用于审批任务的定义。
8 |
9 | - Manual Task: 线下人为操作任务,常用于为了满足流程图对实际业务定义的完整性而进行的与流程驱动无关的线下任务,即此类任务不参与实际工作流流转。常用于诸如物流系统中的装货、运输等任务的描述。
10 |
11 | - Service Task: 服务任务,通常工作流流转过程中会涉及到与自身系统服务 API 调用或与外部服务相互调用的情况,此类任务往往由一个具有特定业务服务功能的 Java 类承担,与 User Task 不同,流程引擎流经此节点会自动调用 Java 类中定义的方法,方法执行完毕自动向下一流程节点流转。另外,此类任务还可充当“条件路由”的功能对流程流转可选分支进行自动判断。常用于业务逻辑 API 的调用。
12 |
13 | - Script Task: 脚本任务,在流程流转期间以“脚本”的声明或语法参与流程变量的计算,目前支持的脚本类型有三种:juel(即 JSP EL)、groovy 和 JavaScript。在 Activiti5.9 中新增了 Shell Task,可以处理系统外部定义的 Shell 脚本文件,也与 Script Task 有类似的功能。常用于流程变量的处理。
14 |
--------------------------------------------------------------------------------
/分布式调度/流程引擎/README.md:
--------------------------------------------------------------------------------
1 | # 流程引擎
2 |
3 | 工作流管理联盟 WfMC 组织对工作流 Workflow 概念的经典定义是全部或者部分由计算机支持或自动处理的业务过程。BPM(Business Process Management)的定义则是通过建模、自动化、管理和优化流程,打破跨部门跨系统业务过程依赖,提高业务效率和效果。
4 |
5 | BPM 基本内容是管理既定工作的流程,通过服务编排,统一调控各个业务流程,以确保工作在正确的时间被正确的人执行,达到优化整体业务过程的目的。BPM 概念的贯彻执行,需要有标准化的流程定义语言来支撑,使用统一的语言遵循一致的标准描述具体业务过程,这些流程定义描述由专有引擎去驱动执行。这个 引擎就是工作流引擎,它作为 BPM 的核心发动机,为各个业务流程定义提供解释、执行和编排,驱动流程“动“起来,让大家的工作“流”起来,为 BPM 的应用 提供基本、核心的动力来源。
6 |
7 | 现实工作中,不可避免的存在跨系统跨业务的情况,而大部分企业在信息化建设过程中是分阶段或分部门(子系统)按步实施的,后期实施的基础可能是前期 实施成果的输出,在耦合业务实施阶段,相同的业务过程可能会在不同的实施阶段重用,在进行流程梳理过程中,不同的实施阶段所使用的流程描述语言或遵循的标 准会有所不同(服务厂商不同),有的使用 WfMC 的 XPDL,还有些使用 BPML、BPEL、WSCI 等,这就造成流程管理、业务集成上存在很大的一致 性、局限性,提高了企业应用集成的成本。
8 |
9 | # 优劣对比
10 |
11 | 这里要分清楚两个概念,业务逻辑流和工作流,很多同学混淆了这两个概念。业务逻辑流是响应一次用户请求的业务处理过程,其本身就是业务逻辑,对其编排和可视化的意义并不是很大,无外乎只是把代码逻辑可视化了。而工作流是指完成一项任务所需要不同节点的连接,节点主要分为自动节点和人工节点,其中每个人工节点都需要用户的参与,也就是响应一次用户的请求,比如审批流程中的经理审批节点,CRM 销售过程的业务员的处理节点等等。此时可以考虑使用工作流引擎,特别是当你的系统需要让用户自定义流程的时候,那就不得不使用可视化和可配置的工作流引擎了,除此之外,最好不要自找麻烦。
12 |
--------------------------------------------------------------------------------
/批处理/MapReduce/README.md:
--------------------------------------------------------------------------------
1 | # MapReduce
2 |
3 | MapReduce 有点像 Unix 工具,但分布在数千台机器上。像 Unix 工具一样,它相当简单粗暴,但令人惊异地管用。一个 MapReduce 作业可以和一个 Unix 进程相类比:它接受一个或多个输入,并产生一个或多个输出。和大多数 Unix 工具一样,运行 MapReduce 作业通常不会修改输入,除了生成输出外没有任何副作用。输出文件以连续的方式一次性写入(一旦写入文件,不会修改任何现有的文件部分)。
4 |
5 | 虽然 Unix 工具使用 stdin 和 stdout 作为输入和输出,但 MapReduce 作业在分布式文件系统上读写文件。在 Hadoop 的 Map-Reduce 实现中,该文件系统被称为 HDFS(Hadoop 分布式文件系统),一个 Google 文件系统(GFS)的开源实现。除 HDFS 外,还有各种其他分布式文件系统,如 GlusterFS 和 Quantcast File System(QFS)。诸如 Amazon S3,Azure Blob 存储和 OpenStack Swift 等对象存储服务在很多方面都是相似的。这里我们将主要使用 HDFS 作为示例,但是这些原则适用于任何分布式文件系统。
6 |
7 | 与网络连接存储(NAS)和存储区域网络(SAN)架构的共享磁盘方法相比,HDFS 基于无共享原则。共享磁盘存储由集中式存储设备实现,通常使用定制硬件和专用网络基础设施(如光纤通道)。而另一方面,无共享方法不需要特殊的硬件,只需要通过传统数据中心网络连接的计算机。
8 |
9 | HDFS 包含在每台机器上运行的守护进程,对外暴露网络服务,允许其他节点访问存储在该机器上的文件(假设数据中心中的每台通用计算机都挂载着一些磁盘)。名为 NameNode 的中央服务器会跟踪哪个文件块存储在哪台机器上。因此,HDFS 在概念上创建了一个大型文件系统,可以使用所有运行有守护进程的机器的磁盘。为了容忍机器和磁盘故障,文件块被复制到多台机器上。复制可能意味着多个机器上的相同数据的多个副本,或者诸如 Reed-Solomon 码这样的纠删码方案,它允许以比完全复制更低的存储开销以恢复丢失的数据。这些技术与 RAID 相似,可以在连接到同一台机器的多个磁盘上提供冗余;区别在于在分布式文件系统中,文件访问和复制是在传统的数据中心网络上完成的,没有特殊的硬件。
10 |
--------------------------------------------------------------------------------
/流处理/10~Flink/Blink/README.md:
--------------------------------------------------------------------------------
1 | # Blink
2 |
3 | Blink 是 Flink 的一个分支,最初在阿里巴巴内部创建的,针对内部用例对 Flink 进行改进。Blink 添加了一系列改进和集成(https://github.com/apache/flink/blob/blink/README.md),其中有很多与有界数据 / 批处理和 SQL 有关。实际上,在上面的功能列表中,除了第 4 项外,Blink 在其他方面都迈出了重要的一步:
4 |
5 | - 统一的流式操作符:Blink 扩展了 Flink 的流式运行时操作符模型,支持选择性读取不同的输入源,同时保持推送模型的低延迟特性。这种对输入源的选择性读取可以更好地支持一些算法(例如相同操作符的混合哈希连接)和线程模型(通过 RocksDB 的连续对称连接)。这些操作符为“侧边输入”(https://cwiki.apache.org/confluence/display/FLINK/FLIP-17+Side+Inputs+for+DataStream+API)等新功能打下了基础。
6 |
7 | - Table API 和 SQL 查询处理器:与最新的 Flink 主分支相比,SQL 查询处理器是演变得最多的一个组件:
8 |
9 | - Flink 目前将查询转换为 DataSet 或 DataStream 程序(取决于输入的特性),而 Blink 会将查询转换为上述流式操作符的数据流。
10 | Blink 为常见的 SQL 操作添加了更多的运行时操作符,如半连接(semi-join)、反连接(anti-join)等。
11 |
12 | - 查询规划器(优化器)仍然是基于 Apache Calcite,但提供了更多的优化规则(包括连接重排序),并且使用了适当的成本模型。
13 | 更加积极的流式操作符链接。
14 |
15 | - 扩展通用数据结构(分类器、哈希表)和序列化器,在操作二进制数据上更进一步,并减小了序列化开销。代码生成被用于行序列化器。
16 |
17 | - 改进的调度和故障恢复:最后,Blink 实现了对任务调度和容错的若干改进。调度策略通过利用操作符处理输入数据的方式来更好地使用资源。故障转移策略沿着持久 shuffle 的边界进行更细粒度的恢复。不需重新启动正在运行的应用程序就可以替换发生故障的 JobManager。
18 |
--------------------------------------------------------------------------------
/流处理/01~流处理系统设计/DAG/README.md:
--------------------------------------------------------------------------------
1 | # DAG
2 |
3 | DAG 是有向无环图(Directed Acyclic Graph)的简称。在大数据处理中,DAG 计算常常指的是将计算任务在内部分解成为若干个子任务,将这些子任务之间的逻辑关系或顺序构建成 DAG(有向无环图)结构。
4 | DAG 在分布式计算中是非常常见的一种结构,在各个细分领域都可以看见它,比如 Dryad,FlumeJava 和 Tez,都是明确构建 DAG 计算模型的典型,再如流式计算的 Storm 等系统或机器学习框架 Spark 等,其计算任务大多也是 DAG 形式出现的,除此外还有很多场景都能见到。
5 |
6 | DAG 计算的三层结构:
7 |
8 | - 最上层是应用表达层,即是通过一定手段将计算任务分解成由若干子任务形成的 DAG 结构,其核心是表达的便捷性,主要是方便应用开发者快速描述或构建应用。
9 |
10 | - 中间层是 DAG 执行引擎层,主要目的是将上层以特殊方式表达的 DAG 计算任务通过转换和映射,将其部署到下层的物理机集群中运行,这层是 DAG 计算的核心部件,计算任务的调度,底层硬件的容错,数据与管理信息的传递,整个系统的管理与正常运转等都需要由这层来完成。
11 |
12 | - 最下层是物理机集群,即由大量物理机器搭建的分布式计算环境,这是计算任务最终执行的场所。
13 |
14 | 常见框架包括了:
15 |
16 | - Dryad 是微软的批处理 DAG 计算系统,其主要目的是为了便于开发者便携地进行分布式任务处理。Dryad 将具体计算组织成有向无环图,其中图节点代表用户写的表达式应用逻辑,图节点之间的边代表了数据流动通道。Dryad 在实时以共享内存,TCP 连接以及临时文件的方式来进行数据传递,绝大多数情况下采用临时文件的方式。
17 |
18 | - FlumeJava 是 Google 内部开发的 DAG 系统,考虑到很多任务是需要多个 MR 任务连接起来共同完成的,而如果直接使用 MR 来完成会非常烦琐,因为除了完成 MR 任务本身外,还需要考虑如何衔接 MR 及清理各种中部结果等琐碎工作。
19 |
20 | - Tez 是 Apache 孵化项目,其本身也是一个相对通用的 DAG 计算系统,最初提出 Tez 是为了改善交互数据分析系统 Stinger 的底层执行引擎,Stinger 是 Hive 的改进版本,最初底层的执行引擎是 Hadoop 和 MR 任务形成的 DAG 任务图,Tez 是它的升级版,效率更高。Tez 通过消除 Map 阶段中间文件输出到磁盘过程以及引入 Reduce-Reduce 结构等改进措施极大提升了底层执行引擎和效率。
21 |
--------------------------------------------------------------------------------
/计算处理模式/量子计算/README.md:
--------------------------------------------------------------------------------
1 | # 量子计算
2 |
3 | 量子计算是一种遵循量子力学规律调控量子信息单元进行计算的新型计算模式,即利用量子叠加和纠缠等物理特性,以微观粒子构成的量子比特为基本单元,通过量子态的受控演化实现计算处理。
4 |
5 | 与传统计算机相比,量子计算机能够实现算力呈指数级规模拓展和爆发式增长,形成“量子优越性”。传统计算机的基础原理是二极管和逻辑门,每一个信息单元叫做比特,只能代表 0 或者 1 中的任意一个数字,对二进制数字或字节组成的信息进行存储和处理;而量子态叠加原理使得每个量子比特同时处于比特 0 和比特 1 的状态,通过两种状态的叠加实现并行存储和计算。这样操纵 1 个量子比特的量子计算机可以同时操纵 2 个状态,当一个量子计算机同时操控 n 个量子比特的时候,它实际上能够同时操控 2n 个状态。
6 |
7 | # 量子计算优势
8 |
9 | 量子计算最主要的价值可以归纳为两点:开源(提高算力)+节流(降低能耗)。
10 |
11 | 首先是对算力的提升:量子计算的核心优势是可以实现高速并行计算。在计算机科学中,无论经典计算还是量子计算,他们的计算功能的实现都可以分解为简单的逻辑门的运算,包括:“与”门,“或”门,“非”门,“异或”门等。简单来讲,每一次逻辑门的运算(简称操作)都是都要消耗一个单位时间来完成。经典计算机的运算模式通常是一步一步进行的。它的每一个数字都是单独存储的,而且是逐个运算。所以对于 4 个数字进行同一个操作时,要消耗 4 单位时间。量子的并行性决定了其可以同时对 2n 个数进行数学运算,相当于经典计算机重复实施 2n 次操作。可以看到,当量子比特数量越大时,这种运算速度的优势将越明显。它可以达到经典计算机不可比拟的运算速度和信息处理功能。
12 |
13 | 其次是降低能耗:量子计算另一核心优势是低能耗。众所周知,在经典计算机中,能耗是一大技术难题。处理器对输入两串数据的异或操作,而输出结果只有一组数据,计算之后数据量减少了,根据能量守恒定律,消失的数据信号必然会产生热量。但量子计算中,输入多少组数据输出依旧是多少组数据,计算过程中数据量没有改变,因此计算过程没有能耗。这也就意味着,只有在最后测量的时候产生了能耗。而经典计算在每一个比特的计算过程中都将产生能耗。因而经典计算的集成度越高,散热越困难。随着摩尔定律渐近极限,以后的计算能力的提高只能依靠堆积更多的计算芯片,这将导致更大的能耗。这方面的突破只能依靠量子计算的发展。[1]
14 |
15 | 受滞于摩尔定律的上限、芯片大小的极限、芯片散热等问题,传统计算机在执行某些任务时遇到瓶颈,例如:1)大数因数分解;2)数据库随机搜索。而量子计算中提出的大数质因子(Shor 算法)、随机数据库搜索(Grover 算法)就很好的解决了这两个问题,能够应用于复杂的大规模数据处理与计算难题。
16 |
--------------------------------------------------------------------------------
/流处理/08~开源框架/Beam/README.md:
--------------------------------------------------------------------------------
1 | # Apache Beam
2 |
3 | 在大数据的浪潮之下,技术的更新迭代十分频繁。受技术开源的影响,大数据开发者提供了十分丰富的工具。但也因为如此,增加了开发者选择合适工具的难度。在大数据处理一些问题的时候,往往使用的技术是多样化的。这完全取决于业务需求,比如进行批处理的 MapReduce,实时流处理的 Flink,以及 SQL 交互的 Spark SQL 等等。而把这些开源框架,工具,类库,平台整合到一起,所需要的工作量以及复杂度,可想而知。这也是大数据开发者比较头疼的问题。
4 |
5 | 
6 |
7 | Apache Beam 最初叫 Apache Dataflow,由谷歌和其合作伙伴向 Apache 捐赠了大量的核心代码,并创立孵化了该项目。该项目的大部分大码来自于 Cloud Dataflow SDK,其特点有以下几点:
8 |
9 | - 统一数据批处理(Batch)和流处理(Stream)编程的范式
10 | - 能运行在任何可执行的引擎之上
11 |
12 | 整个技术的发展流向;一部分是谷歌派系,另一部分则是 Apache 派系。在开发大数据应用时,我们有时候使用谷歌的框架,API,类库,平台等,而有时候我们则使用 Apache 的,比如:HBase,Flink,Spark 等。而我们要整合这些资源则是一个比较头疼的问题,Apache Beam 的问世,整合这些资源提供了很方便的解决方案。
13 |
14 | 
15 |
16 | Beam SDK 提供了一个统一的编程模型,来处理任意规模的数据集,其中包括有限的数据集,无限的流数据。Apache Beam SDK 使用相同的类来表达有限和无限的数据,同样使用相同的转换方法对数据进行操作。Beam 提供了多种 SDK,你可以选择一种你熟悉的来建立数据处理管道,如上述的图,我们可以知道,目前 Beam 支持 Java,Python 以及其他待开发的语言。
17 |
18 | 在 Beam 管道上运行引擎会根据你选择的分布式处理引擎,其中兼容的 API 转换你的 Beam 程序应用,让你的 Beam 应用程序可以有效的运行在指定的分布式处理引擎上。因而,当运行 Beam 程序的时候,你可以按照自己的需求选择一种分布式处理引擎。当前 Beam 支持的管道运行引擎有以下几种:Apache Apex,Apache Flink,Apache Spark,Google Cloud Dataflow。
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore all
2 | *
3 |
4 | # Unignore all with extensions
5 | !*.*
6 |
7 | # Unignore all dirs
8 | !*/
9 |
10 | .DS_Store
11 |
12 | # Logs
13 | logs
14 | *.log
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
19 | # Runtime data
20 | pids
21 | *.pid
22 | *.seed
23 | *.pid.lock
24 |
25 | # Directory for instrumented libs generated by jscoverage/JSCover
26 | lib-cov
27 |
28 | # Coverage directory used by tools like istanbul
29 | coverage
30 |
31 | # nyc test coverage
32 | .nyc_output
33 |
34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
35 | .grunt
36 |
37 | # Bower dependency directory (https://bower.io/)
38 | bower_components
39 |
40 | # node-waf configuration
41 | .lock-wscript
42 |
43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
44 | build/Release
45 |
46 | # Dependency directories
47 | node_modules/
48 | jspm_packages/
49 |
50 | # TypeScript v1 declaration files
51 | typings/
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Optional REPL history
60 | .node_repl_history
61 |
62 | # Output of 'npm pack'
63 | *.tgz
64 |
65 | # Yarn Integrity file
66 | .yarn-integrity
67 |
68 | # dotenv environment variables file
69 | .env
70 |
71 | # next.js build output
72 | .next
73 |
--------------------------------------------------------------------------------
/计算处理模式/Lambda 架构.md:
--------------------------------------------------------------------------------
1 | # Lambda 架构
2 |
3 | 如果批处理用于重新处理历史数据,并且流处理用于处理最近的更新,那么如何将这两者结合起来?Lambda 架构是这方面的一个建议,引起了很多关注。Lambda 架构的核心思想是通过将不可变事件附加到不断增长的数据集来记录传入数据,这类似于事件溯源,为了从这些事件中衍生出读取优化的视图,Lambda 架构建议并行运行两个不同的系统:批处理系统(如 Hadoop MapReduce)和独立的流处理系统(如 Storm)。
4 |
5 | 在 Lambda 方法中,流处理器消耗事件并快速生成对视图的近似更新;批处理器稍后将使用同一组事件并生成衍生视图的更正版本。这个设计背后的原因是批处理更简单,因此不易出错,而流处理器被认为是不太可靠和难以容错的,流处理可以使用快速近似算法,而批处理使用较慢的精确算法。
6 |
7 | Lambda 架构是一种有影响力的想法,它将数据系统的设计变得更好,尤其是通过推广这样的原则:在不可变事件流上建立衍生视图,并在需要时重新处理事件。但是我也认为它有一些实际问题:
8 |
9 | - 在批处理和流处理框架中维护相同的逻辑是很显著的额外工作。虽然像 Summingbird 这样的库提供了一种可以在批处理和流处理的上下文中运行的计算抽象。调试,调整和维护两个不同系统的操作复杂性依然存在。
10 |
11 | - 由于流管道和批处理管道产生独立的输出,因此需要合并它们以响应用户请求。如果计算是基于滚动窗口的简单聚合,则合并相当容易,但如果视图基于更复杂的操作(例如连接和会话化)而导出,或者输出不是时间序列,则会变得非常困难。
12 |
13 | - 尽管有能力重新处理整个历史数据集是很好的,但在大型数据集上这样做经常会开销巨大。因此,批处理流水线通常需要设置为处理增量批处理(例如,在每小时结束时处理一小时的数据),而不是重新处理所有内容。这引发了“关于时间的推理”中讨论的问题,例如处理分段器和处理跨批次边界的窗口。增加批量计算会增加复杂性,使其更类似于流式传输层,这与保持批处理层尽可能简单的目标背道而驰。
14 |
15 | ## 统一批处理和流处理
16 |
17 | 最近的工作使得 Lambda 架构的优点在没有其缺点的情况下得以实现,允许批处理计算(重新处理历史数据)和流计算(处理事件到达时)在同一个系统中实现。在一个系统中统一批处理和流处理需要以下功能,这些功能越来越广泛:
18 |
19 | - 通过处理最近事件流的相同处理引擎来重放历史事件的能力。例如,基于日志的消息代理可以重放消息,某些流处理器可以从 HDFS 等分布式文件系统读取输入。
20 |
21 | - 对于流处理器来说,恰好一次语义,即确保输出与未发生故障的输出相同,即使事实上发生故障。与批处理一样,这需要丢弃任何失败任务的部分输出。
22 |
23 | - 按事件时间进行窗口化的工具,而不是按处理时间进行窗口化,因为处理历史事件时,处理时间毫无意义。例如,Apache Beam 提供了用于表达这种计算的 API,然后可以使用 Apache Flink 或 Google Cloud Dataflow 运行。
24 |
--------------------------------------------------------------------------------
/计算处理模式/README.md:
--------------------------------------------------------------------------------
1 | # 数据处理架构
2 |
3 | # OLTP 与 OLAP
4 |
5 | 数十年来,数据和数据处理在企业中无处不在。多年来,数据的收集和使用一直在增长,公司已经设计并构建了基础架构来管理数据。大多数企业实施的传统架构区分了两种类型的数据处理:事务处理(OLTP, Online Transactional Processing)和分析处理(OLAP, Online Analytical Processing)。在互联网浪潮出现之前,企业的数据量普遍不大,特别是核心的业务数据,通常一个单机的数据库就可以保存。在业务数据处理的早期,对数据库的写入通常对应于正在进行的商业交易:进行销售,向供应商下订单,支付员工工资等等。随着数据库扩展到那些没有不涉及钱易手的业务,我们仍然使用术语交易(transaction)来代指形成一个逻辑单元的一组读写。事务不一定具有 ACID(原子性,一致性,隔离性和持久性)属性。事务处理只是意味着允许客户端进行低延迟读取和写入,而不是只能定期运行(例如每天一次)的批量处理作业。
6 |
7 | 同时,数据库也开始越来越多地用于数据分析,这些数据分析具有非常不同的访问模式。通常,分析查询需要扫描大量记录,每个记录只读取几列,并计算汇总统计信息(如计数,总和或平均值),而不是将原始数据返回给用户。早期所有的线上请求(OLTP) 和后台分析 (OLAP) 都跑在同一个数据库实例上。后来随着数据量不断增大,在二十世纪八十年代末和九十年代初期,公司倾向于在单独的数据库上运行分析,这个单独的数据库被称为数据仓库(data warehouse)。在这样的背景下,以 Hadoop 为代表的大数据技术开始蓬勃发展,它用许多相对廉价的 x86 机器构建了一个数据分析平台,用并行的能力破解大数据集的计算问题。由此,架构师把存储划分成线上业务和数据分析两个模块。如下图所示,业务库的数据通过 ETL 工具抽取出来,导入专用的分析平台。业务数据库专注提供 TP 能力,分析平台提供 AP 能力,各施其职,看起来已经很完美了。但其实这个架构也有自己的不足。
8 |
9 | 
10 |
11 | | 属性 | 事务处理 OLTP | 分析系统 OLAP |
12 | | ------------ | ---------------------------- | ------------------------ |
13 | | 主要读取模式 | 查询少量记录,按键读取 | 在大批量记录上聚合 |
14 | | 主要写入模式 | 随机访问,写入要求低延时 | 批量导入(ETL),事件流 |
15 | | 主要用户 | 终端用户,通过 Web 应用 | 内部数据分析师,决策支持 |
16 | | 处理的数据 | 数据的最新状态(当前时间点) | 随时间推移的历史事件 |
17 | | 数据集尺寸 | GB ~ TB | TB ~ PB |
18 |
--------------------------------------------------------------------------------
/分布式调度/流程引擎/BPMN/README.md:
--------------------------------------------------------------------------------
1 | # BPMN
2 |
3 | 遵循 BPMN2.0 新规范的工作流产品能很大程度上解决此类问题。BPMN2.0 相对于旧的 1.0 规范以及 XPDL、BPML 及 BPEL 等最大的区 别是定义了规范的执行语义和格式,利用标准的图元去描述真实的业务发生过程,保证相同的流程在不同的流程引擎得到的执行结果一致。BPMN2.0 对流程执行语义定义了三类基本要素:
4 |
5 | - Activities(活动)——在工作流中所有具备生命周期状态的都可以称之为“活动”,如原子级的任务(Task)、流向(Sequence Flow),以及子流程(Sub-Process)等
6 | - Gateways(网关)——顾名思义,所谓“网关”就是用来决定流程流转指向的,可能会被用作条件分支或聚合,也可以被用作并行执行或基于事件的排它性条件判断
7 | - Events(事件)——在 BPMN2.0 执行语义中也是一个非常重要的概念,像启动、结束、边界条件以及每个活动的创建、开始、流转等都是流程事件,利用事件机制,可以通过事件控制器为系统增加辅助功能,如其它业务系统集成、活动预警等。
8 |
9 | 这三类执行语义的定义涵盖了业务流程常用的 Sequence Flow(流程转向)、Task(任务)、Sub-Process(子流程)、Parallel Gateway(并行执行网关)、ExclusiveGateway(排它型网关)、InclusiveGateway(包容型网关)等常用图元。
10 |
11 | 
12 |
13 | BPMN2.0 为所有业务元素定义了标准的符号,不同的符号代表不同的含义,以 OA 应用中请假流程为例,使用标准的 BPMN2.0 图元定义:
14 |
15 | 
16 |
17 | 现实业务所有的业务环节都离不开 Activities、Gateways 和 Events,无论是简单的条件审批还是复杂的父子流程循环处理,在一个 流程定义描述中,所有的业务环节都离不开 Task、Sequence Flow、Exclusive Gateway、Inclusive Gateway。
18 |
19 | 应用 BPMN2.0 标准的一个最显著的特色是,不同阶段的人员,无论是需求分析、概要设计、详细设计或是具体的业务实现,都可在一个流程图上开展工作,避免业务理解存在偏差。一个系统的实现,需求分析人员可以利用 BPMN2.0 标准图元草绘一下搜集到的需求;然后可以拿给设计人员,讨论出具体的业务需求进行功能设计,由设计人员在草图的基础上逐步细化,并得到需求人员的认同;设计人员又将细化后的流程图交给开发人员,罗列要实现的功能点,指出流程图上各活动节点所具备的行为,设计人员与开发人员依据此图达成共识,进入具体的开发阶段;如果后期请假流程发生更改,仍然是在现有流程图上更改,随着项目的推进,流程图也在不断的演进,但至始至终,项目受众都使用同一个流程图交流,保障需求理解的一致性,一定程度上推动了项目的敏捷性。
20 |
--------------------------------------------------------------------------------
/流处理/08~开源框架/Spark/代码开发.md:
--------------------------------------------------------------------------------
1 | # DebugTest
2 |
3 | ## UnitTest
4 |
5 | Spark 的任务需要允许在 Spark 的上下文中,可以用一个叫做 FindSpark 的包来辅助:
6 |
7 | ```
8 | pip install findspark
9 | ```
10 |
11 | 在初始化上下文之前,需要调用`findspark.init()`方法,它会自动地根据本地的`SPARK_HOME`环境变量来定位到 Spark 的 Python 依赖包。
12 |
13 | ```python
14 | import unittest2
15 | import logging
16 |
17 | import findspark
18 | findspark.init()
19 | from pyspark.context import SparkContext
20 |
21 | class ExampleTest(unittest2.TestCase):
22 |
23 | def setUp(self):
24 | self.sc = SparkContext('local[4]')
25 | quiet_logs(self.sc)
26 |
27 | def tearDown(self):
28 | self.sc.stop()
29 |
30 | def test_something(self):
31 | # start by creating a mockup dataset
32 | l = [(1, 'hello'), (2, 'world'), (3, 'world')]
33 | # and create a RDD out of it
34 | rdd = self.sc.parallelize(l)
35 | # pass it to the transformation you're unit testing
36 | result = non_trivial_transform(rdd)
37 | # collect the results
38 | output = result.collect()
39 | # since it's unit test let's make an assertion
40 | self.assertEqual(output[0][1], 2)
41 |
42 |
43 | def non_trivial_transform(rdd):
44 | """ a transformation to unit test (word count) - defined here for convenience only"""
45 | return rdd.map(lambda x: (x[1], 1)).reduceByKey(lambda a, b: a + b)
46 |
47 | if __name__ == "__main__":
48 | unittest2.main()
49 | ```
50 |
--------------------------------------------------------------------------------
/计算处理模式/批处理与流处理.md:
--------------------------------------------------------------------------------
1 | # 批处理与流处理
2 |
3 | 数据集成的目标是,确保数据最终能在所有正确的地方表现出正确的形式。这样做需要消费输入,转换,连接,过滤,聚合,训练模型,评估,以及最终写出适当的输出。批处理和流处理是实现这一目标的工具。批处理和流处理的输出是衍生数据集,例如搜索索引,物化视图,向用户显示的建议,聚合指标等;流处理允许将输入中的变化以低延迟反映在衍生视图中,而批处理允许重新处理大量累积的历史数据以便将新视图导出到现有数据集上。批处理和流处理有许多共同的原则,主要的根本区别在于流处理器在无限数据集上运行,而批处理输入是已知的有限大小。处理引擎的实现方式也有很多细节上的差异,但是这些区别已经开始模糊。
4 |
5 | Spark 在批处理引擎上执行流处理,将流分解为微批次(Micro Batches),而 Apache Flink 则在流处理引擎上执行批处理。原则上,一种类型的处理可以用另一种类型来模拟,但是性能特征会有所不同:例如,在跳跃或滑动窗口上,微批次可能表现不佳。批处理有着很强的函数式风格(即使其代码不是用函数式语言编写的):它鼓励确定性的纯函数,其输出仅依赖于输入,除了显式输出外没有副作用,将输入视作不可变的,且输出是仅追加的。流处理与之类似,但它扩展了算子以允许受管理的,容错的状态。具有良好定义的输入和输出的确定性函数的原理不仅有利于容错,也简化了有关组织中数据流的推理。无论衍生数据是搜索索引,统计模型还是缓存,采用这种观点思考都是很有帮助的:将其视为从一个东西衍生出另一个的数据管道,将一个系统的状态变更推送至函数式应用代码中,并将其效果应用至衍生系统中。
6 |
7 | 原则上,衍生数据系统可以同步地维护,就像关系数据库在与被索引表写入操作相同的事务中同步更新辅助索引一样。然而,异步是基于事件日志的系统稳健的原因:它允许系统的一部分故障被抑制在本地,而如果任何一个参与者失败,分布式事务将中止,因此它们倾向于通过将故障传播到系统的其余部分来放大故障。我们在“分区与次级索引”中看到,二级索引经常跨越分区边界。具有二级索引的分区系统需要将写入发送到多个分区(如果索引按关键词分区的话)或将读取发送到所有分区。如果索引是异步维护的,这种交叉分区通信也是最可靠和最可扩展的。
8 |
9 | ## 批处理与流处理
10 |
11 | 流处理和批处理之间的差异对于应用程序来说也是非常重要的。为批处理而构建的应用程序,通过定义处理数据,具有延迟性。在具有多个步骤的数据管道中,这些延迟会累积。此外,新数据的到达与该数据的处理之间的延迟将取决于直到下一批处理窗口的时间--从在某些情况下完全没有时间到批处理窗口之间的全部时间不等,这些数据是在批处理开始后到达的。因此,批处理应用程序(及其用户)不能依赖一致的响应时间,需要相应地调整以适应这种不一致性和更大的延迟。
12 |
13 | Flink、Beam 等都支持流式处理优先,将批处理视为流式处理的特殊情况的理念,这个理念也经常被认为是构建跨实时和离线数据应用程序的强大方式,可以大大降低数据基础设施的复杂性。“批处理只是流式处理的一个特例”并不意味着所有的流式处理器都能用于批处理——流式处理器的出现并没有让批处理器变得过时:
14 |
15 | - 纯流式处理系统在批处理工作负载时其实是很慢的。没有人会认为使用流式处理器来分析海量数据是个好主意。
16 |
17 | - 像 Apache Beam 这样的统一 API 通常会根据数据是持续的(无界)还是固定的(有界)将工作负载委托给不同的运行时。
18 |
19 | - Flink 提供了一个流式 API,可以处理有界和无界的场景,同时仍然提供了单独的 DataSet API 和运行时用于批处理,因为速度会更快。
20 |
--------------------------------------------------------------------------------
/批处理/执行框架/README.md:
--------------------------------------------------------------------------------
1 | # 批处理引擎
2 |
3 | 虽然 MapReduce 在二十世纪二十年代后期变得非常流行,并受到大量的炒作,但它只是分布式系统的许多可能的编程模型之一。对于不同的数据量,数据结构和处理类型,其他工具可能更适合表示计算。不管如何,我们在这一章花了大把时间来讨论 MapReduce,因为它是一种有用的学习工具,它是分布式文件系统的一种相当简单明晰的抽象。在这里,简单意味着我们能理解它在做什么,而不是意味着使用它很简单。恰恰相反:使用原始的 MapReduce API 来实现复杂的处理工作实际上是非常困难和费力的,例如,任意一种连接算法都需要你从头开始实现。
4 |
5 | 针对直接使用 MapReduce 的困难,在 MapReduce 上有很多高级编程模型(Pig,Hive,Cascading,Crunch)被创造出来,作为建立在 MapReduce 之上的抽象。如果你了解 MapReduce 的原理,那么它们学起来相当简单。而且它们的高级结构能显著简化许多常见批处理任务的实现。但是,MapReduce 执行模型本身也存在一些问题,这些问题并没有通过增加另一个抽象层次而解决,而对于某些类型的处理,它表现得非常差劲。一方面,MapReduce 非常稳健:你可以使用它在任务会频繁终止的多租户系统上处理几乎任意大量级的数据,并且仍然可以完成工作(虽然速度很慢)。另一方面,对于某些类型的处理而言,其他工具有时会快上几个数量级。
6 |
7 | # 分布式批处理框架的挑战
8 |
9 | 批处理作业的显著特点是,它读取一些输入数据并产生一些输出数据,但不修改输入—— 换句话说,输出是从输入衍生出的。最关键的是,输入数据是有界的(bounded):它有一个已知的,固定的大小(例如,它包含一些时间点的日志文件或数据库内容的快照)。因为它是有界的,一个作业知道自己什么时候完成了整个输入的读取,所以一个工作在做完后,最终总是会完成的。分布式批处理框架需要解决的两个主要问题是:
10 |
11 | - 分区:在 MapReduce 中,Mapper 根据输入文件块进行分区。Mapper 的输出被重新分区,排序,并合并到可配置数量的 Reducer 分区中。这一过程的目的是把所有的相关数据(例如带有相同键的所有记录)都放在同一个地方。后 MapReduce 时代的数据流引擎若非必要会尽量避免排序,但它们也采取了大致类似的分区方法。
12 |
13 | - 容错:MapReduce 经常写入磁盘,这使得从单个失败的任务恢复很轻松,无需重新启动整个作业,但在无故障的情况下减慢了执行速度。数据流引擎更多地将中间状态保存在内存中,更少地物化中间状态,这意味着如果节点发生故障,则需要重算更多的数据。确定性算子减少了需要重算的数据量。
14 |
15 | 我们讨论了几种 MapReduce 的连接算法,其中大多数也在 MPP 数据库和数据流引擎内部使用。它们也很好地演示了分区算法是如何工作的:
16 |
17 | - 排序合并连接:每个参与连接的输入都通过一个提取连接键的 Mapper。通过分区,排序和合并,具有相同键的所有记录最终都会进入相同的 Reducer 调用。这个函数能输出连接好的记录。
18 |
19 | - 广播哈希连接:两个连接输入之一很小,所以它并没有分区,而且能被完全加载进一个哈希表中。因此,你可以为连接输入大端的每个分区启动一个 Mapper,将输入小端的哈希表加载到每个 Mapper 中,然后扫描大端,一次一条记录,并为每条记录查询哈希表。
20 |
21 | - 分区哈希连接:如果两个连接输入以相同的方式分区(使用相同的键,相同的哈希函数和相同数量的分区),则可以独立地对每个分区应用哈希表方法。
22 |
23 | 分布式批处理引擎有一个刻意限制的编程模型:回调函数(比如 Mapper 和 Reducer)被假定是无状态的,而且除了指定的输出外,必须没有任何外部可见的副作用。这一限制允许框架在其抽象下隐藏一些困难的分布式系统问题:当遇到崩溃和网络问题时,任务可以安全地重试,任何失败任务的输出都被丢弃。如果某个分区的多个任务成功,则其中只有一个能使其输出实际可见。得益于这个框架,你在批处理作业中的代码无需操心实现容错机制:框架可以保证作业的最终输出与没有发生错误的情况相同,也许不得不重试各种任务。在线服务处理用户请求,并将写入数据库作为处理请求的副作用,比起在线服务,批处理提供的这种可靠性语义要强得多。
24 |
--------------------------------------------------------------------------------
/流处理/08~开源框架/Beam/Dataflow 模型.md:
--------------------------------------------------------------------------------
1 | # 流处理模型
2 |
3 | Dataflow 模型(或者说 Beam 模型)旨在建立一套准确可靠的关于流处理的解决方案。在 Dataflow 模型提出以前,流处理常被认为是一种不可靠但低延迟的处理方式,需要配合类似于 MapReduce 的准确但高延迟的批处理框架才能得到一个可靠的结果,这就是著名的 Lambda 架构。这种架构给应用带来了很多的麻烦,例如引入多套组件导致系统的复杂性、可维护性难度提高。因此 Lambda 架构遭到很多开发者的炮轰,并试图设计一套统一批流的架构减少这种复杂性。Spark 1.X 的 Mirco-Batch 模型就尝试从批处理的角度处理流数据,将不间断的流数据切分为一个个微小的批处理块,从而可以使用批处理的 transform 操作处理数据。还有 Jay 提出的 Kappa 架构,使用类似于 Kafka 的日志型消息存储作为中间件,从流处理的角度处理批处理。在工程师的不断努力和尝试下,Dataflow 模型孕育而生。
4 |
5 | 起初,Dataflow 模型是为了解决 Google 的广告变现问题而设计的。因为广告主需要实时的知道自己投放的广告播放、观看情况等指标从而更好的进行决策,但是批处理框架 Mapreduce、Spark 等无法满足时延的要求(因为它们需要等待所有的数据成为一个批次后才会开始处理),(当时)新生的流处理框架 Aurora、Niagara 等还没经受大规模生产环境的考验,饱经考验的流处理框架 Storm、Samza 却没有“恰好一次”的准确性保障(在广告投放时,如果播放量算多一次,意味广告主的亏损,导致对平台的不信任,而少算一次则是平台的亏损,平台方很难接受),DStreaming(Spark1.X)无法处理事件时间,只有基于记录数或基于数据处理时间的窗口,Lambda 架构过于复杂且可维护性低,最契合的 Flink 在当时并未成熟。最后 Google 只能基于 MillWheel 重新审视流的概念设计出 Dataflow 模型和 Google Cloud Dataflow 框架,并最终影响了 Spark 2.x 和 Flink 的发展,也促使了 Apache Beam 项目的开源。
6 |
7 | # 核心概念
8 |
9 | Dataflow 模型从流处理的角度重新审视数据处理过程,将批和流处理的数据抽象成数据集的概念,并将数据集划分为无界数据集和有界数据集,认为流处理是批处理的超集。模型定义了时间域(time domain)的概念,将时间明确的区分为事件时间(event-time)和处理时间(process-time),给出构建一个正确、稳定、低时延的流处理系统所会面临的四个问题及其解决办法:
10 |
11 | - 计算的结果是什么(What results are calculated)?通过 transformations 操作
12 |
13 | - 在事件时间中的哪个位置计算结果(Where in event time are results calculated)?使用窗口(windowing)的概念
14 |
15 | - 在处理时间中的哪个时刻触发计算结果(When in processing time are results materialized)?使用 triggers + watermarks 进行触发计算
16 |
17 | - 如何修正结果(How do refinements of results relate)?通过 accumulation 的类型修正结果数据
18 |
19 | 基于这些考虑,模型的核心概念包括了:
20 |
21 | - 事件时间(Event time)和处理时间(processing time)流处理中最重要的问题是事件发生的时间(事件时间)和处理系统观测到的时间(处理时间)存在延迟。
22 |
23 | - 窗口(Windowing)为了合理地计算无界数据集地结果,所以需要沿时间边界切分数据集(也就是窗口)。
24 |
25 | - 触发器(Triggers)触发器是一种表示处理过程中遇上某种特殊情况时,此刻的输出结果可以是精确的,有意义的机制。
26 |
27 | - 水印(Watermarks)水印是针对事件时间的概念,提供了一种事件时间相对于处理时间是乱序的系统中合理推测无界数据集里数据完整性的工具。
28 |
29 | 累计类型(Accumulation)累计类型是处理单个窗口的输出数据是如何随着流处理的进程而发生变化的。
30 |
31 | # Links
32 |
33 | - https://www.zhihu.com/question/30151872/answer/640568211
34 |
--------------------------------------------------------------------------------
/批处理/执行框架/高级 API 和语言.md:
--------------------------------------------------------------------------------
1 | # 高级 API 和语言
2 |
3 | 自 MapReduce 开始流行的这几年以来,分布式批处理的执行引擎已经很成熟了。到目前为止,基础设施已经足够强大,能够存储和处理超过 10,000 台机器群集上的数 PB 的数据。由于在这种规模下物理执行批处理的问题已经被认为或多或少解决了,所以关注点已经转向其他领域:改进编程模型,提高处理效率,扩大这些技术可以解决的问题集。如前所述,Hive,Pig,Cascading 和 Crunch 等高级语言和 API 变得越来越流行,因为手写 MapReduce 作业实在是个苦力活。随着 Tez 的出现,这些高级语言还有一个额外好处,可以迁移到新的数据流执行引擎,而无需重写作业代码。Spark 和 Flink 也有它们自己的高级数据流 API,通常是从 FlumeJava 中获取的灵感。
4 |
5 | 这些数据流 API 通常使用关系型构建块来表达一个计算:按某个字段连接数据集;按键对元组做分组;按某些条件过滤;并通过计数求和或其他函数来聚合元组。在内部,这些操作是使用本章前面讨论过的各种连接和分组算法来实现的。除了少写代码的明显优势之外,这些高级接口还支持交互式用法,在这种交互式使用中,你可以在 Shell 中增量式编写分析代码,频繁运行来观察它做了什么。这种开发风格在探索数据集和试验处理方法时非常有用。
6 |
7 | 此外,这些高级接口不仅提高了人类的工作效率,也提高了机器层面的作业执行效率。
8 |
9 | # 向声明式查询语言的转变
10 |
11 | 与硬写执行连接的代码相比,指定连接关系算子的优点是,框架可以分析连接输入的属性,并自动决定哪种上述连接算法最适合当前任务。Hive,Spark 和 Flink 都有基于代价的查询优化器可以做到这一点,甚至可以改变连接顺序,最小化中间状态的数量。连接算法的选择可以对批处理作业的性能产生巨大影响,而无需理解和记住本章中讨论的各种连接算法。如果连接是以**声明式(declarative)**的方式指定的,那这就这是可行的:应用只是简单地说明哪些连接是必需的,查询优化器决定如何最好地执行连接。
12 |
13 | 但 MapReduce 及其后继者数据流在其他方面,与 SQL 的完全声明式查询模型有很大区别。MapReduce 是围绕着回调函数的概念建立的:对于每条记录或者一组记录,调用一个用户定义的函数(Mapper 或 Reducer),并且该函数可以自由地调用任意代码来决定输出什么。这种方法的优点是可以基于大量已有库的生态系统创作:解析,自然语言分析,图像分析以及运行数值算法或统计算法等。自由运行任意代码,长期以来都是传统 MapReduce 批处理系统与 MPP 数据库的区别所在。虽然数据库具有编写用户定义函数的功能,但是它们通常使用起来很麻烦,而且与大多数编程语言中广泛使用的程序包管理器和依赖管理系统兼容不佳(例如 Java 的 Maven,Javascript 的 npm,以及 Ruby 的 gems)。
14 |
15 | 然而数据流引擎已经发现,支持除连接之外的更多声明式特性还有其他的优势。例如,如果一个回调函数只包含一个简单的过滤条件,或者只是从一条记录中选择了一些字段,那么在为每条记录调用函数时会有相当大的额外 CPU 开销。如果以声明方式表示这些简单的过滤和映射操作,那么查询优化器可以利用面向列的存储布局,只从磁盘读取所需的列。Hive,Spark DataFrames 和 Impala 还使用了向量化执行:在对 CPU 缓存友好的内部循环中迭代数据,避免函数调用。Spark 生成 JVM 字节码,Impala 使用 LLVM 为这些内部循环生成本机代码。
16 |
17 | 通过在高级 API 中引入声明式的部分,并使查询优化器可以在执行期间利用这些来做优化,批处理框架看起来越来越像 MPP 数据库了(并且能实现可与之媲美的性能)。同时,通过拥有运行任意代码和以任意格式读取数据的可扩展性,它们保持了灵活性的优势。
18 |
19 | # 专业化的不同领域
20 |
21 | 尽管能够运行任意代码的可扩展性是很有用的,但是也有很多常见的例子,不断重复着标准的处理模式。因而这些模式值得拥有自己的可重用通用构建模块实现,传统上,MPP 数据库满足了商业智能分析和业务报表的需求,但这只是许多使用批处理的领域之一。
22 |
23 | 另一个越来越重要的领域是统计和数值算法,它们是机器学习应用所需要的(例如分类器和推荐系统)。可重用的实现正在出现:例如,Mahout 在 MapReduce,Spark 和 Flink 之上实现了用于机器学习的各种算法,而 MADlib 在关系型 MPP 数据库(Apache HAWQ)中实现了类似的功能。
24 |
25 | 空间算法也是有用的,例如最近邻搜索(k-nearest neghbors, kNN),它在一些多维空间中搜索与给定项最近的项目:这是一种相似性搜索。近似搜索对于基因组分析算法也很重要,它们需要找到相似但不相同的字符串。批处理引擎正被用于分布式执行日益广泛的各领域算法。随着批处理系统获得各种内置功能以及高级声明式算子,且随着 MPP 数据库变得更加灵活和易于编程,两者开始看起来相似了:最终,它们都只是存储和处理数据的系统。
26 |
--------------------------------------------------------------------------------
/流处理/08~开源框架/Beam/快速开始.md:
--------------------------------------------------------------------------------
1 | # Apache Beam 快速开始
2 |
3 | # Simple Word Count
4 |
5 | 可以通过 Beam 提供的 Maven 模板来快速创建项目:
6 |
7 | ```s
8 | $ mvn archetype:generate \
9 | -DarchetypeRepository=https://repository.apache.org/content/groups/snapshots \
10 | -DarchetypeGroupId=org.apache.beam \
11 | -DarchetypeArtifactId=beam-sdks-java-maven-archetypes-examples \
12 | -DarchetypeVersion=LATEST \
13 | -DgroupId=org.example \
14 | -DartifactId=word-count-beam \
15 | -Dversion="0.1" \
16 | -Dpackage=org.apache.beam.examples \
17 | -DinteractiveMode=false
18 | ```
19 |
20 | 一个 Beam 程序可以运行在多个 Beam 的可执行引擎上,包括 ApexRunner,FlinkRunner,SparkRunner 或者 DataflowRunner。另外还有 DirectRunner。不需要特殊的配置就可以在本地执行,方便测试使用。使用不同的命令:通过 `--runner=` 参数指明引擎类型,默认是 DirectRunner;添加引擎相关的参数;指定输出文件和输出目录,当然这里需要保证文件目录是执行引擎可以访问到的,比如本地文件目录是不能被外部集群访问的。
21 |
22 | ```sh
23 | # Direct
24 | $ mvn compile exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
25 | -Dexec.args="--inputFile=pom.xml --output=counts" -Pdirect-runner
26 |
27 | # Apex
28 | $ mvn compile exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
29 | -Dexec.args="--inputFile=pom.xml --output=counts --runner=ApexRunner" -Papex-runner
30 |
31 | # Flink-Local
32 | $ mvn compile exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
33 | -Dexec.args="--runner=FlinkRunner --inputFile=pom.xml --output=counts" -Pflink-runner
34 |
35 | # Flink-Cluster
36 | $ mvn package exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
37 | -Dexec.args="--runner=FlinkRunner --flinkMaster= --filesToStage=target/word-count-beam-bundled-0.1.jar \
38 | --inputFile=/path/to/quickstart/pom.xml --output=/tmp/counts" -Pflink-runner
39 |
40 | # Spark
41 | $ mvn compile exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
42 | -Dexec.args="--runner=SparkRunner --inputFile=pom.xml --output=counts" -Pspark-runner
43 |
44 | # Dataflow
45 | $ mvn compile exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
46 | -Dexec.args="--runner=DataflowRunner --gcpTempLocation=gs:///tmp \
47 | --inputFile=gs://apache-beam-samples/shakespeare/* --output=gs:///counts" \
48 | -Pdataflow-runner
49 | ```
50 |
--------------------------------------------------------------------------------
/流处理/01~流处理系统设计/执行框架/容错.md:
--------------------------------------------------------------------------------
1 | # 容错
2 |
3 | 批处理框架可以很容易地容错:如果 MapReduce 作业中的任务失败,可以简单地在另一台机器上再次启动,并且丢弃失败任务的输出。这种透明的重试是可能的,因为输入文件是不可变的,每个任务都将其输出写入到 HDFS 上的独立文件中,而输出仅当任务成功完成后可见。特别是,批处理容错方法可确保批处理作业的输出与没有出错的情况相同,即使实际上某些任务失败了。看起来好像每条输入记录都被处理了恰好一次,没有记录被跳过,而且没有记录被处理两次。尽管重启任务意味着实际上可能会多次处理记录,但输出中的可见效果看上去就像只处理过一次。这个原则被称为恰好一次语义(exactly-once semantics),尽管有效一次(effectively-once)可能会是一个更写实的术语。
4 |
5 | 在流处理中也出现了同样的容错问题,但是处理起来没有那么直观:等待某个任务完成之后再使其输出可见并不是一个可行选项,因为你永远无法处理完一个无限的流。
6 |
7 | # 微批量与存档点
8 |
9 | 一个解决方案是将流分解成小块,并像微型批处理一样处理每个块。这种方法被称为微批次(microbatching),它被用于 Spark Streaming。批次的大小通常约为 1 秒,这是对性能妥协的结果:较小的批次会导致更大的调度与协调开销,而较大的批次意味着流处理器结果可见之前的延迟要更长。微批次也隐式提供了一个与批次大小相等的滚动窗口(按处理时间而不是事件时间戳分窗)。任何需要更大窗口的作业都需要显式地将状态从一个微批次转移到下一个微批次。
10 |
11 | Apache Flink 则使用不同的方法,它会定期生成状态的滚动存档点并将其写入持久存储。如果流算子崩溃,它可以从最近的存档点重启,并丢弃从最近检查点到崩溃之间的所有输出。存档点会由消息流中的 壁障(barrier)触发,类似于微批次之间的边界,但不会强制一个特定的窗口大小。在流处理框架的范围内,微批次与存档点方法提供了与批处理一样的恰好一次语义。但是,只要输出离开流处理器(例如,写入数据库,向外部消息代理发送消息,或发送电子邮件),框架就无法抛弃失败批次的输出了。在这种情况下,重启失败任务会导致外部副作用发生两次,只有微批次或存档点不足以阻止这一问题。
12 |
13 | # 原子提交再现
14 |
15 | 为了在出现故障时表现出恰好处理一次的样子,我们需要确保事件处理的所有输出和副作用当且仅当处理成功时才会生效。这些影响包括发送给下游算子或外部消息传递系统(包括电子邮件或推送通知)的任何消息,任何数据库写入,对算子状态的任何变更,以及对输入消息的任何确认(包括在基于日志的消息代理中将消费者偏移量前移)。
16 |
17 | 这些事情要么都原子地发生,要么都不发生,但是它们不应当失去同步,该方法很类似于分布式事务与两阶段提交。
18 |
19 | # 幂等性
20 |
21 | 我们的目标是丢弃任何失败任务的部分输出,以便能安全地重试,而不会生效两次。分布式事务是实现这个目标的一种方式,而另一种方式是依赖幂等性(idempotence)。幂等操作是多次重复执行与单次执行效果相同的操作。例如,将键值存储中的某个键设置为某个特定值是幂等的(再次写入该值,只是用同样的值替代),而递增一个计数器不是幂等的(再次执行递增意味着该值递增两次)。
22 |
23 | 即使一个操作不是天生幂等的,往往可以通过一些额外的元数据做成幂等的。例如,在使用来自 Kafka 的消息时,每条消息都有一个持久的,单调递增的偏移量。将值写入外部数据库时可以将这个偏移量带上,这样你就可以判断一条更新是不是已经执行过了,因而避免重复执行。Storm 的 Trident 基于类似的想法来处理状态。依赖幂等性意味着隐含了一些假设:重启一个失败的任务必须以相同的顺序重放相同的消息(基于日志的消息代理能做这些事),处理必须是确定性的,没有其他节点能同时更新相同的值。
24 |
25 | 当从一个处理节点故障切换到另一个节点时,可能需要进行防护(fencing),以防止被假死节点干扰。尽管有这么多注意事项,幂等操作是一种实现恰好一次语义的有效方式,仅需很小的额外开销。
26 |
27 | # 失败后重建状态
28 |
29 | 任何需要状态的流处理,例如,任何窗口聚合(例如计数器,平均值和直方图)以及任何用于连接的表和索引,都必须确保在失败之后能恢复其状态。一种选择是将状态保存在远程数据存储中,并进行复制,然而正如在“流表连接”中所述,每个消息都要查询远程数据库可能会很慢。另一种方法是在流处理器本地保存状态,并定期复制。然后当流处理器从故障中恢复时,新任务可以读取状态副本,恢复处理而不丢失数据。例如,Flink 定期捕获算子状态的快照,并将它们写入 HDFS 等持久存储中。Samza 和 Kafka Streams 通过将状态变更发送到具有日志压缩功能的专用 Kafka 主题来复制状态变更,这与变更数据捕获类似。VoltDB 通过在多个节点上对每个输入消息进行冗余处理来复制状态。
30 |
31 | 在某些情况下,甚至可能都不需要复制状态,因为它可以从输入流重建。例如,如果状态是从相当短的窗口中聚合而成,则简单地重放该窗口中的输入事件可能是足够快的。如果状态是通过变更数据捕获来维护的数据库的本地副本,那么也可以从日志压缩的变更流中重建数据库。然而,所有这些权衡取决于底层基础架构的性能特征:在某些系统中,网络延迟可能低于磁盘访问延迟,网络带宽可能与磁盘带宽相当。没有针对所有情况的普世理想权衡,随着存储和网络技术的发展,本地状态与远程状态的优点也可能会互换。
32 |
--------------------------------------------------------------------------------
/批处理/Hadoop/MapReduce/WordCount.md:
--------------------------------------------------------------------------------
1 | # Word Count
2 |
3 | ```java
4 | import java.io.IOException;
5 | import java.util.StringTokenizer;
6 |
7 | import org.apache.hadoop.conf.Configuration;
8 | import org.apache.hadoop.fs.Path;
9 | import org.apache.hadoop.io.IntWritable;
10 | import org.apache.hadoop.io.Text;
11 | import org.apache.hadoop.mapreduce.Job;
12 | import org.apache.hadoop.mapreduce.Mapper;
13 | import org.apache.hadoop.mapreduce.Reducer;
14 | import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
15 | import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
16 |
17 | public class WordCount {
18 |
19 | public static class TokenizerMapper
20 | extends Mapper
24 |
25 | # Distributed System Series(分布式系统·实践笔记)
26 |
27 | 现实世界中的数据系统往往颇为复杂。大型应用程序经常需要以多种方式访问和处理数据,没有一个数据库可以同时满足所有这些不同的需求。因此应用程序通常组合使用多种组件:数据存储,索引,缓存,分析系统,等等,并实现在这些组件中移动数据的机制。许多现有数据系统中都采用这种数据处理方式:你发送请求指令,一段时间后(我们期望)系统会给出一个结果。数据库,缓存,搜索索引,Web 服务器以及其他一些系统都以这种方式工作。
28 |
29 | 像这样的在线(online)系统,无论是浏览器请求页面还是调用远程 API 的服务,我们通常认为请求是由人类用户触发的,并且正在等待响应。他们不应该等太久,所以我们非常关注系统的响应时间。值得说明的是,这不是构建系统的唯一方式,其他方法也有其优点。我们来看看三种不同类型的系统:
30 |
31 | - 服务(在线系统):服务等待客户的请求或指令到达。每收到一个,服务会试图尽快处理它,并发回一个响应。响应时间通常是服务性能的主要衡量指标,可用性通常非常重要(如果客户端无法访问服务,用户可能会收到错误消息)。
32 |
33 | - 批处理系统(离线系统):一个批处理系统有大量的输入数据,跑一个作业(job)来处理它,并生成一些输出数据,这往往需要一段时间(从几分钟到几天),所以通常不会有用户等待作业完成。相反,批量作业通常会定期运行(例如,每天一次)。批处理作业的主要性能衡量标准通常是吞吐量(处理特定大小的输入所需的时间)。
34 |
35 | - 流处理系统(准实时系统):流处理介于在线和离线(批处理)之间,所以有时候被称为准实时(near-real-time)或准在线(nearline)处理。像批处理系统一样,流处理消费输入并产生输出(并不需要响应请求)。但是,流式作业在事件发生后不久就会对事件进行操作,而批处理作业则需等待固定的一组输入数据。这种差异使流处理系统比起批处理系统具有更低的延迟。
36 |
37 | 
38 |
39 | ## Nav | 关联导航
40 |
41 | - 如果你想了解微服务/云原生等分布式系统的应用实践,可以参阅;如果你想了解数据库相关,可以参阅 [Database-Notes](https://github.com/wx-chevalier/Database-Notes);如果你想了解虚拟化与云计算相关,可以参阅 [Cloud-Notes](https://github.com/wx-chevalier/Cloud-Notes);如果你想了解 Linux 与操作系统相关,可以参阅 [Linux-Notes](https://github.com/wx-chevalier/Linux-Notes)。
42 |
43 | # About
44 |
45 | ## Copyright & More | 延伸阅读
46 |
47 | 笔者所有文章遵循 [知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh),欢迎转载,尊重版权。您还可以前往 [NGTE Books](https://ng-tech.icu/books-gallery/) 主页浏览包含知识体系、编程语言、软件工程、模式与架构、Web 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表:
48 |
49 | [](https://ng-tech.icu/books-gallery/)
50 |
51 |
52 |
53 |
54 | [contributors-shield]: https://img.shields.io/github/contributors/wx-chevalier/DistributedSystem-Notes.svg?style=flat-square
55 | [contributors-url]: https://github.com/wx-chevalier/DistributedSystem-Notes/graphs/contributors
56 | [forks-shield]: https://img.shields.io/github/forks/wx-chevalier/DistributedSystem-Notes.svg?style=flat-square
57 | [forks-url]: https://github.com/wx-chevalier/DistributedSystem-Notes/network/members
58 | [stars-shield]: https://img.shields.io/github/stars/wx-chevalier/DistributedSystem-Notes.svg?style=flat-square
59 | [stars-url]: https://github.com/wx-chevalier/DistributedSystem-Notes/stargazers
60 | [issues-shield]: https://img.shields.io/github/issues/wx-chevalier/DistributedSystem-Notes.svg?style=flat-square
61 | [issues-url]: https://github.com/wx-chevalier/DistributedSystem-Notes/issues
62 | [license-shield]: https://img.shields.io/github/license/wx-chevalier/DistributedSystem-Notes.svg?style=flat-square
63 | [license-url]: https://github.com/wx-chevalier/DistributedSystem-Notes/blob/master/LICENSE.txt
64 |
--------------------------------------------------------------------------------
/批处理/Hadoop/Hadoop 与数据库对比.md:
--------------------------------------------------------------------------------
1 | # Hadoop 与分布式数据库对比
2 |
3 | 正如我们所看到的,Hadoop 有点像 Unix 的分布式版本,其中 HDFS 是文件系统,而 MapReduce 是 Unix 进程的怪异实现(总是在 Map 阶段和 Reduce 阶段运行 sort 工具)。我们了解了如何在这些原语的基础上实现各种连接和分组操作。当 MapReduce 论文发表时,它从某种意义上来说:并不新鲜。我们在前几节中讨论的所有处理和并行连接算法已经在十多年前所谓的**大规模并行处理(MPP,massively parallel processing)**数据库中实现了。比如 Gamma database machine,Teradata 和 Tandem NonStop SQL 就是这方面的先驱。
4 |
5 | 最大的区别是,MPP 数据库专注于在一组机器上并行执行分析 SQL 查询,而 MapReduce 和分布式文件系统的组合则更像是一个可以运行任意程序的通用操作系统。
6 |
7 | # 存储多样性
8 |
9 | 数据库要求你根据特定的模型(例如关系或文档)来构造数据,而分布式文件系统中的文件只是字节序列,可以使用任何数据模型和编码来编写。它们可能是数据库记录的集合,但同样可以是文本,图像,视频,传感器读数,稀疏矩阵,特征向量,基因组序列或任何其他类型的数据。说白了,Hadoop 开放了将数据不加区分地转储到 HDFS 的可能性,允许后续再研究如何进一步处理。相比之下,在将数据导入数据库专有存储格式之前,MPP 数据库通常需要对数据和查询模式进行仔细的前期建模。在纯粹主义者看来,这种仔细的建模和导入似乎是可取的,因为这意味着数据库的用户有更高质量的数据来处理。然而实践经验表明,简单地使数据快速可用:即使它很古怪,难以使用,使用原始格式:也通常要比事先决定理想数据模型要更有价值。
10 |
11 | 这个想法与数据仓库类似:将大型组织的各个部分的数据集中在一起是很有价值的,因为它可以跨越以前相分离的数据集进行连接。MPP 数据库所要求的谨慎模式设计拖慢了集中式数据收集速度;以原始形式收集数据,稍后再操心模式的设计,能使数据收集速度加快(有时被称为“数据湖(data lake)”或“企业数据中心(enterprise data hub)”)。
12 |
13 | 不加区分的数据转储转移了解释数据的负担:数据集的生产者不再需要强制将其转化为标准格式,数据的解释成为消费者的问题(读时模式方法;参阅“文档模型中的架构灵活性”)。如果生产者和消费者是不同优先级的不同团队,这可能是一种优势。甚至可能不存在一个理想的数据模型,对于不同目的有不同的合适视角。以原始形式简单地转储数据,可以允许多种这样的转换。这种方法被称为寿司原则(sushi principle):“原始数据更好”。
14 |
15 | 因此,Hadoop 经常被用于实现 ETL 过程:事务处理系统中的数据以某种原始形式转储到分布式文件系统中,然后编写 MapReduce 作业来清理数据,将其转换为关系形式,并将其导入 MPP 数据仓库以进行分析。数据建模仍然在进行,但它在一个单独的步骤中进行,与数据收集相解耦。这种解耦是可行的,因为分布式文件系统支持以任何格式编码的数据。
16 |
17 | # 处理模型多样性
18 |
19 | MPP 数据库是单体的,紧密集成的软件,负责磁盘上的存储布局,查询计划,调度和执行。由于这些组件都可以针对数据库的特定需求进行调整和优化,因此整个系统可以在其设计针对的查询类型上取得非常好的性能。而且,SQL 查询语言允许以优雅的语法表达查询,而无需编写代码,使业务分析师用来做商业分析的可视化工具(例如 Tableau)能够访问。另一方面,并非所有类型的处理都可以合理地表达为 SQL 查询。例如,如果要构建机器学习和推荐系统,或者使用相关性排名模型的全文搜索索引,或者执行图像分析,则很可能需要更一般的数据处理模型。这些类型的处理通常是特别针对特定应用的(例如机器学习的特征工程,机器翻译的自然语言模型,欺诈预测的风险评估函数),因此它们不可避免地需要编写代码,而不仅仅是查询。
20 |
21 | MapReduce 使工程师能够轻松地在大型数据集上运行自己的代码。如果你有 HDFS 和 MapReduce,那么你可以在它之上建立一个 SQL 查询执行引擎,事实上这正是 Hive 项目所做的。但是,你也可以编写许多其他形式的批处理,这些批处理不必非要用 SQL 查询表示。随后,人们发现 MapReduce 对于某些类型的处理而言局限性很大,表现很差,因此在 Hadoop 之上其他各种处理模型也被开发出来。有两种处理模型,SQL 和 MapReduce,还不够,需要更多不同的模型!而且由于 Hadoop 平台的开放性,实施一整套方法是可行的,而这在单体 MPP 数据库的范畴内是不可能的。
22 |
23 | 至关重要的是,这些不同的处理模型都可以在共享的单个机器集群上运行,所有这些机器都可以访问分布式文件系统上的相同文件。在 Hadoop 方法中,不需要将数据导入到几个不同的专用系统中进行不同类型的处理:系统足够灵活,可以支持同一个群集内不同的工作负载。不需要移动数据,使得从数据中挖掘价值变得容易得多,也使采用新的处理模型容易的多。Hadoop 生态系统包括随机访问的 OLTP 数据库,如 HBase 和 MPP 风格的分析型数据库,如 Impala 。HBase 与 Impala 都不使用 MapReduce,但都使用 HDFS 进行存储。它们是迥异的数据访问与处理方法,但是它们可以共存,并被集成到同一个系统中。
24 |
25 | # 针对频繁故障设计
26 |
27 | 当比较 MapReduce 和 MPP 数据库时,两种不同的设计思路出现了:处理故障和使用内存与磁盘的方式。与在线系统相比,批处理对故障不太敏感,因为就算失败也不会立即影响到用户,而且它们总是能再次运行。如果一个节点在执行查询时崩溃,大多数 MPP 数据库会中止整个查询,并让用户重新提交查询或自动重新运行它。由于查询通常最多运行几秒钟或几分钟,所以这种错误处理的方法是可以接受的,因为重试的代价不是太大。MPP 数据库还倾向于在内存中保留尽可能多的数据(例如,使用哈希连接)以避免从磁盘读取的开销。
28 |
29 | 另一方面,MapReduce 可以容忍单个 Map 或 Reduce 任务的失败,而不会影响作业的整体,通过以单个任务的粒度重试工作。它也会非常急切地将数据写入磁盘,一方面是为了容错,另一部分是因为假设数据集太大而不能适应内存。
30 |
31 | MapReduce 方式更适用于较大的作业:要处理如此之多的数据并运行很长时间的作业,以至于在此过程中很可能至少遇到一个任务故障。在这种情况下,由于单个任务失败而重新运行整个作业将是非常浪费的。即使以单个任务的粒度进行恢复引入了使得无故障处理更慢的开销,但如果任务失败率足够高,这仍然是一种合理的权衡。但是这些假设有多么现实呢?在大多数集群中,机器故障确实会发生,但是它们不是很频繁:可能少到绝大多数作业都不会经历机器故障。为了容错,真的值得带来这么大的额外开销吗?
32 |
33 | 要了解 MapReduce 节约使用内存和在任务的层次进行恢复的原因,了解最初设计 MapReduce 的环境是很有帮助的。Google 有着混用的数据中心,在线生产服务和离线批处理作业在同样机器上运行。每个任务都有一个通过容器强制执行的资源配给(CPU 核心,RAM,磁盘空间等)。每个任务也具有优先级,如果优先级较高的任务需要更多的资源,则可以终止(抢占)同一台机器上较低优先级的任务以释放资源。优先级还决定了计算资源的定价:团队必须为他们使用的资源付费,而优先级更高的进程花费更多。
34 |
35 | 这种架构允许非生产(低优先级)计算资源被过量使用(overcommitted),因为系统知道必要时它可以回收资源。与分离生产和非生产任务的系统相比,过量使用资源可以更好地利用机器并提高效率。但由于 MapReduce 作业以低优先级运行,它们随时都有被抢占的风险,因为优先级较高的进程可能需要其资源。在高优先级进程拿走所需资源后,批量作业能有效地“捡面包屑”,利用剩下的任何计算资源。在谷歌,运行一个小时的 MapReduce 任务有大约有 5%的风险被终止,为了给更高优先级的进程挪地方。这一概率比硬件问题,机器重启或其他原因的概率高了一个数量级。按照这种抢占率,如果一个作业有 100 个任务,每个任务运行 10 分钟,那么至少有一个任务在完成之前被终止的风险大于 50%。
36 |
37 | 这就是 MapReduce 被设计为容忍频繁意外任务终止的原因:不是因为硬件很不可靠,而是因为任意终止进程的自由有利于提高计算集群中的资源利用率。在开源的集群调度器中,抢占的使用较少。YARN 的 CapacityScheduler 支持抢占,以平衡不同队列的资源分配,但在编写本文时,YARN,Mesos 或 Kubernetes 不支持通用优先级抢占。在任务不经常被终止的环境中,MapReduce 的这一设计决策就没有多少意义了。在下一节中,我们将研究一些与 MapReduce 设计决策相异的替代方案。
38 |
--------------------------------------------------------------------------------
/批处理/Hadoop/MapReduce/聚合计算.md:
--------------------------------------------------------------------------------
1 | # Sort
2 |
3 | ## 简单排序
4 |
5 | 对输入文件中数据进行排序。输入文件中的每行内容均为一个数字,即一个数据。要求在输出中每行有两个间隔的数字,其中,第一个代表原始数据在原始数据集中的位次,第二个代表原始数据。样例输入:
6 |
7 | ```
8 | 1)file1:
9 |
10 | 2
11 |
12 | 32
13 |
14 | 654
15 |
16 | 32
17 |
18 | 15
19 |
20 | 756
21 |
22 | 65223
23 |
24 |
25 | 2)file2:
26 |
27 | 5956
28 |
29 | 22
30 |
31 | 650
32 |
33 | 92
34 |
35 |
36 | 3)file3:
37 |
38 | 26
39 |
40 | 54
41 |
42 | 6
43 |
44 |
45 | 样例输出:
46 |
47 | 1 2
48 |
49 | 2 6
50 |
51 | 3 15
52 |
53 | 4 22
54 |
55 | 5 26
56 |
57 | 6 32
58 |
59 | 7 32
60 |
61 | 8 54
62 |
63 | 9 92
64 |
65 | 10 650
66 |
67 | 11 654
68 |
69 | 12 756
70 |
71 | 13 5956
72 |
73 | 14 65223
74 | ```
75 |
76 | 这个实例仅仅要求对输入数据进行排序,熟悉 MapReduce 过程的读者会很快想到在 MapReduce 过程中就有排序,是否可以利用这个默认的排序,而不需要自己再实现具体的排序呢?答案是肯定的。
77 |
78 | 但是在使用之前首先需要了解它的默认排序规则。它是按照 key 值进行排序的,如果 key 为封装 int 的 IntWritable 类型,那么 MapReduce 按照数字大小对 key 排序,如果 key 为封装为 String 的 Text 类型,那么 MapReduce 按照字典顺序对字符串排序。
79 |
80 | 了解了这个细节,我们就知道应该使用封装 int 的 IntWritable 型数据结构了。也就是在 map 中将读入的数据转化成 IntWritable 型,然 后作为 key 值输出(value 任意)。reduce 拿到之后,将输入的 key 作为 value 输出,并根据 value-list 中元素的个数决定输出的次数。输出的 key(即代码中的 linenum)是一个全局变量,它统计当前 key 的位次。需要注意的是这个 程序中没有配置 Combiner,也就是在 MapReduce 过程中不使用 Combiner。这主要是因为使用 map 和 reduce 就已经能够完成任务 了。
81 |
82 | ```java
83 | public class Sort {
84 |
85 |
86 | //map将输入中的value化成IntWritable类型,作为输出的key
87 |
88 | public static class Map extends
89 |
90 | Mapper