diff --git a/README.md b/README.md
index 47ec489..e1a1be0 100644
--- a/README.md
+++ b/README.md
@@ -109,7 +109,7 @@
| 第九章:一致性与共识 | 初翻 65% | Vonng |
| 第三部分:前言 | 精翻 | |
| 第十章:批处理 | 草翻 | |
-| 第十一章:流处理 | 机翻 | |
+| 第十一章:流处理 | 草翻 | |
| 第十二章:数据系统的未来 | 机翻 | |
| 术语表 | - | |
| 后记 | 机翻 | |
diff --git a/ch11.md b/ch11.md
index e00cee8..ffdffff 100644
--- a/ch11.md
+++ b/ch11.md
@@ -269,7 +269,7 @@ Apache Kafka 【17,18】,Amazon Kinesis Streams 【19】和Twitter的Distribut
我们可以调用日志消费者导出的数据系统,正如在第三部分的介绍中所讨论的:存储在搜索索引和数据仓库中的数据只是记录系统中数据的另一个视图。更改数据捕获是一种机制,可确保对记录系统所做的所有更改都反映在派生数据系统中,以便派生系统具有数据的准确副本。
-从本质上说,改变数据捕获使得一个数据库成为领导者(从中捕获变化的数据库),并将其他人变成追随者。基于日志的消息代理非常适合从源数据库传输更改事件,因为它保留了消息的排序(避免了图11-2的重新排序问题)。
+从本质上说,改变数据捕获使得一个数据库成为领导者(从中捕获变化的数据库),并将其他人变成追随者。基于日志的消息代理非常适合从源数据库传输更改事件,因为它保留了消息的排序(避免了[图11-2](img/fig11-2.png)的重新排序问题)。
数据库触发器可用于通过注册触发器来实现更改数据捕获(参阅“[基于触发器的复制](ch5.md#基于触发器的复制)”),这些触发器可观察数据表的所有更改,并将相应的条目添加到更改日志表中。但是,他们往往是脆弱的,并有显着的性能开销。解析复制日志可以是一个更强大的方法,但它也带来了挑战,例如处理模式更改。
@@ -316,7 +316,7 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
* 在更改数据捕获中,应用程序以可变方式使用数据库,随意更新和删除记录。从数据库中提取较低级别的更改日志(例如,通过解析复制日志),从而确保从数据库中提取的写入顺序与实际写入的顺序相匹配,从而避免[图11-4](img/fig11-4.png)中的竞争条件。写入数据库的应用程序不需要知道CDC正在发生。
* 在事件源中,应用程序逻辑是基于写入事件日志的不可变事件而显式构建的。在这种情况下,事件存储是附加的,更新或删除是不鼓励或禁止的。事件旨在反映应用程序级别发生的事情,而不是低级状态更改。
-事件源是一种强大的数据建模技术:从应用程序的角度来看,将用户的行为记录为不可变的事件更有意义,而不是记录这些行为对可变数据库的影响。事件采购使得随着时间的推移而逐渐发展应用程序变得更加容易,通过更容易理解事情发生的原因以及防范应用程序错误(请参阅“不可变事件的优点”),帮助进行调试。
+事件源是一种强大的数据建模技术:从应用程序的角度来看,将用户的行为记录为不可变的事件更有意义,而不是记录这些行为对可变数据库的影响。事件采购使得随着时间的推移而逐渐发展应用程序变得更加容易,通过更容易理解事情发生的原因以及防范应用程序错误(请参阅“[不可变事件的优点](#不可变事件的优点)”),帮助进行调试。
例如,存储“学生取消课程注册”事件清楚地表达了单一行为的中性意图,而副作用“从注册表中删除了一个条目,并且一个取消原因被添加到学生反馈表“嵌入了很多有关方式的假设数据稍后将被使用。如果引入新的应用程序功能,例如“将地点提供给等待列表中的下一个人” —— 事件顺序方法允许将新的副作用轻松地链接到现有事件上。
@@ -359,7 +359,7 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
无论国家如何变化,总会有一系列事件导致这些变化。即使事情已经解决,事实仍然是事实发生的事实。关键的想法是可变状态和不可变事件的附加日志不相互矛盾:它们是同一枚硬币的两面。所有变化的日志,变化日志,代表了随着时间的推移状态的演变。
-如果您有数学上的倾向,那么您可能会说应用程序状态是随着时间的推移整合了一个事件流而得到的,而且当您按照时间区分状态时会得到一个更改流,如图11-6所示[ 49,50,51]。这个比喻有一定的局限性(例如,国家的二阶导数似乎没有意义),但这是考虑数据的一个有用的起点。
+如果您有数学上的倾向,那么您可能会说应用程序状态是随着时间的推移整合了一个事件流而得到的,而且当您按照时间区分状态时会得到一个更改流,如[图11-6](img/fig11-6.png)所示【49,50,51】。这个比喻有一定的局限性(例如,国家的二阶导数似乎没有意义),但这是考虑数据的一个有用的起点。
![](img/fig11-6.png)
@@ -383,7 +383,7 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
#### 从同一事件日志中获取多个视图
-而且,通过从不变事件日志中分离可变状态,可以从事件的相同日志中派生出几个不同的面向读取的表示。这就像一个流的多个消费者一样工作([图11-5](img/fig11-5.png)):例如,分析数据库Druid使用这种方法从Kafka直接获取【55】,Pista chio是一个分布式的键值存储,使用Kafka作为提交日志【56】,Kafka Connect接收器可以将来自Kafka的数据导出到各种不同的数据库和索引【41】。对于许多其他存储和索引系统(如搜索服务器)来说,类似地从分布式日志中获取输入也是有意义的(请参阅第455页上的“保持系统同步”)。
+而且,通过从不变事件日志中分离可变状态,可以从事件的相同日志中派生出几个不同的面向读取的表示。这就像一个流的多个消费者一样工作([图11-5](img/fig11-5.png)):例如,分析数据库Druid使用这种方法从Kafka直接获取【55】,Pista chio是一个分布式的键值存储,使用Kafka作为提交日志【56】,Kafka Connect接收器可以将来自Kafka的数据导出到各种不同的数据库和索引【41】。对于许多其他存储和索引系统(如搜索服务器)来说,类似地从分布式日志中获取输入也是有意义的(请参阅“[保持系统同步](#保持系统同步)”)。
从事件日志到数据库有一个明确的转换步骤,可以更容易地随时间推移您的应用程序:如果您想要引入一个以新的方式呈现现有数据的新功能,您可以使用事件日志来构建一个单独的新功能的读取优化视图,并与现有的一起运行
@@ -391,19 +391,19 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
如果您不必担心如何查询和访问数据,那么存储数据通常是非常简单的。模式设计,索引和存储引擎的许多复杂性都是希望支持某些查询和访问模式的结果(参见[第3章](ch3.md))。出于这个原因,通过将数据写入的形式与读取形式分开,并允许几个不同的读取视图,可以获得很大的灵活性。这个想法有时被称为命令查询责任分离(CQRS)【42,58,59】。
-数据库和模式设计的传统方法是基于数据必须以与查询相同的形式写入的谬误。有关正常化和非规范化的争论(请参阅第31页上的“多对一和多对多关系”),如果可以将数据从写入优化的事件日志转换为读取优化的应用程序状态,则变得基本无关紧要:在读取优化的视图中对数据进行非规范化是完全合理的,因为翻译过程为您提供了一种机制,使其与事件日志保持一致。
+数据库和模式设计的传统方法是基于数据必须以与查询相同的形式写入的谬误。有关正常化和非规范化的争论(参阅“[多对一和多对多的关系](ch2.md#多对一和多对多的关系)”),如果可以将数据从写入优化的事件日志转换为读取优化的应用程序状态,则变得基本无关紧要:在读取优化的视图中对数据进行非规范化是完全合理的,因为翻译过程为您提供了一种机制,使其与事件日志保持一致。
在“[描述负载](ch1.md#描述负载)”中,我们讨论了推特主页时间表,最近一个特定用户正在关注的人(如邮箱)写的最近发布的推文缓存。这是阅读优化状态的另一个例子:家庭时间表高度变形,因为你的推文在所有跟随你的人的时间线上都是重复的。然而,扇出服务保持这种复制状态与新的推文和新的以下关系保持同步,这保持了复制的可管理性。
#### 并发控制
-事件采集和更改数据捕获的最大缺点是事件日志的消费者通常是异步的,所以用户可能会写入日志,然后从日志派生的视图中读取并查找他们的写作还没有反映在读取视图。我们在第162页的“阅读您自己的作品”中讨论了这个问题和潜在的解决方案。
+事件采集和更改数据捕获的最大缺点是事件日志的消费者通常是异步的,所以用户可能会写入日志,然后从日志派生的视图中读取并查找他们的写作还没有反映在读取视图。我们在“[读己之写](ch5.md#读己之写)”中讨论了这个问题和潜在的解决方案。
-一种解决方案是同步执行读取视图的更新,并将事件附加到日志中。这需要一个事务来将写入操作合并到一个原子单元中,所以要么需要将事件日志和读取视图保存在同一个存储系统中,要么需要跨不同系统的分布式事务。或者,您可以使用第350页上的“使用总订单广播实现线性化存储”中讨论的方法。
+一种解决方案是同步执行读取视图的更新,并将事件附加到日志中。这需要一个事务来将写入操作合并到一个原子单元中,所以要么需要将事件日志和读取视图保存在同一个存储系统中,要么需要跨不同系统的分布式事务。或者,您可以使用在“[使用全序广播实现线性化存储](ch9.md#使用全序广播实现线性化存储)”中讨论的方法。
-另一方面,从事件日志导出当前状态也简化了并发控制的某些方面。对多个对象事务的需求(请参阅第228页上的“单对象和多对象操作”)源于单个用户操作,需要在多个不同的位置更改数据。通过事件采购,您可以设计一个事件,以便对用户操作进行独立的描述。用户操作只需要在一个地方进行一次写操作,即将事件附加到日志中,这很容易使原子化。
+另一方面,从事件日志导出当前状态也简化了并发控制的某些方面。对多个对象事务的需求(参阅“[单对象和多对象操作](ch7.md#单对象和多对象操作)”)源于单个用户操作,需要在多个不同的位置更改数据。通过事件采购,您可以设计一个事件,以便对用户操作进行独立的描述。用户操作只需要在一个地方进行一次写操作,即将事件附加到日志中,这很容易使原子化。
-如果事件日志和应用程序状态以相同的方式分区(例如,为分区3中的客户处理事件只需要更新应用程序状态的分区3),则直接的单线程日志消费者不需要并发控制(write-by)构造,它一次只处理一个事件(另请参阅第252页的“实际的串行执行”)。该日志通过在分区中定义事件的串行顺序来消除并发性的不确定性【24】。如果一个事件触及多个状态分区,那么需要做更多的工作,我们将在第12章讨论。
+如果事件日志和应用程序状态以相同的方式分区(例如,为分区3中的客户处理事件只需要更新应用程序状态的分区3),则直接的单线程日志消费者不需要并发控制(write-by)构造,它一次只处理一个事件(参阅“[真的的串行执行](ch7.md#真的的串行执行)”)。该日志通过在分区中定义事件的串行顺序来消除并发性的不确定性【24】。如果一个事件触及多个状态分区,那么需要做更多的工作,我们将在[第12章](ch12.md)讨论。
#### 不变性的限制
@@ -425,7 +425,7 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
剩下的就是讨论一下你可以用流做什么 —— 也就是说,你可以处理它。一般来说,有三种选择:
-1. 您可以将事件中的数据写入数据库,缓存,搜索索引或类似的存储系统,然后由其他客户端查询。如图11-5所示,这是保持数据库与系统其他部分发生更改同步的好方法 - 特别是当流消费者是写入数据库的唯一客户端时。写入存储系统的流程相当于我们在“批处理工作流程的输出”页面上讨论的内容。
+1. 您可以将事件中的数据写入数据库,缓存,搜索索引或类似的存储系统,然后由其他客户端查询。如[图11-5](img/fig11-5.png)所示,这是保持数据库与系统其他部分发生更改同步的好方法 —— 特别是当流消费者是写入数据库的唯一客户端时。写入存储系统的流程相当于我们在“批处理工作流程的输出”页面上讨论的内容。
2. 您可以以某种方式将事件推送给用户,例如通过发送电子邮件警报或推送通知,或通过将事件流式传输到可实时显示的实时仪表板。在这种情况下,人是流的最终消费者。
3. 您可以处理一个或多个输入流以产生一个或多个输出流。数据流可能会经过由几个这样的处理阶段组成的流水线,然后才会输出(选项1或2)。
@@ -433,7 +433,7 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
流处理器中的分区和并行化模式也非常类似于[第10章](ch10.md)中介绍的MapReduce和数据流引擎,因此我们不在这里重复这些主题。基本的映射操作(如转换和过滤记录)也是一样的。
-批量作业的一个关键区别是流不会结束。这种差别有很多含义:正如本章开始部分所讨论的,排序对无界数据集没有意义,因此不能使用排序合并联接(请参阅“减少联接和分组”)。容错机制也必须改变:对于已经运行了几分钟的批处理作业,可以简单地从头开始重新启动失败的任务,但是对于已经运行数年的流作业,在开始后重新开始崩溃可能不是一个可行的选择。
+批量作业的一个关键区别是流不会结束。这种差别有很多含义:正如本章开始部分所讨论的,排序对无界数据集没有意义,因此不能使用排序合并联接(请参阅“[减少连接和分组](ch10.md#减少连接和分组)”)。容错机制也必须改变:对于已经运行了几分钟的批处理作业,可以简单地从头开始重新启动失败的任务,但是对于已经运行数年的流作业,在开始后重新开始崩溃可能不是一个可行的选择。
### 流处理的应用
@@ -683,7 +683,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
一种选择是将状态保持在远程数据存储中并复制它,尽管如每个单独消息的远程数据库查询速度可能会很慢,正如在“[流表连接](#流表连接)”中所述。另一种方法是保持流处理器的本地状态,并定期复制。然后,当流处理器从故障中恢复时,新任务可以读取复制状态并恢复处理而不丢失数据。
-例如,Flink定期捕获操作员状态的快照,并将它们写入HDFS等持久存储器中【92,93】。 Samza和Kafka Streams通过将状态更改发送到具有日志压缩功能的专用Kafka主题来复制状态更改,这类似于更改数据捕获【84,100】。 VoltDB通过冗余处理多个节点上的每个输入消息来复制状态(请参阅第252页的“实际的串行执行”)。
+例如,Flink定期捕获操作员状态的快照,并将它们写入HDFS等持久存储器中【92,93】。 Samza和Kafka Streams通过将状态更改发送到具有日志压缩功能的专用Kafka主题来复制状态更改,这类似于更改数据捕获【84,100】。 VoltDB通过冗余处理多个节点上的每个输入消息来复制状态(参阅“[真的串行执行](ch7.md#真的串行执行)”)。
在某些情况下,甚至可能不需要复制状态,因为它可以从输入流重建。例如,如果状态由一个相当短的窗口中的聚合组成,则它可能足够快,以便重放与该窗口相对应的输入事件。如果状态是通过更改数据捕获维护的数据库的本地副本,那么也可以从日志压缩的更改流重建数据库(请参阅“[日志压缩](#日志压缩)”一节)。
@@ -700,7 +700,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
***AMQP/JMS风格的消息代理***
- 经纪人将个人消息分配给消费者,消费者在成功处理个人消息时确认消息。消息被确认后从代理中删除。这种方法适合作为RPC的异步形式(另请参阅第136页的“消息传递数据流”),例如在任务队列中,消息处理的确切顺序并不重要,没有在处理之后,需要重新读取旧消息。
+ 经纪人将个人消息分配给消费者,消费者在成功处理个人消息时确认消息。消息被确认后从代理中删除。这种方法适合作为RPC的异步形式(另请参阅“[消息传递数据流](ch4.md#消息传递数据流)”),例如在任务队列中,消息处理的确切顺序并不重要,没有在处理之后,需要重新读取旧消息。
***基于日志的消息代理***
@@ -820,8 +820,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
*engineering.linkedin.com*, December 16, 2013.
1. Shirshanka Das, Chavdar Botev, Kapil Surlaker,
- et al.: “[All Aboard the Databus!](http://www.socc2012.org/s18-das.pdf),” at *3rd ACM
- Symposium on Cloud Computing* (SoCC), October 2012.
+ et al.: “[All Aboard the Databus!](http://www.socc2012.org/s18-das.pdf),” at *3rd ACM Symposium on Cloud Computing* (SoCC), October 2012.
1. Yogeshwer Sharma, Philippe Ajoux, Petchean Ang, et al.:
“[Wormhole: Reliable Pub-Sub to Support Geo-Replicated Internet Services](https://www.usenix.org/system/files/conference/nsdi15/nsdi15-paper-sharma.pdf),” at *12th USENIX Symposium on
@@ -873,8 +872,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
February 18, 2016.
1. Greg Young:
- “[CQRS and Event Sourcing](https://www.youtube.com/watch?v=JHGkaShoyNs),” at *Code on
- the Beach*, August 2014.
+ “[CQRS and Event Sourcing](https://www.youtube.com/watch?v=JHGkaShoyNs),” at *Code on the Beach*, August 2014.
1. Martin Fowler:
“[Event Sourcing](http://martinfowler.com/eaaDev/EventSourcing.html),” *martinfowler.com*,
@@ -885,15 +883,13 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
Addison-Wesley Professional, 2013. ISBN: 978-0-321-83457-7
1. H. V. Jagadish, Inderpal Singh Mumick, and Abraham Silberschatz:
- “[View Maintenance Issues for the Chronicle Data Model](http://www.mathcs.emory.edu/~cheung/papers/StreamDB/Histogram/1995-Jagadish-Histo.pdf),” at *14th ACM SIGACT-SIGMOD-SIGART Symposium
- on Principles of Database Systems* (PODS), May 1995.
+ “[View Maintenance Issues for the Chronicle Data Model](http://www.mathcs.emory.edu/~cheung/papers/StreamDB/Histogram/1995-Jagadish-Histo.pdf),” at *14th ACM SIGACT-SIGMOD-SIGART Symposium on Principles of Database Systems* (PODS), May 1995.
[doi:10.1145/212433.220201](http://dx.doi.org/10.1145/212433.220201)
1. “[Event Store 3.5.0 Documentation](http://docs.geteventstore.com/),” Event Store LLP, *docs.geteventstore.com*, February 2016.
1. Martin Kleppmann:
- *Making Sense of Stream
- Processing*. Report, O'Reilly Media, May 2016.
+ *Making Sense of Stream Processing*. Report, O'Reilly Media, May 2016.
1. Sander Mak:
“[Event-Sourced Architectures with Akka](http://www.slideshare.net/SanderMak/eventsourced-architectures-with-akka),” at *JavaOne*, September 2014.
@@ -907,8 +903,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
ISBN: 978-0-262-57122-7
1. Timothy Griffin and Leonid Libkin:
- “[Incremental Maintenance of Views with Duplicates](http://homepages.inf.ed.ac.uk/libkin/papers/sigmod95.pdf),” at *ACM International Conference on Management of
- Data* (SIGMOD), May 1995.
+ “[Incremental Maintenance of Views with Duplicates](http://homepages.inf.ed.ac.uk/libkin/papers/sigmod95.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), May 1995.
[doi:10.1145/223784.223849](http://dx.doi.org/10.1145/223784.223849)
1. Pat Helland:
@@ -978,8 +973,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
1. Philippe Flajolet, Éric Fusy, Olivier
Gandouet, and Frédéric Meunier:
- “[HyperLogLog: The Analysis of a Near-Optimal Cardinality Estimation Algorithm](http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf),” at *Conference on Analysis of
- Algorithms* (AofA), June 2007.
+ “[HyperLogLog: The Analysis of a Near-Optimal Cardinality Estimation Algorithm](http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf),” at *Conference on Analysis of Algorithms* (AofA), June 2007.
1. Jay Kreps:
“[Questioning the Lambda Architecture](https://www.oreilly.com/ideas/questioning-the-lambda-architecture),” *oreilly.com*, July 2, 2014.
@@ -1019,8 +1013,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
1. Rajagopal Ananthanarayanan,
Venkatesh Basker, Sumit Das, et al.:
- “[Photon: Fault-Tolerant and Scalable Joining of Continuous Data Streams](http://research.google.com/pubs/pub41318.html),” at *ACM International Conference on Management of
- Data* (SIGMOD), June 2013.
+ “[Photon: Fault-Tolerant and Scalable Joining of Continuous Data Streams](http://research.google.com/pubs/pub41318.html),” at *ACM International Conference on Management of Data* (SIGMOD), June 2013.
[doi:10.1145/2463676.2465272](http://dx.doi.org/10.1145/2463676.2465272)
1. Martin Kleppmann:
@@ -1053,8 +1046,7 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
“[Lightweight Asynchronous Snapshots for Distributed Dataflows](http://arxiv.org/abs/1506.08603),” arXiv:1506.08603 [cs.DC], June 29, 2015.
1. Ryan Betts and John Hugg:
- *Fast Data: Smart and
- at Scale*. Report, O'Reilly Media, October 2015.
+ *Fast Data: Smart and at Scale*. Report, O'Reilly Media, October 2015.
1. Flavio Junqueira:
“[Making Sense of Exactly-Once Semantics](http://conferences.oreilly.com/strata/hadoop-big-data-eu/public/schedule/detail/49690),” at *Strata+Hadoop World London*, June 2016.