diff --git a/README.md b/README.md
index 97e2b50..b496458 100644
--- a/README.md
+++ b/README.md
@@ -106,9 +106,9 @@
| 第八章:分布式系统中的问题 | 初翻 | |
| 第九章:一致性与共识 | 初翻 | |
| 第三部分:衍生数据 | 精翻 | |
-| 第十章:批处理 | 草翻 | |
+| 第十章:批处理 | 草翻 | Vonng |
| 第十一章:流处理 | 草翻 | |
-| 第十二章:数据系统的未来 | 草翻 | |
+| 第十二章:数据系统的未来 | 初翻 40% | Vonng |
| 术语表 | - | |
| 后记 | 初翻 | |
@@ -124,12 +124,15 @@ All contribution will give proper credit. 贡献者需要同意[法律声明](#
1. [序言初翻修正](https://github.com/Vonng/ddia/commit/afb5edab55c62ed23474149f229677e3b42dfc2c) by [@seagullbird](https://github.com/Vonng/ddia/commits?author=seagullbird)
2. [第一章语法标点校正](https://github.com/Vonng/ddia/commit/973b12cd8f8fcdf4852f1eb1649ddd9d187e3644) by [@nevertiree](https://github.com/Vonng/ddia/commits?author=nevertiree)
-3. [第六章部分校正](https://github.com/Vonng/ddia/commit/d4eb0852c0ec1e93c8aacc496c80b915bb1e6d48) by @[MuAlex](https://github.com/Vonng/ddia/commits?author=MuAlex)
+3. [第六章部分校正](https://github.com/Vonng/ddia/commit/d4eb0852c0ec1e93c8aacc496c80b915bb1e6d48) 与[第10章的翻译](https://github.com/Vonng/ddia/commit/9de8dbd1bfe6fbb03b3bf6c1a1aa2291aed2490e) by @[MuAlex](https://github.com/Vonng/ddia/commits?author=MuAlex)
4. 第一部分前言,ch2校正 by @jiajiadebug
5. 词汇表、后记关于野猪的部分 by @[Chowss](https://github.com/Vonng/ddia/commits?author=Chowss)
+
+
+
## 译读者交流微信群

diff --git a/ch10.md b/ch10.md
index b73ddb8..59658dd 100644
--- a/ch10.md
+++ b/ch10.md
@@ -10,58 +10,60 @@
[TOC]
-在本书的前两部分中,我们讨论了很多关于请求和查询以及相应的响应或结果。许多现有数据系统中都采用这种数据处理方式:你发送请求指令,一段时间后(我们期望)系统会给出一个结果。数据库,缓存,搜索索引,Web服务器以及其他一些系统都以这种方式工作。
+ 在本书的前两部分中,我们讨论了很多关于**请求**和**查询**以及相应的**响应**或**结果**。许多现有数据系统中都采用这种数据处理方式:你发送请求指令,一段时间后(我们期望)系统会给出一个结果。数据库,缓存,搜索索引,Web服务器以及其他一些系统都以这种方式工作。
-像这样的线上系统,无论是浏览器请求页面还是调用远程API的服务,我们通常认为请求是由用户触发的,并且正在等待响应。他们不应该等太久,所以我们非常关注系统的响应时间(参阅“[描述性能](ch1.md)”)。
+ 像这样的**在线(online)**系统,无论是浏览器请求页面还是调用远程API的服务,我们通常认为请求是由用户触发的,并且正在等待响应。他们不应该等太久,所以我们非常关注系统的响应时间(参阅“[描述性能](ch1.md)”)。
-Web和越来越多的基于HTTP/REST的API使交互的请求/响应风格变得如此普遍,以至于很容易将其视为理所当然。但我们应该记住,这不是构建系统的唯一方式,其他方法也有其优点。我们来看看三种不同类型的系统:
+ Web和越来越多的基于HTTP/REST的API使交互的请求/响应风格变得如此普遍,以至于很容易将其视为理所当然。但我们应该记住,这不是构建系统的唯一方式,其他方法也有其优点。我们来看看三种不同类型的系统:
- ***服务(线上系统)***
+ ***服务(在线系统)***
-- 服务等待客户的请求或指令到达。每收到一个,服务会试图尽快处理它,并发回一个响应。响应时间通常是服务性能的主要衡量指标,可用性通常非常重要(如果客户端无法访问服务,用户可能会收到错误消息)。
+ 服务等待客户的请求或指令到达。每收到一个,服务会试图尽快处理它,并发回一个响应。响应时间通常是服务性能的主要衡量指标,可用性通常非常重要(如果客户端无法访问服务,用户可能会收到错误消息)。
-***批处理系统(线下系统)***
+***批处理系统(离线系统)***
-- 一个批处理系统有大量的输入数据,跑一个作业(job)来处理它,并生成一些输出数据,这往往需要一段时间(从几分钟到几天),所以通常不会有用户等待作业完成。相反,批量作业通常会定期运行(例如,每天一次)。批处理作业的主要性能衡量标准通常是吞吐量(处理特定大小的输入所需的时间)。本章中讨论的就是批处理。
+ 一个批处理系统有大量的输入数据,跑一个**作业(job)**来处理它,并生成一些输出数据,这往往需要一段时间(从几分钟到几天),所以通常不会有用户等待作业完成。相反,批量作业通常会定期运行(例如,每天一次)。批处理作业的主要性能衡量标准通常是吞吐量(处理特定大小的输入所需的时间)。本章中讨论的就是批处理。
-***流处理系统(近实时系统)***
+***流处理系统(准实时系统)***
-- 流处理介于线上和线下(批处理)之间,所以有时候被称为近实时或近线(nearline)处理。像批处理系统一样,流处理消费输入并产生输出(并不需要响应请求)。但是,流式作业在事件发生后不久就会对事件进行操作,而批处理作业则需等待固定的一组输入数据。这种差异使流处理系统比起批处理系统具有更低的延迟。由于流处理基于批处理,我们将在第11章讨论它。
+ 流处理介于在线和离线(批处理)之间,所以有时候被称为**准实时(near-real-time)**或**准在线(nearline)**处理。像批处理系统一样,流处理消费输入并产生输出(并不需要响应请求)。但是,流式作业在事件发生后不久就会对事件进行操作,而批处理作业则需等待固定的一组输入数据。这种差异使流处理系统比起批处理系统具有更低的延迟。由于流处理基于批处理,我们将在[第11章](ch11.md)讨论它。
-正如我们将在本章中看到的那样,批处理是构建可靠,可扩展和可维护应用程序的重要组成部分。例如,2004年发布的批处理算法Map-Reduce(可能有点过于热门)被称为“造就Google大规模可扩展性的算法”)被称为“造就Google大规模可扩展性的算法”[2]。随后在各种开源数据系统中得到应用,包括Hadoop,CouchDB和MongoDB。
-与多年前为数据仓库开发的并行处理系统【3,4】相比,MapReduce是一个相当低级别的编程模型,但它在现有硬件水平基础上,迈出了处理大数据重要的一步。虽然MapReduce的重要性正在下降【5】,但它仍然值得理解,因为它提供了一个清晰的画面来阐述批处理为什么以及如何有用。
-实际上,批处理是一种非常古老的计算方式。早在可编程数字计算机诞生之前,打孔卡制表机(例如1890年美国人口普查【6】中使用的霍尔里斯机)实现了半机械化的批处理形式,从大量输入中汇总计算。 Map-Reduce与1940年代和1950年代广泛用于商业数据处理的机电IBM卡片分类机器有着惊人的相似之处[7]。正如我们所说,历史总是在不断重演。
+ 正如我们将在本章中看到的那样,批处理是构建可靠,可扩展和可维护应用程序的重要组成部分。例如,2004年发布的批处理算法Map-Reduce(可能被过分热情地)被称为“造就Google大规模可扩展性的算法”【2】。随后在各种开源数据系统中得到应用,包括Hadoop,CouchDB和MongoDB。
-在本章中,我们将了解MapReduce和其他一些批处理算法和框架,并探索它们在现代数据系统中的作用。但首先我们将看看使用标准Unix工具的数据处理。即使你已经熟悉了它们,Unix的哲学也值得一读,Unix的思想和经验教训可以转移到大规模,异构的分布式数据系统中。
+ 与多年前为数据仓库开发的并行处理系统【3,4】相比,MapReduce是一个相当低级别的编程模型,但它使得在商用硬件上能进行的处理规模迈上一个新的台阶。虽然MapReduce的重要性正在下降【5】,但它仍然值得去理解,因为它描绘了一幅关于批处理为什么有用,以及如何实用的清晰图景。
+
+ 实际上,批处理是一种非常古老的计算方式。早在可编程数字计算机诞生之前,打孔卡制表机(例如1890年美国人口普查【6】中使用的霍尔里斯机)实现了半机械化的批处理形式,从大量输入中汇总计算。 Map-Reduce与1940年代和1950年代广泛用于商业数据处理的机电IBM卡片分类机器有着惊人的相似之处【7】。正如我们所说,历史总是在不断重复自己。
+
+ 在本章中,我们将了解MapReduce和其他一些批处理算法和框架,并探索它们在现代数据系统中的作用。但首先我们将看看使用标准Unix工具的数据处理。即使你已经熟悉了它们,Unix的哲学也值得一读,Unix的思想和经验教训可以迁移到大规模,异构的分布式数据系统中。
## 使用Unix工具的批处理
-我们从一个简单的例子开始。假设您有一台Web服务器,每次处理请求时都会在日志文件中附加一行。例如,使用nginx默认访问日志格式,日志的一行可能如下所示:
+ 我们从一个简单的例子开始。假设您有一台Web服务器,每次处理请求时都会在日志文件中附加一行。例如,使用nginx默认访问日志格式,日志的一行可能如下所示:
-```
+```bash
216.58.210.78 - - [27/Feb/2015:17:55:11 +0000] "GET /css/typography.css HTTP/1.1"
200 3377 "http://martin.kleppmann.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36"
```
-(实际上这只是一行,分成多行只是为了便于阅读)。这一行中有很多信息。为了理解它,你需要看看日志格式的定义,如下所示:
+ (实际上这只是一行,分成多行只是为了便于阅读。)这一行中有很多信息。为了理解它,你需要了解日志格式的定义,如下所示:
```
$remote_addr - $remote_user [$time_local] "$request"
$status $body_bytes_sent "$http_referer" "$http_user_agent"
```
-日志的这一行表明在2015年2月27日17:55:11 UTC,服务器从客户端IP地址216.58.210.78接收到对文件`/css/typography.css`的请求。用户没有被认证,所以`$remote_user`被设置为连字符(`-` )。响应状态是200(即请求成功),响应的大小是3377字节。网页浏览器是Chrome 40,并且加载了该文件,因为在URL `http://martin.kleppmann.com/`的页面中引用。
+ 日志的这一行表明在2015年2月27日17:55:11 UTC,服务器从客户端IP地址`216.58.210.78`接收到对文件`/css/typography.css`的请求。用户没有被认证,所以`$remote_user`被设置为连字符(`-` )。响应状态是200(即请求成功),响应的大小是3377字节。网页浏览器是Chrome 40,URL `http://martin.kleppmann.com/` 的页面中的引用导致该文件被加载。
### 分析简单日志
-很多工具可以利用这些日志文件产生关于你网站流量的漂亮的报告,但为了练手,让我们使用基本的Unix功能创建自己的工具。 例如,假设你想在你的网站上找到五个最受欢迎的网页。 你可以在Unix shell中这样做:[^i]
+ 很多工具可以利用这些日志文件产生关于你网站流量的漂亮的报告,但为了练手,让我们使用基本的Unix功能创建自己的工具。 例如,假设你想在你的网站上找到五个最受欢迎的网页。 则可以在Unix shell中这样做:[^i]
[^i]: 有些人认为`cat`这里并没有必要,因为输入文件可以直接作为awk的参数。 但这种写法让线性管道更为显眼。
@@ -77,9 +79,9 @@ cat /var/log/nginx/access.log | #1
1. 读取日志文件
2. 将每一行按空格分割成不同的字段,每行只输出第七个字段,恰好是请求的URL。在我们的例子中是`/css/typography.css`。
3. 按字母顺序排列请求的URL列表。如果某个URL被请求过n次,那么排序后,文件在一行中会重复出现n次该URL。
-4. uniq命令通过检查两个相邻的行是否相同来过滤掉输入中的重复行。 -c则表示输出一个计数器:对于每个不同的URL,它会报告输入中出现该URL的次数。
-5. 第二种排序按每行起始处的数字(-n)排序,这是URL的请求次数。然后逆序(-r)返回结果,大的数字在前。
-6. 最后,只输出前五行(-n 5),并丢弃其余的。该系列命令的输出如下所示:
+4. `uniq`命令通过检查两个相邻的行是否相同来过滤掉输入中的重复行。 `-c`则表示输出一个计数器:对于每个不同的URL,它会报告输入中出现该URL的次数。
+5. 第二种排序按每行起始处的数字(`-n`)排序,这是URL的请求次数。然后逆序(`-r`)返回结果,大的数字在前。
+6. 最后,只输出前五行(`-n 5`),并丢弃其余的。该系列命令的输出如下所示:
```
4189 /favicon.ico
@@ -89,13 +91,13 @@ cat /var/log/nginx/access.log | #1
915 /css/typography.css
```
-如果你不熟悉Unix工具,上面的命令行可能看起来有点吃力,但是它非常强大。它能在几秒钟内处理几GB的日志文件,并且您可以根据需要轻松修改命令。例如,如果要从报告中省略CSS文件,可以将awk参数更改为`'$7 !~ /\.css$/ {print $7}'`,如果想统计最多的客户端IP地址,可以把awk参数改为`'{print $1}'`等等。
+ 如果你不熟悉Unix工具,上面的命令行可能看起来有点吃力,但是它非常强大。它能在几秒钟内处理几GB的日志文件,并且您可以根据需要轻松修改命令。例如,如果要从报告中省略CSS文件,可以将awk参数更改为`'$7 !~ /\.css$/ {print $7}'`,如果想统计最多的客户端IP地址,可以把awk参数改为`'{print $1}'`等等。
-我们不会在这里详细探索Unix工具,但是它非常值得学习。令人惊讶的是,使用awk,sed,grep,sort,uniq和xargs的组合,可以在几分钟内完成许多数据分析,并且它们的性能相当的好[8]。
+ 我们不会在这里详细探索Unix工具,但是它非常值得学习。令人惊讶的是,使用awk,sed,grep,sort,uniq和xargs的组合,可以在几分钟内完成许多数据分析,并且它们的性能相当的好【8】。
#### 命令链与自定义程序
-抛开Unix命令链,你可以写一个简单的程序来做同样的事情。例如,在Ruby中,它可能看起来像这样:
+除了Unix命令链,你还可以写一个简单的程序来做同样的事情。例如在Ruby中,它可能看起来像这样:
```ruby
counts = Hash.new(0) # 1
@@ -110,40 +112,42 @@ top5 = counts.map{|url, count| [count, url] }.sort.reverse[0...5] # 4
top5.each{|count, url| puts "#{count} #{url}" } # 5
```
-1. `counts`是一个存储计数器次数的哈希表,初始情况下每个网址对应的计数为零。
-2. 我们读取日志的每一行,把获取第七个空格分隔的字段(这里的数组索引是6,因为Ruby的数组从0开始)对应的URL。
-3. 将日志当前行中URL的计数加一。
-4. 按计数器值(降序)对哈希表进行排序,并取前五位。
+1. `counts`是一个存储计数器的哈希表,保存了每个URL被浏览的次数,默认为0。
+2. 逐行读取日志,抽取每行第七个被空格分隔的字段为URL(这里的数组索引是6,因为Ruby的数组索引从0开始计数)
+3. 将日志当前行中URL对应的计数器值加一。
+4. 按计数器值(降序)对哈希表内容进行排序,并取前五位。
5. 打印出前五个条目。
-这个程序并不像Unix管道那样简练,但是它的可读性很强,对于这两种方式每个人有不同的偏好。但是,除了表面上的语法差异,两者执行流程也有很大差异,文件越大则越明显。
+这个程序并不像Unix管道那样简洁,但是它的可读性很强,喜欢哪一种属于口味的问题。但两者除了表面上的差异之外,执行流程也有很大差异,如果你在大文件上运行此分析,则会变得明显。
#### 排序 VS 内存中的聚合
-Ruby脚本在内存中保存着一个URL的哈希表,其中每个URL映射到它被统计的次数。 Unix流水线没有这样一个哈希表,而是依赖于对URL列表的排序,在这个URL列表中,同一个URL的只是简单的重复出现。
+ Ruby脚本在内存中保存了一个URL的哈希表,将每个URL映射到它出现的次数。 Unix管道没有这样的哈希表,而是依赖于对URL列表的排序,在这个URL列表中,同一个URL的只是简单地重复出现。
-哪种方法更好?这取决于你有多少个不同的URL。对于大多数中小型网站,您可能可以为所有不同网址提供一个计数器(假设我们使用1GB内存)。在此例中,作业的工作集(作业需要随机访问的内存大小)仅取决于不同URL的数量:如果单个URL有一百万个日志条目,则哈希中所需的空间表仍然只有一个URL加上一个计数器的大小。如果这个工作集足够小,那么内存哈希表工作正常,甚至在性能较差的笔记本电脑上也可以。
+ 哪种方法更好?这取决于你有多少个不同的URL。对于大多数中小型网站,您可能可以为所有不同网址提供一个计数器(假设我们使用1GB内存)。在此例中,作业的**工作集(working set)**(作业需要随机访问的内存大小)仅取决于不同URL的数量:如果日志中只有单个URL,重复出现一百万次,则散列表所需的空间表就只有一个URL加上一个计数器的大小。当工作集足够小时,内存散列表表现良好,甚至在性能较差的笔记本电脑上也可以正常工作。
-另一方面,如果作业的工作集大于可用内存,则排序方法的优点是可以高效地使用磁盘。这与我们在第74页的“[SSTables和LSM树]”中讨论过的原理是一样的:数据块可以在内存中排序并分段写入磁盘,然后多个有序的分段可以合并为一个更大的有序文件。归并排序具有在磁盘上运行良好的顺序访问模式。(请记住,在顺序I/O中进行优化是第3章中反复出现的主题,这里我们再次提到。)
+ 另一方面,如果作业的工作集大于可用内存,则排序方法的优点是可以高效地使用磁盘。这与我们在“[SSTables和LSM树](ch3.md#SSTables和LSM树)”中讨论过的原理是一样的:数据块可以在内存中排序并作为段文件写入磁盘,然后多个排序好的段可以合并为一个更大的排序文件。 归并排序具有在磁盘上运行良好的顺序访问模式。 (请记住,针对顺序I/O进行优化是[第3章](ch3.md)中反复出现的主题,相同的模式在此重现)
-Linux GNU 核心工具(Coreutils)中的排序自动把大于可用内存的数据集在磁盘进行分割,并且通过CPU的多核并行排序【9】。这意味着之前简单的Unix命令链很容易扩展到大数据集,并且不会耗尽内存。瓶颈更可能是从磁盘读取输入文件的速度。
+ GNU Coreutils(Linux)中的`sort `程序通过溢出至磁盘的方式来自动应对大于内存的数据集,并能同时使用多个CPU核进行并行排序【9】。这意味着我们之前看到的简单的Unix命令链很容易扩展到大数据集,且不会耗尽内存。瓶颈可能是从磁盘读取输入文件的速度。
### Unix哲学
-我们可以非常容易地使用前一个例子中的命令链来分析日志文件,这并非巧合:这实际上是Unix的关键设计思想之一,而且它今天依然令人惊讶。让我们更深入地进行研究,这样我们可以从Unix中借鉴一些想法【10】。
+ 我们可以非常容易地使用前一个例子中的命令链来分析日志文件,这并非巧合:这实际上是Unix的关键设计思想之一,而且它今天依然令人惊讶。让我们更深入地进行研究,这样我们可以从Unix中借鉴一些想法【10】。
-Unix管道的发明者道格·麦克罗伊(Doug McIlroy)在1964年首先做出了描述【11】:“我们需要一种像花园软管一样的连接程序的方法 - 可以把数据通过拧口传输到不同的片段中。这也是I/O的实现方式”。通过管道连接程序的想法成为了Unix的哲学的一部分,Unix哲学成为在开发人员和用户之间流行的设计准则。1978年, Unix哲学得到具体描述【12,13】:
+ Unix管道的发明者道格·麦克罗伊(Doug McIlroy)在1964年首先做出了描述【11】:“我们需要一种像花园软管一样的连接程序的方法 - 可以把数据通过拧口传输到不同的片段中。这也是I/O的实现方式”。通过管道连接程序的想法成为了Unix的哲学的一部分,Unix哲学成为在开发人员和用户之间流行的设计准则。1978年, Unix哲学得到具体描述【12,13】:
1. 让每个程序都做好一件事。如果有新的任务,重新建立一个新的程序,而不是在原有程序上增加“功能”使其复杂化。
2. 每个程序的输出应该成为另一个程序的输入,而不是未知程序。不要将输出与无关的信息混淆。避免使用严格的柱状或二进制输入格式。不要坚持交互式输入。
3. 设计和构建软件,甚至是操作系统时要尽早尝试,最好在几周内完成。毫不犹犹豫扔掉笨拙的部分并重建它们。
4. 优先使用工具而不是拙劣的帮助来减轻编程任务,即使您必须花费额外经历开发工具,并且其中一些工具可能今后再也不会用到。
-这种方式 - 自动化,快速原型设计,迭代式迭代,对实验友好,将大型项目分解成可管理的模块 - 听起来非常像今天的敏捷和DevOps概念。令人惊奇的是这个理念四十年来变化不大。
-排序工具是一个“做好一件事”很好的例子。它可以说比大多数编程语言的标准库(不会分割到磁盘并且不使用多线程,即使这么做有好处)中的实现更好。然而,排序工具几乎不会单独使用。它只能与其他Unix工具(如`uniq`)结合使用。
+
+ 这种方式 —— 自动化,快速原型设计,迭代式迭代,对实验友好,将大型项目分解成可管理的模块 —— 听起来非常像今天的敏捷和DevOps概念。令人惊奇的是这个理念四十年来变化不大。
+
+ 排序工具是一个“做好一件事”很好的例子。它可以说比大多数编程语言的标准库(不会分割到磁盘并且不使用多线程,即使这么做有好处)中的实现更好。然而,排序工具几乎不会单独使用。它只能与其他Unix工具(如`uniq`)结合使用。
像bash这样的Unix shell可以让我们轻松地将这些小程序组合成令人惊讶的强大数据处理作业。尽管这些程序有很多是由不同人群编写的,但它们可以灵活地组合在一起。 Unix如何实现这种可组合性?
@@ -188,7 +192,7 @@ Unix工具如此成功的部分原因是它们使得查看正在发生的事情
- Unix命令的输入文件通常被视为不可变的。这意味着您可以随意运行命令,尝试各种命令行选项,而不会损坏输入文件。
- 您可以在任何时候结束管道,将输出管道输送到`less`,然后查找它是否具有预期的数据。这种检查能力对调试非常有用。
-
+
- 您可以将一个流水线阶段的输出写入文件,并将该文件用作下一阶段的输入。这使您可以重新启动后面的阶段,而无需重新运行整个管道。
因此,与关系数据库的查询优化器相比,即使Unix工具非常直接和简单,仍然非常有用,特别是对于调试来说。
@@ -268,17 +272,17 @@ Reducer通过一个迭代器对相同的主键进行逐步扫描(在某些情
#### MapReduce工作流程
-单个MapReduce作业可以解决的问题范围有限。请参阅日志分析案例,一个MapReduce作业可以确定每个URL的页面浏览次数,但无法确定访问最多的URL,因为这需要第二轮排序。
+ 单个MapReduce作业可以解决的问题范围有限。请参阅日志分析案例,一个MapReduce作业可以确定每个URL的页面浏览次数,但无法确定访问最多的URL,因为这需要第二轮排序。
-因此,将MapReduce作业链到工作流中非常常见,例如,一个作业的输出成为下一个作业的输入。 Hadoop Map-Reduce框架对工作流没有特别的支持,所以这个链接是通过目录名隐含完成的:第一个作业必须通过配置将其输出写入到HDFS中的指定目录,第二个作业必须是读取这个目录。从MapReduce框架的角度来看,他们是两个独立的工作。
+ 因此,将MapReduce作业链到工作流中非常常见,例如,一个作业的输出成为下一个作业的输入。 Hadoop Map-Reduce框架对工作流没有特别的支持,所以这个链接是通过目录名隐含完成的:第一个作业必须通过配置将其输出写入到HDFS中的指定目录,第二个作业必须是读取这个目录。从MapReduce框架的角度来看,他们是两个独立的工作。
-因此,被链接的MapReduce作业和Unix命令的流水线(它直接将一个进程的输出作为输入传递给另一个进程,只使用一个小的内存缓冲区)不太一样,它更像是一个命令序列,其中每个命令的输出会写入临时文件,下一个命令从临时文件中读取。这种设计有利有弊,我们将在第419页“中间状态的具体化”中讨论。
+ 因此,被链接的MapReduce作业和Unix命令的流水线(它直接将一个进程的输出作为输入传递给另一个进程,只使用一个小的内存缓冲区)不太一样,它更像是一个命令序列,其中每个命令的输出会写入临时文件,下一个命令从临时文件中读取。这种设计有利有弊,我们将在第419页“中间状态的具体化”中讨论。
-只有当作业成功完成时,批处理作业的输出才被视为有效(MapReduce丢弃失败作业产生的部分输出)。因此,工作流中的一项工作只有在先前的工作 - 即产生它所需的输入工作成功完成时才能开始。为了处理这些作业之间的依赖关系执行,基于Hadoop开发了各种工作流调度器,包括Oozie,Azkaban,Luigi,Airflow和Pinball [28]。
+ 只有当作业成功完成时,批处理作业的输出才被视为有效(MapReduce丢弃失败作业产生的部分输出)。因此,工作流中的一项工作只有在先前的工作 - 即产生它所需的输入工作成功完成时才能开始。为了处理这些作业之间的依赖关系执行,基于Hadoop开发了各种工作流调度器,包括Oozie,Azkaban,Luigi,Airflow和Pinball [28]。
-这些调度程序还具有管理功能,在维护大量批处理作业时非常有用。在构建推荐系统[29]时,由50到100个MapReduce作业组成的工作流非常常见,而在大型组织中,不同的团队可能运行不同的作业来读取彼此的输出。工具支持对于管理这样复杂的数据流非常重要。
+ 这些调度程序还具有管理功能,在维护大量批处理作业时非常有用。在构建推荐系统[29]时,由50到100个MapReduce作业组成的工作流非常常见,而在大型组织中,不同的团队可能运行不同的作业来读取彼此的输出。工具支持对于管理这样复杂的数据流非常重要。
-Hadoop的各种高级工具(如Pig [30],Hive [31],Cascading [32],Crunch [33]和FlumeJava [34])也设置了多个MapReduce阶段的工作流程并且将他们自动连接在一起。
+ Hadoop的各种高级工具(如Pig [30],Hive [31],Cascading [32],Crunch [33]和FlumeJava [34])也设置了多个MapReduce阶段的工作流程并且将他们自动连接在一起。
### Reduce端连接(join)与分组(group)
@@ -492,7 +496,7 @@ MPP数据库是单一的,紧密集成的软件,负责磁盘上的存储布
MapReduce使工程师能够轻松地在大型数据集上运行自己的代码。如果你有HDFS和MapReduce,那么你可以在它上面建立一个SQL查询执行引擎,事实上这正是Hive项目所做的[31]。而且对于不适合用SQL查询表示的批处理,也可以自己实现处理方式。
-随后,人们发现MapReduce对于某些类型的处理来说太过于限制,表现得很差,基于Hadoop开发了其他各种处理模型(我们将在第419页的“Beyond MapReduce”中看到其中的一些)。仅仅两种处理模型,SQL和MapReduce是不够的,我们需要更多不同的模型!而且由于Hadoop平台的开放性,实施一整套方法是可行的,而这在整体MPP数据库的范围内是不可能的[58]。
+随后,人们发现MapReduce对于某些类型的处理来说太过于限制,表现得很差,基于Hadoop开发了其他各种处理模型(我们将在第419页的“[超越MapReduce]()”中看到其中的一些)。仅仅两种处理模型,SQL和MapReduce是不够的,我们需要更多不同的模型!而且由于Hadoop平台的开放性,实施一整套方法是可行的,而这在整体MPP数据库的范围内是不可能的【58】。
至关重要的是,这些不同的处理模型都可以在一个共享用途的集群上运行,所有这些机器都可以访问分布式文件系统上的相同文件。在Hadoop方法中,不需要将数据导入到几个不同的专用系统中进行不同类型的处理:系统足够灵活,可以支持同一个群集内不同的工作负载。不需要移动数据使得从数据中取值变得容易得多,并且在新的处理模型进行实验更加容易。
diff --git a/ch11.md b/ch11.md
index ffdffff..d6ce997 100644
--- a/ch11.md
+++ b/ch11.md
@@ -10,54 +10,54 @@
[TOC]
-在[第10章](ch10.md)中,我们讨论了批处理技术,它将一组文件读取为输入并生成一组新的输出文件。输出是派生数据的一种形式;也就是说,如果需要,可以通过再次运行批处理过程来重新创建数据集。我们看到了如何使用这个简单而强大的想法来创建搜索索引,推荐系统,分析等等。
+ 在[第10章](ch10.md)中,我们讨论了批处理技术,它将一组文件读取为输入并生成一组新的输出文件。输出是**衍生数据(derived data)**的一种形式;也就是说,如果需要,可以通过再次运行批处理过程来重新创建数据集。我们看到了如何使用这个简单而强大的想法来建立搜索索引,推荐系统,做分析等等。
-然而,在[第10章](ch10.md)中仍然有一个大的假设:即输入是有界的,即已知和有限的大小,所以批处理知道它何时完成了它的输入。例如,MapReduce中心的排序操作必须读取其全部输入,然后才能开始生成输出:可能发生最后一个输入记录是具有最低键的输入记录,因此必须是第一个输出记录,所以提前开始输出不是一种选择。
+ 然而,在[第10章](ch10.md)中仍然有一个很大的假设:即输入是有界的,即已知和有限的大小,所以批处理知道它何时完成了它的输入。例如,MapReduce核心的排序操作必须读取其全部输入,然后才能开始生成输出:可能发生这种情况:最后一条输入记录具有最小的键,因此需要第一个被输出,所以提早开始输出是不可行的。
-实际上,很多数据是无限的,因为它随着时间的推移逐渐到达:你的用户昨天和今天产生了数据,明天他们将继续产生更多的数据。除非您停业,否则这个过程永远都不会结束,所以数据集从来就不会以任何有意义的方式“完成”【1】。因此,批处理程序必须将数据人为地分成固定时间段的数据块,例如,在每天结束时处理一天的数据,或者在每小时结束时处理一小时的数据。
+ 实际上,很多数据是无限的,因为它随着时间的推移而逐渐到达:你的用户昨天和今天产生了数据,明天他们将继续产生更多的数据。除非你停业,否则这个过程永远都不会结束,所以数据集从来就不会以任何有意义的方式“完成”【1】。因此,批处理程序必须将数据人为地分成固定时间段的数据块,例如,在每天结束时处理一天的数据,或者在每小时结束时处理一小时的数据。
-日常批处理过程中的问题是,输入的更改只会在一天之后的输出中反映出来,这对于许多急躁的用户来说太慢了。为了减少延迟,我们可以更频繁地运行处理 - 比如说,在每秒钟的末尾,甚至连续地处理秒数的数据,完全放弃固定的时间片,并简单地处理每一个事件。这是流处理背后的想法。
+ 日常批处理中的问题是,输入的更改只会在一天之后的输出中反映出来,这对于许多急躁的用户来说太慢了。为了减少延迟,我们可以更频繁地运行处理 —— 比如说,在每秒钟的末尾 —— 或者甚至更连续一些,完全抛开固定的时间切片,当事件发生时就立即进行处理,这就是**流处理(stream processing)**背后的想法。
-一般来说,“流”是指随着时间的推移逐渐可用的数据。这个概念出现在很多地方:Unix的stdin和stdout,编程语言(lazy lists)【2】,文件系统API(如Java的FileInputStream),TCP连接,通过互联网传送音频和视频等等。
-在本章中,我们将把事件流视为一个数据管理机制:我们在上一章中看到的批量数据的无界的,递增处理的对应物。我们将首先讨论流如何被表示,存储和通过网络传输。在“[数据库和流](#数据库和流)”中,我们将研究流和数据库之间的关系。最后在“[流处理](#流处理)”中,我们将探索连续处理这些流的方法和工具,以及它们可以用来构建应用程序的方法。
+ 一般来说,“流”是指随着时间的推移逐渐可用的数据。这个概念出现在很多地方:Unix的stdin和stdout,编程语言(lazy lists)【2】,文件系统API(如Java的FileInputStream),TCP连接,通过互联网传送音频和视频等等。
+ 在本章中,我们将把**事件流(event stream)**视为一种数据管理机制:无界限,增量处理,与[上一章](ch10.md)中批量数据相对应的。我们将首先讨论怎样表示、存储、通过网络传输流。在“[数据库和流](#数据库和流)”中,我们将研究流和数据库之间的关系。最后在“[流处理](#流处理)”中,我们将研究**连续处理**这些流的方法和工具,以及它们用于应用构建的方式。
## 传递事件流
-在批处理领域,作业的输入和输出是文件(也许在分布式文件系统上)。什么是类似的流媒体?
+ 在批处理领域,作业的输入和输出是文件(也许在分布式文件系统上)。什么是类似的流媒体?
-当输入是一个文件(一个字节序列)时,第一个处理步骤通常是将其解析为一系列记录。在流处理的上下文中,记录通常被称为事件,但它本质上是一样的:一个小的,自包含的,不可变的对象,包含某个时间点发生的事情的细节。一个事件通常包含一个时间戳,指示何时根据时钟来发生(参见“[单调钟与时钟](ch8.md#单调钟与时钟)”)。
+ 当输入是一个文件(一个字节序列)时,第一个处理步骤通常是将其解析为一系列记录。在流处理的上下文中,记录通常被称为事件,但它本质上是一样的:一个小的,自包含的,不可变的对象,包含某个时间点发生的事情的细节。一个事件通常包含一个时间戳,指示何时根据时钟来发生(参见“[单调钟与时钟](ch8.md#单调钟与时钟)”)。
-例如,发生的事情可能是用户采取的行动,例如查看页面或进行购买。它也可能来源于机器,例如来自温度传感器的周期性测量或者CPU利用率度量。在“[使用Unix工具进行批处理](ch10.md#使用Unix工具进行批处理)”的示例中,Web服务器日志的每一行都是一个事件。
+ 例如,发生的事情可能是用户采取的行动,例如查看页面或进行购买。它也可能来源于机器,例如来自温度传感器的周期性测量或者CPU利用率度量。在“[使用Unix工具进行批处理](ch10.md#使用Unix工具进行批处理)”的示例中,Web服务器日志的每一行都是一个事件。
-事件可能被编码为文本字符串或JSON,或者以某种二进制形式编码,如第4章所述。这种编码允许您存储一个事件,例如将其附加到一个文件,将其插入关系表,或将其写入文档数据库。它还允许您通过网络将事件发送到另一个节点以进行处理。
+ 事件可能被编码为文本字符串或JSON,或者以某种二进制形式编码,如第4章所述。这种编码允许您存储一个事件,例如将其附加到一个文件,将其插入关系表,或将其写入文档数据库。它还允许您通过网络将事件发送到另一个节点以进行处理。
-在批处理领域,作业的输入和输出是文件(也许在分布式文件系统上)。什么是类似的流媒体?
+ 在批处理领域,作业的输入和输出是文件(也许在分布式文件系统上)。什么是类似的流媒体?
-当输入是一个文件(一个字节序列)时,第一个处理步骤通常是将其解析为一系列记录。在流处理的上下文中,记录通常被称为事件,但它本质上是一样的:一个小的,自包含的,不可变的对象,包含某个时间点发生的事情的细节。一个事件通常包含一个时间戳,指示何时根据时钟来发生(参见“[单调钟与时钟](ch8.md#单调钟与时钟)”)。
+ 当输入是一个文件(一个字节序列)时,第一个处理步骤通常是将其解析为一系列记录。在流处理的上下文中,记录通常被称为事件,但它本质上是一样的:一个小的,自包含的,不可变的对象,包含某个时间点发生的事情的细节。一个事件通常包含一个时间戳,指示何时根据时钟来发生(参见“[单调钟与时钟](ch8.md#单调钟与时钟)”)。
-例如,发生的事情可能是用户采取的行动,例如查看页面或进行购买。它也可能来源于机器,例如来自温度传感器的周期性测量或者CPU利用率度量。在“[使用Unix工具进行批处理](ch10.md#使用Unix工具进行批处理)”的示例中,Web服务器日志的每一行都是一个事件。
+ 例如,发生的事情可能是用户采取的行动,例如查看页面或进行购买。它也可能来源于机器,例如来自温度传感器的周期性测量或者CPU利用率度量。在“[使用Unix工具进行批处理](ch10.md#使用Unix工具进行批处理)”的示例中,Web服务器日志的每一行都是一个事件。
-事件可能被编码为文本字符串或JSON,或者以某种二进制形式编码,如[第4章](ch4.md)所述。这种编码允许您存储一个事件,例如将其附加到一个文件,将其插入关系表,或将其写入文档数据库。它还允许您通过网络将事件发送到另一个节点以进行处理。
+ 事件可能被编码为文本字符串或JSON,或者以某种二进制形式编码,如[第4章](ch4.md)所述。这种编码允许您存储一个事件,例如将其附加到一个文件,将其插入关系表,或将其写入文档数据库。它还允许您通过网络将事件发送到另一个节点以进行处理。
-在批处理中,文件被写入一次,然后可能被多个作业读取。类似地,在流媒体术语中,一个事件由生产者(也称为发布者或发送者)生成一次,然后由多个消费者(订阅者或接收者)进行处理【3】。在文件系统中,文件名标识一组相关记录;在流媒体系统中,相关的事件通常被组合成一个主题或流。
+ 在批处理中,文件被写入一次,然后可能被多个作业读取。类似地,在流媒体术语中,一个事件由生产者(也称为发布者或发送者)生成一次,然后由多个消费者(订阅者或接收者)进行处理【3】。在文件系统中,文件名标识一组相关记录;在流媒体系统中,相关的事件通常被组合成一个主题或流。
-原则上,文件或数据库足以连接生产者和消费者:生产者将其生成的每个事件写入数据存储,并且每个使用者定期轮询数据存储以检查自上次运行以来出现的事件。这实际上是批处理在每天结束时处理一天的数据的过程。
+ 原则上,文件或数据库足以连接生产者和消费者:生产者将其生成的每个事件写入数据存储,并且每个使用者定期轮询数据存储以检查自上次运行以来出现的事件。这实际上是批处理在每天结束时处理一天的数据的过程。
-但是,如果数据存储不是为这种用途而设计的,那么在延迟较小的情况下继续进行处理时,轮询将变得非常昂贵。您调查的次数越多,返回新事件的请求百分比越低,因此开销越高。相反,当新事件出现时,最好通知消费者。
+ 但是,如果数据存储不是为这种用途而设计的,那么在延迟较小的情况下继续进行处理时,轮询将变得非常昂贵。您调查的次数越多,返回新事件的请求百分比越低,因此开销越高。相反,当新事件出现时,最好通知消费者。
-数据库传统上不太支持这种通知机制:关系数据库通常具有触发器,它们可以对变化作出反应(例如,将一行插入到表中),但是它们的功能非常有限,有点在数据库设计中是事后的【4,5】。相反,已经开发了专门的工具来提供事件通知。
+ 数据库传统上不太支持这种通知机制:关系数据库通常具有触发器,它们可以对变化作出反应(例如,将一行插入到表中),但是它们的功能非常有限,有点在数据库设计中是事后的【4,5】。相反,已经开发了专门的工具来提供事件通知。
### 消息系统
-通知消费者有关新事件的常用方法是使用消息传递系统:生产者发送包含事件的消息,然后将消息推送给消费者。我们之前在“[消息传递中的数据流](ch4.md#消息传递中的数据流)”中介绍了这些系统,但现在我们将详细介绍这些系统。
+ 通知消费者有关新事件的常用方法是使用消息传递系统:生产者发送包含事件的消息,然后将消息推送给消费者。我们之前在“[消息传递中的数据流](ch4.md#消息传递中的数据流)”中介绍了这些系统,但现在我们将详细介绍这些系统。
-像生产者和消费者之间的Unix管道或TCP连接这样的直接通信渠道将是实现消息传递系统的简单方法。但是,大多数消息传递系统都在这个基本模型上扩展特别是,Unix管道和TCP将一个发送者与一个接收者完全连接,而一个消息传递系统允许多个生产者节点将消息发送到相同的主题,并允许多个消费者节点接收主题中的消息。
+ 像生产者和消费者之间的Unix管道或TCP连接这样的直接通信渠道将是实现消息传递系统的简单方法。但是,大多数消息传递系统都在这个基本模型上扩展特别是,Unix管道和TCP将一个发送者与一个接收者完全连接,而一个消息传递系统允许多个生产者节点将消息发送到相同的主题,并允许多个消费者节点接收主题中的消息。
-在这个发布/订阅模式中,不同的系统采取多种方法,对于所有目的都没有一个正确的答案。为了区分这些系统,询问以下两个问题特别有帮助:
+ 在这个发布/订阅模式中,不同的系统采取多种方法,对于所有目的都没有一个正确的答案。为了区分这些系统,询问以下两个问题特别有帮助:
1. 如果生产者发送消息的速度比消费者能够处理的速度快,会发生什么?一般来说,有三种选择:系统可以放置消息,缓冲队列中的消息或应用背压(也称为流量控制;即阻止生产者发送更多的消息)。例如,Unix管道和TCP使用背压:他们有一个小的固定大小的缓冲区,如果填满,发件人被阻塞,直到收件人从缓冲区中取出数据(参见“[网络拥塞和排队](ch8.md#网络拥塞和排队)”)。
@@ -67,7 +67,7 @@
消息丢失是否可以接受取决于应用程序。例如,对于定期传输的传感器读数和指标,偶尔丢失的数据点可能并不重要,因为更新后的值将在短时间后发送。但是,要注意,如果大量的消息被丢弃,那么衡量标准是不正确的【7】。如果您正在计数事件,那么更重要的是它们可靠地传送,因为每个丢失的消息都意味着不正确的计数器。
-我们在第10章中探讨的批处理系统的一个很好的特性是它们提供了强大的可靠性保证:失败的任务会自动重试,失败任务的部分输出会自动丢弃。这意味着输出与没有发生故障一样,这有助于简化编程模型。在本章的后面,我们将研究如何在流式上下文中提供类似的保证。
+ 我们在[第10章](ch10.md)中探讨的批处理系统的一个很好的特性是它们提供了强大的可靠性保证:失败的任务会自动重试,失败任务的部分输出会自动丢弃。这意味着输出与没有发生故障一样,这有助于简化编程模型。在本章的后面,我们将研究如何在流式上下文中提供类似的保证。
#### 直接从生产者传递给消费者
@@ -83,22 +83,22 @@
尽管这些直接消息传递系统在设计它们的情况下运行良好,但是它们通常要求应用程序代码知道消息丢失的可能性。他们可以容忍的错误是相当有限的:即使协议检测并重新传输在网络中丢失的数据包,他们通常假设生产者和消费者不断在线。
-如果消费者处于脱机状态,则可能会丢失在无法访问时发送的消息。一些协议允许生产者重试失败的消息传递,但是如果生产者崩溃,这种方法可能会失效,失去了它应该重试的消息的缓冲区。
+ 如果消费者处于脱机状态,则可能会丢失在无法访问时发送的消息。一些协议允许生产者重试失败的消息传递,但是如果生产者崩溃,这种方法可能会失效,失去了它应该重试的消息的缓冲区。
#### 消息代理
-一个广泛使用的替代方法是通过消息代理(也称为消息队列)发送消息,消息代理实质上是一种针对处理消息流而优化的数据库。它作为服务器运行,生产者和消费者作为客户连接到服务器。生产者将消息写入经纪人,消费者通过从经纪人那里读取消息来接收他们。
+ 一个广泛使用的替代方法是通过消息代理(也称为消息队列)发送消息,消息代理实质上是一种针对处理消息流而优化的数据库。它作为服务器运行,生产者和消费者作为客户连接到服务器。生产者将消息写入代理,消费者通过从代理那里读取消息来接收他们。
-通过将数据集中在代理中,这些系统可以更容易地容忍来来去去的客户端(连接,断开连接和崩溃),而耐久性问题则转移到代理商。一些消息代理只将消息保存在内存中,而另一些消息代理(取决于配置)将其写入磁盘,以便在代理崩溃的情况下不会丢失。面对缓慢的消费者,他们通常会允许排除异常的排队(而不是丢弃消息或背压),尽管这种选择也可能取决于配置。
+ 通过将数据集中在代理中,这些系统可以更容易地容忍来来去去的客户端(连接,断开连接和崩溃),而耐久性问题则转移到代理商。一些消息代理只将消息保存在内存中,而另一些消息代理(取决于配置)将其写入磁盘,以便在代理崩溃的情况下不会丢失。面对缓慢的消费者,他们通常会允许排除异常的排队(而不是丢弃消息或背压),尽管这种选择也可能取决于配置。
-排队的结果也是消费者通常是异步的:当生产者发送消息时,通常只等待代理确认已经缓存消息,不等待消息被消费者处理。向消费者的交付将发生在未来某个未定的时间点 - 通常在几分之一秒之内,但有时在队列积压之后显着延迟。
+ 排队的结果也是消费者通常是异步的:当生产者发送消息时,通常只等待代理确认已经缓存消息,不等待消息被消费者处理。向消费者的交付将发生在未来某个未定的时间点 - 通常在几分之一秒之内,但有时在队列积压之后显着延迟。
#### 消息代理与数据库进行比较
-有些消息代理甚至可以使用XA或JTA参与两阶段提交协议(参阅“[实践中的分布式事务](ch9.md#实践中的分布式事务)”)。这个功能与数据库在本质上非常相似,尽管消息代理和数据库之间仍存在重要的实际差异:
+ 有些消息代理甚至可以使用XA或JTA参与两阶段提交协议(参阅“[实践中的分布式事务](ch9.md#实践中的分布式事务)”)。这个功能与数据库在本质上非常相似,尽管消息代理和数据库之间仍存在重要的实际差异:
* 数据库通常保留数据,直到被明确删除,而大多数消息代理在消息成功传递给消费者时自动删除消息。这样的消息代理不适合长期的数据存储。
-* 由于他们很快删除了邮件,大多数邮件经纪人都认为他们的工作集合相当小,即队列很短。如果代理需要缓冲很多消息,因为消费者速度较慢(如果消息不再适合内存,则可能会将消息泄漏到磁盘),每个消息需要更长的时间处理,整体吞吐量可能会降低【6】。
+* 由于他们很快删除了邮件,大多数邮件代理都认为他们的工作集合相当小,即队列很短。如果代理需要缓冲很多消息,因为消费者速度较慢(如果消息不再适合内存,则可能会将消息泄漏到磁盘),每个消息需要更长的时间处理,整体吞吐量可能会降低【6】。
* 数据库通常支持二级索引和各种搜索数据的方式,而消息代理通常支持某种方式订阅匹配某种模式的主题子集。机制是不同的,但是这两种方式都是客户选择想要了解的数据部分的根本途径。
* 查询数据库时,结果通常基于数据的时间点快照;如果另一个客户端随后向数据库写入更改查询结果的内容,则第一个客户端不会发现其先前的结果现在已过期(除非它重复查询或轮询更改)。相比之下,消息代理不支持任意查询,但是当数据发生变化时(即新消息可用时),它们会通知客户端。
@@ -110,11 +110,11 @@
***负载均衡***
-每条消息都被传递给其中一个消费者,所以消费者可以共享处理消息的主题。经纪人可以任意分配消息给消费者。当处理消息的代价很高时,此模式非常有用,因此您希望能够添加消费者来并行处理消息。 (在AMQP中,可以通过让多个客户端从同一个队列中消费来实现负载均衡,而在JMS中则称为共享订阅)。
+ 每条消息都被传递给其中一个消费者,所以消费者可以共享处理消息的主题。代理可以任意分配消息给消费者。当处理消息的代价很高时,此模式非常有用,因此您希望能够添加消费者来并行处理消息。 (在AMQP中,可以通过让多个客户端从同一个队列中消费来实现负载均衡,而在JMS中则称为共享订阅)。
***扇出***
-每条消息都被传递给所有的消费者。扇出允许几个独立的消费者每个“收听”相同的消息广播,而不会相互影响 - 流式等同于具有读取相同输入文件的多个不同批处理作业。 (此功能由JMS中的主题订阅提供,并在AMQP中交换绑定。)
+ 每条消息都被传递给所有的消费者。扇出允许几个独立的消费者每个“收听”相同的消息广播,而不会相互影响 —— 流式等同于具有读取相同输入文件的多个不同批处理作业。 (此功能由JMS中的主题订阅提供,并在AMQP中交换绑定。)

@@ -124,51 +124,51 @@
#### 确认和重新交付
-消费者可能会随时崩溃,所以可能发生的情况是经纪人向消费者提供消息,但消费者从不处理消费者,或者在消费者崩溃之前只处理消费者。为了确保消息不会丢失,消息代理使用确认:客户端必须明确告诉代理处理消息的时间,以便代理可以将其从队列中移除。
+ 消费者可能会随时崩溃,所以可能发生的情况是代理向消费者提供消息,但消费者从不处理消费者,或者在消费者崩溃之前只处理消费者。为了确保消息不会丢失,消息代理使用确认:客户端必须明确告诉代理处理消息的时间,以便代理可以将其从队列中移除。
-如果与客户端的连接关闭或超时而没有代理收到确认,则认为消息未处理,因此它将消息再次传递给另一个消费者。 (请注意,可能发生这样的消息实际上是完全处理的,但网络中的确认丢失了,处理这种情况需要一个原子提交协议,正如在“[实践中的分布式事务](ch9.md#实践中的分布式事务)”中所讨论的那样)
+ 如果与客户端的连接关闭或超时而没有代理收到确认,则认为消息未处理,因此它将消息再次传递给另一个消费者。 (请注意,可能发生这样的消息实际上是完全处理的,但网络中的确认丢失了,处理这种情况需要一个原子提交协议,正如在“[实践中的分布式事务](ch9.md#实践中的分布式事务)”中所讨论的那样)
-当与负载平衡相结合时,这种重新传递行为对消息的排序有一个有趣的影响。在[图11-2](img/fig11-2.png)中,消费者通常按照生产者发送的顺序处理消息。然而,消费者2在处理消息m3时崩溃,与此同时消费者1正在处理消息m4。未确认的消息m3随后被重新发送给消费者1,结果消费者1按照m4,m3,m5的顺序处理消息。因此,m3和m4不是以与生产者1发送的顺序相同的顺序交付的。
+ 当与负载平衡相结合时,这种重新传递行为对消息的排序有一个有趣的影响。在[图11-2](img/fig11-2.png)中,消费者通常按照生产者发送的顺序处理消息。然而,消费者2在处理消息m3时崩溃,与此同时消费者1正在处理消息m4。未确认的消息m3随后被重新发送给消费者1,结果消费者1按照m4,m3,m5的顺序处理消息。因此,m3和m4不是以与生产者1发送的顺序相同的顺序交付的。

**图11-2 消费者2在处理m3时崩溃,因此稍后再次向消费者1递送**
-即使消息代理试图保留消息的顺序(如JMS和AMQP标准所要求的),负载平衡与重新传递的组合也不可避免地导致消息被重新排序。为避免此问题,您可以为每个使用者使用单独的队列(即不使用负载均衡功能)。如果消息是完全独立的,消息重新排序并不是一个问题,但是如果消息之间存在因果依赖关系,这一点很重要,我们将在后面的章节中看到。
+ 即使消息代理试图保留消息的顺序(如JMS和AMQP标准所要求的),负载平衡与重新传递的组合也不可避免地导致消息被重新排序。为避免此问题,您可以为每个使用者使用单独的队列(即不使用负载均衡功能)。如果消息是完全独立的,消息重新排序并不是一个问题,但是如果消息之间存在因果依赖关系,这一点很重要,我们将在后面的章节中看到。
### 分区日志
-通过网络发送数据包或向网络服务发送请求通常是暂时的操作,不会留下永久的痕迹。尽管可以永久记录(使用数据包捕获和日志记录),但我们通常不这么想。即使是信息经纪人,他们将信息持久地写入磁盘,在被传递给消费者之后,很快就会将其删除,因为它们是以短暂的消息传递思想为基础构建的。
+ 通过网络发送数据包或向网络服务发送请求通常是暂时的操作,不会留下永久的痕迹。尽管可以永久记录(使用数据包捕获和日志记录),但我们通常不这么想。即使是信息代理,他们将信息持久地写入磁盘,在被传递给消费者之后,很快就会将其删除,因为它们是以短暂的消息传递思想为基础构建的。
-数据库和文件系统采用相反的方法:写入数据库或文件的所有内容通常都要被永久记录下来,至少在某些人明确选择将其删除之前。
+ 数据库和文件系统采用相反的方法:写入数据库或文件的所有内容通常都要被永久记录下来,至少在某些人明确选择将其删除之前。
-思维方式上的这种差异对如何创建派生数据有很大的影响。批处理过程的一个关键特征是,你可以反复运行它们,试验处理步骤,没有损坏输入的风险(因为输入是只读的)。 AMQP/JMS风格的消息并非如此:如果确认导致从代理中删除消息,则接收消息具有破坏性,因此您不能再次运行同一消费者,并期望得到相同的结果。
+ 思维方式上的这种差异对如何创建派生数据有很大的影响。批处理过程的一个关键特征是,你可以反复运行它们,试验处理步骤,没有损坏输入的风险(因为输入是只读的)。 AMQP/JMS风格的消息并非如此:如果确认导致从代理中删除消息,则接收消息具有破坏性,因此您不能再次运行同一消费者,并期望得到相同的结果。
-如果您将新消费者添加到消息系统,则通常只会开始接收在注册后发送的消息;任何先前的消息已经消失,无法恢复。将它与文件和数据库进行对比,您可以随时添加新客户端,并且可以读取过去任意写入的数据(只要应用程序没有明确覆盖或删除数据)。
+ 如果您将新消费者添加到消息系统,则通常只会开始接收在注册后发送的消息;任何先前的消息已经消失,无法恢复。将它与文件和数据库进行对比,您可以随时添加新客户端,并且可以读取过去任意写入的数据(只要应用程序没有明确覆盖或删除数据)。
-为什么我们不能混合使用数据库的持久存储方式和低延迟的消息通知功能呢?这是基于日志消息代理的思想。
+ 为什么我们不能混合使用数据库的持久存储方式和低延迟的消息通知功能呢?这是基于日志消息代理的思想。
#### 使用日志进行消息存储
-日志只是磁盘上只能追加记录的序列。我们先前在第3章中的日志结构存储引擎和预写日志的上下文中讨论了日志,在[第5章](ch5.md)中也讨论了复制的上下文中。
+ 日志只是磁盘上只能追加记录的序列。我们先前在第3章中的日志结构存储引擎和预写日志的上下文中讨论了日志,在[第5章](ch5.md)中也讨论了复制的上下文中。
-可以使用相同的结构来实现消息代理:生产者通过将消息附加到日志的末尾来发送消息,并且消费者通过依次读取日志来接收消息。如果消费者到达日志的末尾,则等待通知新消息已被追加。 Unix工具`tail -f`监视数据被附加的文件,基本上是这样工作的。
+ 可以使用相同的结构来实现消息代理:生产者通过将消息附加到日志的末尾来发送消息,并且消费者通过依次读取日志来接收消息。如果消费者到达日志的末尾,则等待通知新消息已被追加。 Unix工具`tail -f`监视数据被附加的文件,基本上是这样工作的。
-为了扩展到比单个磁盘提供更高的吞吐量,可以对日志进行分区(在[第6章](ch6.md)的意义上)。然后可以在不同的机器上托管不同的分区,使每个分区成为一个单独的日志,可以独立于其他分区读取和写入。然后可以将一个话题定义为一组分段,它们都携带相同类型的消息。这种方法如[图11-3](img/fig11-3.png)所示。
+ 为了扩展到比单个磁盘提供更高的吞吐量,可以对日志进行分区(在[第6章](ch6.md)的意义上)。然后可以在不同的机器上托管不同的分区,使每个分区成为一个单独的日志,可以独立于其他分区读取和写入。然后可以将一个话题定义为一组分段,它们都携带相同类型的消息。这种方法如[图11-3](img/fig11-3.png)所示。
-在每个分区中,代理为每个消息分配一个单调递增的序列号或偏移量(在[图11-3](img/fig11-3.png)中,框中的数字是消息偏移量)。这样的序列号是有意义的,因为分区是仅附加的,所以分区内的消息是完全有序的。没有跨不同分区的订购保证。
+ 在每个分区中,代理为每个消息分配一个单调递增的序列号或偏移量(在[图11-3](img/fig11-3.png)中,框中的数字是消息偏移量)。这样的序列号是有意义的,因为分区是仅附加的,所以分区内的消息是完全有序的。没有跨不同分区的订购保证。

**图11-3 生产者通过将消息附加到主题分区文件来发送消息,消费者依次读取这些文件**
-Apache Kafka 【17,18】,Amazon Kinesis Streams 【19】和Twitter的DistributedLog 【20,21】都是基于日志的消息代理。 Google Cloud Pub / Sub在体系结构上相似,但是暴露了JMS风格的API而不是日志抽象【16】。尽管这些消息代理将所有消息写入磁盘,但通过在多台机器上进行分区,每秒能够实现数百万条消息的吞吐量,并通过复制消息来实现容错性【22,23】。
+ Apache Kafka 【17,18】,Amazon Kinesis Streams 【19】和Twitter的DistributedLog 【20,21】都是基于日志的消息代理。 Google Cloud Pub / Sub在体系结构上相似,但是暴露了JMS风格的API而不是日志抽象【16】。尽管这些消息代理将所有消息写入磁盘,但通过在多台机器上进行分区,每秒能够实现数百万条消息的吞吐量,并通过复制消息来实现容错性【22,23】。
#### 日志与传统消息相比
-基于日志的方法平凡支持扇出消息传递,因为多个消费者可以独立读取日志,而不会相互影响 - 读取消息不会将其从日志中删除。为了在一组消费者之间实现负载平衡,代理人可以将整个分区分配给消费者组中的节点,而不是将消息分配给消费者客户端。
+ 基于日志的方法平凡支持扇出消息传递,因为多个消费者可以独立读取日志,而不会相互影响 - 读取消息不会将其从日志中删除。为了在一组消费者之间实现负载平衡,代理人可以将整个分区分配给消费者组中的节点,而不是将消息分配给消费者客户端。
-每个客户端然后使用分配的分区中的所有消息。通常情况下,当一个用户被分配了一个日志分区时,它会以直接的单线程的方式顺序地读取分区中的消息。这种粗粒度的负载均衡方法有一些缺点:
+ 每个客户端然后使用分配的分区中的所有消息。通常情况下,当一个用户被分配了一个日志分区时,它会以直接的单线程的方式顺序地读取分区中的消息。这种粗粒度的负载均衡方法有一些缺点:
* 共享消费主题工作的节点数最多可以是该主题中的日志分区数,因为同一个分区内的消息被传递到同一个节点[^i]。
* 如果单个消息处理缓慢,则会阻止处理该分区中的后续消息(一种行头阻塞形式;请参阅“[描述性能](ch1.md#描述性能)”)。
@@ -179,87 +179,87 @@ Apache Kafka 【17,18】,Amazon Kinesis Streams 【19】和Twitter的Distribut
#### 消费者偏移量
-消耗一个分区依次可以很容易地判断哪些消息已经被处理:所有消息的偏移量小于消费者的当前偏移量已经被处理,并且具有更大偏移量的所有消息还没有被看到。因此,经纪人不需要跟踪每条消息的确认,只需要定期记录消费者的偏移。在这种方法中减少的簿记开销以及批处理和流水线的机会有助于提高基于日志的系统的吞吐量。
+ 消耗一个分区依次可以很容易地判断哪些消息已经被处理:所有消息的偏移量小于消费者的当前偏移量已经被处理,并且具有更大偏移量的所有消息还没有被看到。因此,代理不需要跟踪每条消息的确认,只需要定期记录消费者的偏移。在这种方法中减少的簿记开销以及批处理和流水线的机会有助于提高基于日志的系统的吞吐量。
-实际上,这种偏移量与单领先数据库复制中常见的日志序列号非常相似,我们在“[设置新从库](ch5.md#设置新从库)”中讨论了这种情况。在数据库复制中,日志序列号允许跟随者断开连接后,重新连接到领导,并在不跳过任何写入的情况下恢复复制。这里使用的原则完全相同:消息代理的行为像一个领导者数据库,而消费者就像一个追随者。
+ 实际上,这种偏移量与单领先数据库复制中常见的日志序列号非常相似,我们在“[设置新从库](ch5.md#设置新从库)”中讨论了这种情况。在数据库复制中,日志序列号允许跟随者断开连接后,重新连接到领导,并在不跳过任何写入的情况下恢复复制。这里使用的原则完全相同:消息代理的行为像一个领导者数据库,而消费者就像一个追随者。
-如果消费者节点失败,则使用者组中的另一个节点将被分配失败的使用者分区,并开始以最后记录的偏移量使用消息。如果消费者已经处理了后续的消息,但还没有记录他们的偏移量,那么在重新启动后这些消息将被第二次处理。本章后面我们将讨论处理这个问题的方法。
+ 如果消费者节点失败,则使用者组中的另一个节点将被分配失败的使用者分区,并开始以最后记录的偏移量使用消息。如果消费者已经处理了后续的消息,但还没有记录他们的偏移量,那么在重新启动后这些消息将被第二次处理。本章后面我们将讨论处理这个问题的方法。
#### 磁盘空间使用情况
-如果您只附加到日志,则最终将耗尽磁盘空间。为了回收磁盘空间,日志实际上被分割成段,并且不时地将旧段删除或移动到归档存储器。 (我们将在后面讨论一种更为复杂的释放磁盘空间的方法。)
+ 如果您只附加到日志,则最终将耗尽磁盘空间。为了回收磁盘空间,日志实际上被分割成段,并且不时地将旧段删除或移动到归档存储器。 (我们将在后面讨论一种更为复杂的释放磁盘空间的方法。)
-这就意味着,如果一个消费者的速度慢,消费者的消费速度落后于消费者的偏移量,那么消费者的偏移量就会指向一个已经被删除的消费者。实际上,日志实现了一个有限大小的缓冲区,当旧的消息满时,它也被称为循环缓冲区或环形缓冲区。但是,由于该缓冲区在磁盘上,因此可能相当大。
+ 这就意味着,如果一个消费者的速度慢,消费者的消费速度落后于消费者的偏移量,那么消费者的偏移量就会指向一个已经被删除的消费者。实际上,日志实现了一个有限大小的缓冲区,当旧的消息满时,它也被称为循环缓冲区或环形缓冲区。但是,由于该缓冲区在磁盘上,因此可能相当大。
-让我们来做一个后台计算。在撰写本文时,典型的大型硬盘驱动器容量为6TB,顺序写入吞吐量为150MB/s。如果您以最快的速度写邮件,则需要大约11个小时才能填满驱动器。因此,磁盘可以缓存11个小时的消息,之后它将开始覆盖旧的消息。即使您使用多个硬盘驱动器和机器,这个比率也是一样的。在实践中,部署很少使用磁盘的完整写入带宽,所以日志通常可以保存几天甚至几周的缓冲区。
+ 让我们来做一个后台计算。在撰写本文时,典型的大型硬盘驱动器容量为6TB,顺序写入吞吐量为150MB/s。如果您以最快的速度写邮件,则需要大约11个小时才能填满驱动器。因此,磁盘可以缓存11个小时的消息,之后它将开始覆盖旧的消息。即使您使用多个硬盘驱动器和机器,这个比率也是一样的。在实践中,部署很少使用磁盘的完整写入带宽,所以日志通常可以保存几天甚至几周的缓冲区。
-不管你保留多长时间的消息,一个日志的吞吐量或多或少保持不变,因为无论如何每个消息都被写入磁盘【18】。这种行为与将邮件默认保存在内存中的消息传递系统形成鲜明对比,如果队列变得太大,只将其写入磁盘:当这些系统开始写入磁盘时,这些系统速度很快,并且变慢得多,所以吞吐量取决于保留的历史数量。
+ 不管你保留多长时间的消息,一个日志的吞吐量或多或少保持不变,因为无论如何每个消息都被写入磁盘【18】。这种行为与将邮件默认保存在内存中的消息传递系统形成鲜明对比,如果队列变得太大,只将其写入磁盘:当这些系统开始写入磁盘时,这些系统速度很快,并且变慢得多,所以吞吐量取决于保留的历史数量。
#### 当消费者跟不上生产者时
-在“[信息系统]()”第441页的开头,我们讨论了如果消费者无法跟上生产者发送信息的速度的三种选择:丢弃信息,缓冲或施加背压。在这个分类法中,基于日志的方法是一种缓冲形式,具有较大但固定大小的缓冲区(受可用磁盘空间的限制)。
+ 在“[信息系统]()”第441页的开头,我们讨论了如果消费者无法跟上生产者发送信息的速度的三种选择:丢弃信息,缓冲或施加背压。在这个分类法中,基于日志的方法是一种缓冲形式,具有较大但固定大小的缓冲区(受可用磁盘空间的限制)。
-如果消费者远远落后于它所要求的信息比保留在磁盘上的信息要旧,那么它将不能读取这些信息,所以代理人有效地丢弃了比缓冲区容量更大的旧信息。您可以监控消费者在日志头后面的距离,如果显着落后,则会发出警报。由于缓冲区很大,因此有足够的时间让人类操作员修复缓慢的消费者,并在消息开始丢失之前让其赶上。
+ 如果消费者远远落后于它所要求的信息比保留在磁盘上的信息要旧,那么它将不能读取这些信息,所以代理人有效地丢弃了比缓冲区容量更大的旧信息。您可以监控消费者在日志头后面的距离,如果显着落后,则会发出警报。由于缓冲区很大,因此有足够的时间让人类操作员修复缓慢的消费者,并在消息开始丢失之前让其赶上。
-即使消费者落后太多,开始丢失信息,也只有消费者受到影响;它不会中断其他消费者的服务。这是一个巨大的运营优势:您可以通过实验性的方式使用生产日志进行开发,测试或调试,而不必担心会中断生产服务。当消费者关闭或崩溃时,会停止消耗资源,唯一剩下的就是消费者抵消。
+ 即使消费者落后太多,开始丢失信息,也只有消费者受到影响;它不会中断其他消费者的服务。这是一个巨大的运营优势:您可以通过实验性的方式使用生产日志进行开发,测试或调试,而不必担心会中断生产服务。当消费者关闭或崩溃时,会停止消耗资源,唯一剩下的就是消费者抵消。
-这种行为也与传统的信息经纪人形成了鲜明的对比,在这种情况下,您需要小心删除消费者已经关闭的任何队列,否则他们会继续不必要地积累消息,并从仍然活跃的消费者那里带走内存。
+ 这种行为也与传统的信息代理形成了鲜明的对比,在这种情况下,您需要小心删除消费者已经关闭的任何队列,否则他们会继续不必要地积累消息,并从仍然活跃的消费者那里带走内存。
#### 重播旧信息
-我们之前提到,使用AMQP和JMS风格的消息代理,处理和确认消息是一个破坏性的操作,因为它会导致消息在代理上被删除。另一方面,在基于日志的消息代理中,使用消息更像是从文件中读取数据:这是只读操作,不会更改日志。
+ 我们之前提到,使用AMQP和JMS风格的消息代理,处理和确认消息是一个破坏性的操作,因为它会导致消息在代理上被删除。另一方面,在基于日志的消息代理中,使用消息更像是从文件中读取数据:这是只读操作,不会更改日志。
-处理的唯一副作用,除了消费者的任何产出之外,消费者补偿正在向前发展。但是偏移量是在消费者的控制之下的,所以如果需要的话可以很容易地被操纵:例如,你可以用昨天的偏移量开始一个消费者的副本,并将输出写到不同的位置,以便重新处理最后一天值得的消息。您可以重复这个任意次数,改变处理代码。
+ 处理的唯一副作用,除了消费者的任何产出之外,消费者补偿正在向前发展。但是偏移量是在消费者的控制之下的,所以如果需要的话可以很容易地被操纵:例如,你可以用昨天的偏移量开始一个消费者的副本,并将输出写到不同的位置,以便重新处理最后一天值得的消息。您可以重复这个任意次数,改变处理代码。
-这方面使得基于日志的消息传递更像上一章的批处理过程,其中派生数据通过可重复的转换过程与输入数据明确分离。它允许更多的实验,更容易从错误和错误中恢复,使其成为在组织内集成数据流的好工具【24】。
+ 这方面使得基于日志的消息传递更像上一章的批处理过程,其中派生数据通过可重复的转换过程与输入数据明确分离。它允许更多的实验,更容易从错误和错误中恢复,使其成为在组织内集成数据流的好工具【24】。
## 流与数据库
-我们已经在消息代理和数据库之间进行了一些比较。尽管传统上它们被视为单独的工具类别,但是我们看到基于日志的消息中介已经成功地从数据库中获取了想法并将其应用于消息传递。我们也可以反过来:从消息和流中获取想法,并将它们应用于数据库。
+ 我们已经在消息代理和数据库之间进行了一些比较。尽管传统上它们被视为单独的工具类别,但是我们看到基于日志的消息中介已经成功地从数据库中获取了想法并将其应用于消息传递。我们也可以反过来:从消息和流中获取想法,并将它们应用于数据库。
-我们之前曾经说过,事件是某个时刻发生的事情的记录。发生的事情可能是用户操作(例如键入搜索查询)或传感器读取,但也可能是写入数据库。事情被写入数据库的事实是可以被捕获,存储和处理的事件。这一观察结果表明,数据库和数据流之间的连接不仅仅是磁盘上日志的物理存储 - 这是非常重要的。
+ 我们之前曾经说过,事件是某个时刻发生的事情的记录。发生的事情可能是用户操作(例如键入搜索查询)或传感器读取,但也可能是写入数据库。事情被写入数据库的事实是可以被捕获,存储和处理的事件。这一观察结果表明,数据库和数据流之间的连接不仅仅是磁盘上日志的物理存储 - 这是非常重要的。
-事实上,复制日志(参阅“[复制日志的实现](ch5.md#复制日志的实现)”)是数据库写入事件的流,由领导者在处理事务时生成。追随者将写入流应用到他们自己的数据库副本,从而最终得到相同数据的精确副本。复制日志中的事件描述发生的数据更改。
+ 事实上,复制日志(参阅“[复制日志的实现](ch5.md#复制日志的实现)”)是数据库写入事件的流,由领导者在处理事务时生成。追随者将写入流应用到他们自己的数据库副本,从而最终得到相同数据的精确副本。复制日志中的事件描述发生的数据更改。
-我们还在“[全序广播](ch9.md#全序广播)”中遇到了状态机复制原理,其中指出:如果每个事件代表对数据库的写入,并且每个副本按相同的顺序处理相同的事件,则副本将所有这些都以相同的最终状态结束。 (处理一个事件被认为是一个确定性的操作。)这只是事件流的另一种情况!
+ 我们还在“[全序广播](ch9.md#全序广播)”中遇到了状态机复制原理,其中指出:如果每个事件代表对数据库的写入,并且每个副本按相同的顺序处理相同的事件,则副本将所有这些都以相同的最终状态结束。 (处理一个事件被认为是一个确定性的操作。)这只是事件流的另一种情况!
-在本节中,我们将首先看看异构数据系统中出现的一个问题,然后探讨如何通过将事件流的想法带入数据库来解决这个问题。
+ 在本节中,我们将首先看看异构数据系统中出现的一个问题,然后探讨如何通过将事件流的想法带入数据库来解决这个问题。
### 保持系统同步
-正如我们在本书中所看到的,没有一个系统能够满足所有的数据存储,查询和处理需求。在实践中,大多数不重要的应用程序需要结合多种不同的技术来满足他们的需求:例如,使用OLTP数据库来为用户请求提供服务,缓存来加速常见请求,处理全文索引搜索查询和用于分析的数据仓库。每个数据都有其自己的数据副本,存储在自己的表示中,并根据自己的目的进行了优化。
+ 正如我们在本书中所看到的,没有一个系统能够满足所有的数据存储,查询和处理需求。在实践中,大多数不重要的应用程序需要结合多种不同的技术来满足他们的需求:例如,使用OLTP数据库来为用户请求提供服务,缓存来加速常见请求,处理全文索引搜索查询和用于分析的数据仓库。每个数据都有其自己的数据副本,存储在自己的表示中,并根据自己的目的进行了优化。
-由于相同或相关的数据出现在不同的地方,因此需要保持相互同步:如果某个项目在数据库中进行了更新,则还需要在缓存,搜索索引和数据仓库中进行更新。对于数据仓库,这种同步通常由ETL进程执行(参见“[数据仓库](ch3.md#数据仓库)”),通常通过获取数据库的完整副本,转换数据库并将其批量加载到数据仓库中 —— 换句话说,一个批处理。同样,我们在“[批量工作流的输出](ch10.md#批量工作流的输出)”中看到了如何使用批处理过程创建搜索索引,建议系统和其他派生数据系统。
+ 由于相同或相关的数据出现在不同的地方,因此需要保持相互同步:如果某个项目在数据库中进行了更新,则还需要在缓存,搜索索引和数据仓库中进行更新。对于数据仓库,这种同步通常由ETL进程执行(参见“[数据仓库](ch3.md#数据仓库)”),通常通过获取数据库的完整副本,转换数据库并将其批量加载到数据仓库中 —— 换句话说,一个批处理。同样,我们在“[批量工作流的输出](ch10.md#批量工作流的输出)”中看到了如何使用批处理过程创建搜索索引,建议系统和其他派生数据系统。
-如果周期性的完整数据库转储过于缓慢,有时使用的替代方法是双重写入,其中应用程序代码在数据更改时明确写入每个系统:例如,首先写入数据库,然后更新搜索索引,然后使缓存条目无效(或者甚至同时执行这些写入)。
+ 如果周期性的完整数据库转储过于缓慢,有时使用的替代方法是双重写入,其中应用程序代码在数据更改时明确写入每个系统:例如,首先写入数据库,然后更新搜索索引,然后使缓存条目无效(或者甚至同时执行这些写入)。
-但是,双重写入有一些严重的问题,其中一个是[图11-4](img/fig11-4.png)所示的竞争条件。在这个例子中,两个客户端同时想要更新一个项目X:客户端1想要将值设置为A,客户端2想要将其设置为B.两个客户端首先将新值写入数据库,然后将其写入到搜索索引。由于运行时间不正确,这些请求是交错的:数据库首先看到从客户端1的写入将值设置为A,然后从客户端2写入将值设置为B,因此数据库中的最终值为B.搜索索引首先看到来自客户端2,然后是客户端1的写入,所以搜索索引中的最终值是A.这两个系统现在永久地不一致,即使没有发生错误。
+ 但是,双重写入有一些严重的问题,其中一个是[图11-4](img/fig11-4.png)所示的竞争条件。在这个例子中,两个客户端同时想要更新一个项目X:客户端1想要将值设置为A,客户端2想要将其设置为B.两个客户端首先将新值写入数据库,然后将其写入到搜索索引。由于运行时间不正确,这些请求是交错的:数据库首先看到从客户端1的写入将值设置为A,然后从客户端2写入将值设置为B,因此数据库中的最终值为B.搜索索引首先看到来自客户端2,然后是客户端1的写入,所以搜索索引中的最终值是A.这两个系统现在永久地不一致,即使没有发生错误。

**图11-4 在数据库中,X首先被设置为A,然后被设置为B,而在搜索索引处,写入以相反的顺序到达**
-除非有一些额外的并发检测机制,例如我们在“[检测并发写入](ch5.md#检测并发写入)”中讨论的版本向量,否则您甚至不会注意到发生了并发写入 —— 一个值将简单地以无提示方式覆盖另一个值。
+ 除非有一些额外的并发检测机制,例如我们在“[检测并发写入](ch5.md#检测并发写入)”中讨论的版本向量,否则您甚至不会注意到发生了并发写入 —— 一个值将简单地以无提示方式覆盖另一个值。
-双重写入的另一个问题是其中一个写入可能会失败,而另一个成功。这是一个容错问题,而不是一个并发问题,但也会造成两个系统互相矛盾的结果。确保它们都成功或者两者都失败是原子提交问题的一个例子,这个问题的解决是昂贵的(参阅“[原子提交和两阶段提交(2PC)](ch7.md#原子提交和两阶段提交(2PC))”)。
+ 双重写入的另一个问题是其中一个写入可能会失败,而另一个成功。这是一个容错问题,而不是一个并发问题,但也会造成两个系统互相矛盾的结果。确保它们都成功或者两者都失败是原子提交问题的一个例子,这个问题的解决是昂贵的(参阅“[原子提交和两阶段提交(2PC)](ch7.md#原子提交和两阶段提交(2PC))”)。
-如果只有一个复制的数据库和一个领导者,那么这个领导者决定了写入顺序,所以状态机复制方法可以在数据库的副本中工作。然而,在[图11-4](img/fig11-4.png)中,没有一个领导者:数据库可能有一个领导者,搜索索引可能有一个领导者,但是既不在另一个领导者之后,也可能发生冲突(参见“[多领导者复制](ch5.md#多领导者复制)“)。
+ 如果只有一个复制的数据库和一个领导者,那么这个领导者决定了写入顺序,所以状态机复制方法可以在数据库的副本中工作。然而,在[图11-4](img/fig11-4.png)中,没有一个领导者:数据库可能有一个领导者,搜索索引可能有一个领导者,但是既不在另一个领导者之后,也可能发生冲突(参见“[多领导者复制](ch5.md#多领导者复制)“)。
-如果实际上只有一个领导者(例如数据库),并且如果我们可以使搜索索引成为数据库的追随者,情况会更好。但这在实践中可能吗?
+ 如果实际上只有一个领导者(例如数据库),并且如果我们可以使搜索索引成为数据库的追随者,情况会更好。但这在实践中可能吗?
### 改变数据捕获
-大多数数据库的复制日志的问题在于,它们一直被认为是数据库的内部实现细节,而不是公共API。客户端应该通过其数据模型和查询语言来查询数据库,而不是分析复制日志并尝试从中提取数据。
+ 大多数数据库的复制日志的问题在于,它们一直被认为是数据库的内部实现细节,而不是公共API。客户端应该通过其数据模型和查询语言来查询数据库,而不是分析复制日志并尝试从中提取数据。
-数十年来,许多数据库根本没有记录方式来获取写入到他们的变更日志。由于这个原因,很难将数据库中所做的所有更改复制到不同的存储技术,如搜索索引,缓存或数据仓库。
+ 数十年来,许多数据库根本没有记录方式来获取写入到他们的变更日志。由于这个原因,很难将数据库中所做的所有更改复制到不同的存储技术,如搜索索引,缓存或数据仓库。
-最近,人们对变更数据捕获(CDC)越来越感兴趣,它是观察写入数据库的所有数据变化并将其提取出来,并将其复制到其他系统中的过程。 CDC特别感兴趣的是,如果改变可以立即用于流,可以立即写入。
+ 最近,人们对变更数据捕获(CDC)越来越感兴趣,它是观察写入数据库的所有数据变化并将其提取出来,并将其复制到其他系统中的过程。 CDC特别感兴趣的是,如果改变可以立即用于流,可以立即写入。
-例如,您可以捕获数据库中的更改并不断将相同的更改应用于搜索索引。如果更改的日志以相同的顺序应用,则可以预期搜索索引中的数据与数据库中的数据匹配。搜索索引和任何其他派生的数据系统只是变化流的消费者,如[图11-5](img/fig11-5.png)所示。
+ 例如,您可以捕获数据库中的更改并不断将相同的更改应用于搜索索引。如果更改的日志以相同的顺序应用,则可以预期搜索索引中的数据与数据库中的数据匹配。搜索索引和任何其他派生的数据系统只是变化流的消费者,如[图11-5](img/fig11-5.png)所示。

@@ -267,155 +267,155 @@ Apache Kafka 【17,18】,Amazon Kinesis Streams 【19】和Twitter的Distribut
#### 实现变更数据捕获
-我们可以调用日志消费者导出的数据系统,正如在第三部分的介绍中所讨论的:存储在搜索索引和数据仓库中的数据只是记录系统中数据的另一个视图。更改数据捕获是一种机制,可确保对记录系统所做的所有更改都反映在派生数据系统中,以便派生系统具有数据的准确副本。
+ 我们可以调用日志消费者导出的数据系统,正如在第三部分的介绍中所讨论的:存储在搜索索引和数据仓库中的数据只是记录系统中数据的另一个视图。更改数据捕获是一种机制,可确保对记录系统所做的所有更改都反映在派生数据系统中,以便派生系统具有数据的准确副本。
-从本质上说,改变数据捕获使得一个数据库成为领导者(从中捕获变化的数据库),并将其他人变成追随者。基于日志的消息代理非常适合从源数据库传输更改事件,因为它保留了消息的排序(避免了[图11-2](img/fig11-2.png)的重新排序问题)。
+ 从本质上说,改变数据捕获使得一个数据库成为领导者(从中捕获变化的数据库),并将其他人变成追随者。基于日志的消息代理非常适合从源数据库传输更改事件,因为它保留了消息的排序(避免了[图11-2](img/fig11-2.png)的重新排序问题)。
-数据库触发器可用于通过注册触发器来实现更改数据捕获(参阅“[基于触发器的复制](ch5.md#基于触发器的复制)”),这些触发器可观察数据表的所有更改,并将相应的条目添加到更改日志表中。但是,他们往往是脆弱的,并有显着的性能开销。解析复制日志可以是一个更强大的方法,但它也带来了挑战,例如处理模式更改。
+ 数据库触发器可用于通过注册触发器来实现更改数据捕获(参阅“[基于触发器的复制](ch5.md#基于触发器的复制)”),这些触发器可观察数据表的所有更改,并将相应的条目添加到更改日志表中。但是,他们往往是脆弱的,并有显着的性能开销。解析复制日志可以是一个更强大的方法,但它也带来了挑战,例如处理模式更改。
-LinkedIn的Databus 【25】,Facebook的Wormhole 【26】和Yahoo!的Sherpa 【27】大规模地使用这个想法。 Bottled Water使用解码预写日志的API来实现PostgreSQL的CDC 【28】,Maxwell和Debezium通过解析binlog为MySQL做类似的事情【29,30,31】,Mongoriver读取MongoDB oplog 【32,33】 ,而GoldenGate为Oracle提供类似的功能【34,35】。
+ LinkedIn的Databus 【25】,Facebook的Wormhole 【26】和Yahoo!的Sherpa 【27】大规模地使用这个想法。 Bottled Water使用解码预写日志的API来实现PostgreSQL的CDC 【28】,Maxwell和Debezium通过解析binlog为MySQL做类似的事情【29,30,31】,Mongoriver读取MongoDB oplog 【32,33】 ,而GoldenGate为Oracle提供类似的功能【34,35】。
-像消息代理一样,更改数据捕获通常是异步的:记录数据库系统不会等待更改应用到消费者,然后再进行更改。这种设计具有的操作优势是添加缓慢的使用者不会影响记录系统太多,但是它具有所有复制滞后问题的缺点(参见“[复制延迟问题](ch5.md#复制延迟问题)”)。
+ 像消息代理一样,更改数据捕获通常是异步的:记录数据库系统不会等待更改应用到消费者,然后再进行更改。这种设计具有的操作优势是添加缓慢的使用者不会影响记录系统太多,但是它具有所有复制滞后问题的缺点(参见“[复制延迟问题](ch5.md#复制延迟问题)”)。
#### 初始快照
-如果具有对数据库所做的所有更改的日志,则可以通过重播日志来重新构建数据库的整个状态。但是,在许多情况下,永远保留所有更改将需要太多的磁盘空间,并且重播将花费太长时间,因此日志需要被截断。
+ 如果具有对数据库所做的所有更改的日志,则可以通过重播日志来重新构建数据库的整个状态。但是,在许多情况下,永远保留所有更改将需要太多的磁盘空间,并且重播将花费太长时间,因此日志需要被截断。
-例如,构建新的全文索引需要整个数据库的完整副本 - 仅仅应用最近更改的日志是不够的,因为它将丢失最近未更新的项目。因此,如果您没有完整的日志历史记录,则需要从一致的快照开始,如先前在第155页上的“设置新的追随者”中所述。
+ 例如,构建新的全文索引需要整个数据库的完整副本 —— 仅仅应用最近更改的日志是不够的,因为它将丢失最近未更新的项目。因此,如果您没有完整的日志历史记录,则需要从一致的快照开始,如先前在第155页上的“设置新的追随者”中所述。
-数据库的快照必须与更改日志中的已知位置或偏移量相对应,以便您知道在快照处理完成后,在哪一点开始应用更改。一些CDC工具集成了这个快照工具,而其他工具则将其作为手动操作。
+ 数据库的快照必须与更改日志中的已知位置或偏移量相对应,以便您知道在快照处理完成后,在哪一点开始应用更改。一些CDC工具集成了这个快照工具,而其他工具则将其作为手动操作。
#### 日志压缩
-如果只能保留有限的日志历史记录,则每次需要添加新的派生数据系统时都需要执行快照过程。但是,日志压缩提供了一个很好的选择。
+ 如果只能保留有限的日志历史记录,则每次需要添加新的派生数据系统时都需要执行快照过程。但是,日志压缩提供了一个很好的选择。
-在日志结构化的存储引擎的情况下,我们先讨论了“[Hash索引](ch3.md#Hash索引)”中的日志压缩(参见[图3-2](img/fig3-2.png)的示例)。原理很简单:存储引擎使用相同的密钥定期查找日志记录,丢弃任何重复内容,并且只保留每个密钥的最新更新。这个压缩和合并过程在后台运行。
+ 在日志结构化的存储引擎的情况下,我们先讨论了“[Hash索引](ch3.md#Hash索引)”中的日志压缩(参见[图3-2](img/fig3-2.png)的示例)。原理很简单:存储引擎使用相同的密钥定期查找日志记录,丢弃任何重复内容,并且只保留每个密钥的最新更新。这个压缩和合并过程在后台运行。
-在日志结构存储引擎中,具有特殊空值(逻辑删除)的更新指示删除了一个密钥,并在日志压缩过程中将其删除。但只要密钥不被覆盖或删除,它就永远留在日志中。这种压缩日志所需的磁盘空间仅取决于数据库的当前内容,而不取决于数据库中发生的写入次数。如果相同的密钥经常被覆盖,则以前的值将最终被垃圾收集,并且只保留最新的值。
+ 在日志结构存储引擎中,具有特殊空值(逻辑删除)的更新指示删除了一个密钥,并在日志压缩过程中将其删除。但只要密钥不被覆盖或删除,它就永远留在日志中。这种压缩日志所需的磁盘空间仅取决于数据库的当前内容,而不取决于数据库中发生的写入次数。如果相同的密钥经常被覆盖,则以前的值将最终被垃圾收集,并且只保留最新的值。
-在基于日志的消息代理和更改数据捕获方面,相同的想法也适用。如果CDC系统设置为每个更改都有一个主键,并且每个键的更新都替换了该键的以前的值,那么仅保留最近写入的特定键就足够了。
+ 在基于日志的消息代理和更改数据捕获方面,相同的想法也适用。如果CDC系统设置为每个更改都有一个主键,并且每个键的更新都替换了该键的以前的值,那么仅保留最近写入的特定键就足够了。
-现在,无论何时要重建派生数据系统(如搜索索引),都可以从日志压缩主题的偏移量0开始新的使用者,然后依次扫描日志中的所有消息。日志保证包含数据库中每个键的最新值(也可能是一些较旧的值)—— 换句话说,您可以使用它来获取数据库内容的完整副本,而无需获取CDC的另一个快照源数据库。
+ 现在,无论何时要重建派生数据系统(如搜索索引),都可以从日志压缩主题的偏移量0开始新的使用者,然后依次扫描日志中的所有消息。日志保证包含数据库中每个键的最新值(也可能是一些较旧的值)—— 换句话说,您可以使用它来获取数据库内容的完整副本,而无需获取CDC的另一个快照源数据库。
-Apache Kafka支持此日志压缩功能。正如我们将在本章后面看到的,它允许消息代理被用于持久存储,而不仅仅是用于临时消息。
+ Apache Kafka支持此日志压缩功能。正如我们将在本章后面看到的,它允许消息代理被用于持久存储,而不仅仅是用于临时消息。
#### API支持更改流
-越来越多的数据库开始支持变更流作为一流的接口,而不是典型的改造和逆向工程CDC的努力。例如,RethinkDB允许查询在查询更改结果【36】,Firebase 【37】和CouchDB 【38】基于同样可用于应用程序的更改提要进行数据同步时订阅通知,而Meteor使用MongoDB oplog订阅数据更改并更新用户界面【39】。
+ 越来越多的数据库开始支持变更流作为一流的接口,而不是典型的改造和逆向工程CDC的努力。例如,RethinkDB允许查询在查询更改结果【36】,Firebase 【37】和CouchDB 【38】基于同样可用于应用程序的更改提要进行数据同步时订阅通知,而Meteor使用MongoDB oplog订阅数据更改并更新用户界面【39】。
-VoltDB允许事务以流的形式连续地从数据库中导出数据【40】。数据库将关系数据模型中的输出流表示为一个表,事务可以在其中插入元组,但不能被查询。然后这个流由提交事务已经写入这个特殊表的元组日志组成,它们按照提交的顺序。外部使用者可以异步使用此日志并使用它来更新派生的数据系统。
+ VoltDB允许事务以流的形式连续地从数据库中导出数据【40】。数据库将关系数据模型中的输出流表示为一个表,事务可以在其中插入元组,但不能被查询。然后这个流由提交事务已经写入这个特殊表的元组日志组成,它们按照提交的顺序。外部使用者可以异步使用此日志并使用它来更新派生的数据系统。
-Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获工具与Kafka集成。一旦更改事件发生在Kafka中,它就可以用来更新派生的数据系统,比如搜索索引,也可以用于本章稍后讨论的流处理系统。
+ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获工具与Kafka集成。一旦更改事件发生在Kafka中,它就可以用来更新派生的数据系统,比如搜索索引,也可以用于本章稍后讨论的流处理系统。
### 事件源
-我们在这里讨论的想法和事件采购之间有一些相似之处,这是一个在领域驱动设计(DDD)社区中开发的技术。我们将简要讨论事件源,因为它包含了一些有用的和相关的流式系统的想法。
+我 们在这里讨论的想法和事件代理之间有一些相似之处,这是一个在领域驱动设计(DDD)社区中开发的技术。我们将简要讨论事件源,因为它包含了一些有用的和相关的流式系统的想法。
-与更改数据捕获类似,事件采购涉及将所有对应用程序状态的更改存储为更改事件的日志。最大的区别是事件源代码在不同的抽象层次上应用了这个想法:
+ 与更改数据捕获类似,事件代理涉及将所有对应用程序状态的更改存储为更改事件的日志。最大的区别是事件源代码在不同的抽象层次上应用了这个想法:
* 在更改数据捕获中,应用程序以可变方式使用数据库,随意更新和删除记录。从数据库中提取较低级别的更改日志(例如,通过解析复制日志),从而确保从数据库中提取的写入顺序与实际写入的顺序相匹配,从而避免[图11-4](img/fig11-4.png)中的竞争条件。写入数据库的应用程序不需要知道CDC正在发生。
* 在事件源中,应用程序逻辑是基于写入事件日志的不可变事件而显式构建的。在这种情况下,事件存储是附加的,更新或删除是不鼓励或禁止的。事件旨在反映应用程序级别发生的事情,而不是低级状态更改。
-事件源是一种强大的数据建模技术:从应用程序的角度来看,将用户的行为记录为不可变的事件更有意义,而不是记录这些行为对可变数据库的影响。事件采购使得随着时间的推移而逐渐发展应用程序变得更加容易,通过更容易理解事情发生的原因以及防范应用程序错误(请参阅“[不可变事件的优点](#不可变事件的优点)”),帮助进行调试。
+事件源是一种强大的数据建模技术:从应用程序的角度来看,将用户的行为记录为不可变的事件更有意义,而不是记录这些行为对可变数据库的影响。事件代理使得随着时间的推移而逐渐发展应用程序变得更加容易,通过更容易理解事情发生的原因以及防范应用程序错误(请参阅“[不可变事件的优点](#不可变事件的优点)”),帮助进行调试。
-例如,存储“学生取消课程注册”事件清楚地表达了单一行为的中性意图,而副作用“从注册表中删除了一个条目,并且一个取消原因被添加到学生反馈表“嵌入了很多有关方式的假设数据稍后将被使用。如果引入新的应用程序功能,例如“将地点提供给等待列表中的下一个人” —— 事件顺序方法允许将新的副作用轻松地链接到现有事件上。
+ 例如,存储“学生取消课程注册”事件清楚地表达了单一行为的中性意图,而副作用“从注册表中删除了一个条目,并且一个取消原因被添加到学生反馈表“嵌入了很多有关方式的假设数据稍后将被使用。如果引入新的应用程序功能,例如“将地点提供给等待列表中的下一个人” —— 事件顺序方法允许将新的副作用轻松地链接到现有事件上。
-事件顺序类似于编年史数据模型【45】,事件日志和事实表之间也有相似之处,您可以在星型模式中找到它(参阅“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”) 。
+ 事件顺序类似于编年史数据模型【45】,事件日志和事实表之间也有相似之处,您可以在星型模式中找到它(参阅“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”) 。
-专门的数据库如Event Store 【46】已经被开发来支持使用事件采购的应用程序,但总的来说,这个方法是独立于任何特定的工具的。传统的数据库或基于日志的消息代理也可以用来构建这种风格的应用程序。
+ 专门的数据库如Event Store 【46】已经被开发来支持使用事件代理的应用程序,但总的来说,这个方法是独立于任何特定的工具的。传统的数据库或基于日志的消息代理也可以用来构建这种风格的应用程序。
#### 从事件日志中导出当前状态
-事件日志本身并不是很有用,因为用户通常期望看到系统的当前状态,而不是修改的历史。例如,在购物网站上,用户期望能够看到他们购物车的当前内容,而不是他们对购物车所做的所有改变的附加列表。
+ 事件日志本身并不是很有用,因为用户通常期望看到系统的当前状态,而不是修改的历史。例如,在购物网站上,用户期望能够看到他们购物车的当前内容,而不是他们对购物车所做的所有改变的附加列表。
-因此,使用事件源的应用程序需要记录事件的日志(表示写入系统的数据),并将其转换为适合向用户显示的应用程序状态(从系统读取数据的方式【47】)。这种转换可以使用任意的逻辑,但它应该是确定性的,以便您可以再次运行它并从事件日志中派生相同的应用程序状态。
+ 因此,使用事件源的应用程序需要记录事件的日志(表示写入系统的数据),并将其转换为适合向用户显示的应用程序状态(从系统读取数据的方式【47】)。这种转换可以使用任意的逻辑,但它应该是确定性的,以便您可以再次运行它并从事件日志中派生相同的应用程序状态。
-与更改数据捕获一样,重放事件日志可以让您重新构建系统的当前状态。但是,日志压缩需要以不同的方式处理:
+ 与更改数据捕获一样,重放事件日志可以让您重新构建系统的当前状态。但是,日志压缩需要以不同的方式处理:
* 用于记录更新的CDC事件通常包含记录的全部新版本,因此主键的当前值完全由该主键的最近事件确定,并且日志压缩可以丢弃以前的事件同样的钥匙。
-* 另一方面,事件采购是将事件建模为更高的级别:事件通常表示用户操作的意图,而不是由于操作而发生的状态更新的机制。在这种情况下,以后的事件通常不会覆盖以前的事件,所以您需要事件的完整历史来重构最终状态。日志压缩不可能以相同的方式进行。
+* 另一方面,事件代理是将事件建模为更高的级别:事件通常表示用户操作的意图,而不是由于操作而发生的状态更新的机制。在这种情况下,以后的事件通常不会覆盖以前的事件,所以您需要事件的完整历史来重构最终状态。日志压缩不可能以相同的方式进行。
使用事件源的应用程序通常有一些机制来存储从事件日志中导出的当前状态的快照,因此它们不需要重复处理完整的日志。但是,这只是一个性能优化,可以加快读取和恢复崩溃的速度;目的是系统能够永久存储所有原始事件,并在需要时重新处理完整的事件日志。我们在第463页的“不变性的限制”中讨论这个假设。
#### 命令和事件
-事件采购哲学是仔细区分事件和命令【48】。当来自用户的请求首先到达时,它最初是一个命令:在这一点上它可能仍然失败,例如因为违反了一些完整性条件。应用程序必须首先验证它是否可以执行该命令。如果验证成功并且命令被接受,则它变成一个持久且不可变的事件。
+ 事件代理哲学是仔细区分事件和命令【48】。当来自用户的请求首先到达时,它最初是一个命令:在这一点上它可能仍然失败,例如因为违反了一些完整性条件。应用程序必须首先验证它是否可以执行该命令。如果验证成功并且命令被接受,则它变成一个持久且不可变的事件。
-例如,如果用户试图注册特定用户名,或在飞机上或剧院中预定座位,则应用程序需要检查用户名或座位是否已被占用。 (我们先前在第364页的“[容错概念](ch8.md#容错概念)”中讨论过这个例子。)当检查成功时,应用程序可以生成一个事件来指示特定的用户名是由特定的用户ID注册的,座位已经预留给特定的顾客。
+ 例如,如果用户试图注册特定用户名,或在飞机上或剧院中预定座位,则应用程序需要检查用户名或座位是否已被占用。 (我们先前在第364页的“[容错概念](ch8.md#容错概念)”中讨论过这个例子。)当检查成功时,应用程序可以生成一个事件来指示特定的用户名是由特定的用户ID注册的,座位已经预留给特定的顾客。
-在事件发生的时候,这成为事实。即使客户稍后决定更改或取消预订,事实仍然是事实,他们以前曾为某个特定的座位进行预订,而更改或取消是稍后添加的单独事件。
+ 在事件发生的时候,这成为事实。即使客户稍后决定更改或取消预订,事实仍然是事实,他们以前曾为某个特定的座位进行预订,而更改或取消是稍后添加的单独事件。
-事件流的消费者不允许拒绝事件:当消费者看到事件时,它已经是日志中不可变的部分,并且可能已经被其他消费者看到。因此,任何命令的验证都需要在事件成为事件之前同步发生,例如,通过使用可自动验证命令并发布事件的可序列化事务。
+ 事件流的消费者不允许拒绝事件:当消费者看到事件时,它已经是日志中不可变的部分,并且可能已经被其他消费者看到。因此,任何命令的验证都需要在事件成为事件之前同步发生,例如,通过使用可自动验证命令并发布事件的可序列化事务。
-或者,预订座位的用户请求可以分成两个事件:第一个是暂时预约,第二个是确认预约后的单独确认事件(如第350页上的“使用总订单广播实现线性化存储”中所述) 。这个分割允许验证发生在一个异步的过程中。
+ 或者,预订座位的用户请求可以分成两个事件:第一个是暂时预约,第二个是确认预约后的单独确认事件(如第350页上的“使用总订单广播实现线性化存储”中所述) 。这个分割允许验证发生在一个异步的过程中。
#### 状态,流和不变性
-我们在第10章中看到批量处理从其输入文件的不变性中受益,因此您可以在现有输入文件上运行实验性处理作业,而不用担心损坏它们。这种不变性原则也是使得事件采购和数据变化如此强大的原因。
+ 我们在[第10章](ch10.md)中看到批量处理从其输入文件的不变性中受益,因此您可以在现有输入文件上运行实验性处理作业,而不用担心损坏它们。这种不变性原则也是使得事件代理和数据变化如此强大的原因。
-我们通常将数据库视为存储应用程序的当前状态 - 这种表示法针对读取进行了优化,而且通常对于服务查询来说是最方便的。状态的本质是它的变化,所以数据库支持更新和删除数据以及插入数据。这是如何符合不变性的?
+ 我们通常将数据库视为存储应用程序的当前状态 —— 这种表示法针对读取进行了优化,而且通常对于服务查询来说是最方便的。状态的本质是它的变化,所以数据库支持更新和删除数据以及插入数据。这是如何符合不变性的?
-只要你的状态发生了变化,那么这个状态是随着时间的推移而变化的事件的结果。例如,您当前可用的座位列表是您已经处理的预订的结果,当前帐户余额是帐户中的信用卡和借方的结果,而您的Web服务器的响应时间图是发生的所有Web请求的个别响应时间。
+ 只要你的状态发生了变化,那么这个状态是随着时间的推移而变化的事件的结果。例如,您当前可用的座位列表是您已经处理的预订的结果,当前帐户余额是帐户中的信用卡和借方的结果,而您的Web服务器的响应时间图是发生的所有Web请求的个别响应时间。
-无论国家如何变化,总会有一系列事件导致这些变化。即使事情已经解决,事实仍然是事实发生的事实。关键的想法是可变状态和不可变事件的附加日志不相互矛盾:它们是同一枚硬币的两面。所有变化的日志,变化日志,代表了随着时间的推移状态的演变。
+ 无论状态如何变化,总会有一系列事件导致这些变化。即使事情已经解决,事实仍然是事实发生的事实。关键的想法是可变状态和不可变事件的附加日志不相互矛盾:它们是同一枚硬币的两面。所有变化的日志,变化日志,代表了随着时间的推移状态的演变。
-如果您有数学上的倾向,那么您可能会说应用程序状态是随着时间的推移整合了一个事件流而得到的,而且当您按照时间区分状态时会得到一个更改流,如[图11-6](img/fig11-6.png)所示【49,50,51】。这个比喻有一定的局限性(例如,国家的二阶导数似乎没有意义),但这是考虑数据的一个有用的起点。
+ 如果您有数学上的倾向,那么您可能会说应用程序状态是随着时间的推移整合了一个事件流而得到的,而且当您按照时间区分状态时会得到一个更改流,如[图11-6](img/fig11-6.png)所示【49,50,51】。这个比喻有一定的局限性(例如,状态的二阶导数似乎没有意义),但这是考虑数据的一个有用的起点。

**图11-6 当前应用程序状态和事件流之间的关系**
-如果你持久地存储更新日志,那么这只是使状态重现的效果。如果你认为事件的日志是你的记录系统,并且从它派生出任何可变状态,那么就更容易推断通过系统的数据流。正如帕特·赫兰(Pat Helland)所说的【52】:
+ 如果你持久地存储更新日志,那么这只是使状态重现的效果。如果你认为事件的日志是你的记录系统,并且从它派生出任何可变状态,那么就更容易推断通过系统的数据流。正如帕特·赫兰(Pat Helland)所说的【52】:
> 事务日志记录对数据库所做的所有更改。高速追加是更改日志的唯一方法。从这个角度来看,数据库的内容会保存日志中最新记录值的缓存。事实是日志。数据库是日志子集的缓存。该缓存子集恰好是来自日志的每个记录和索引值的最新值。
-日志压缩(如“[日志压缩](#日志压缩)”中所述)是一种桥接日志和数据库状态之间区别的方法:它只保留每条记录的最新版本,并丢弃被覆盖的版本。
+ 日志压缩(如“[日志压缩](#日志压缩)”中所述)是一种桥接日志和数据库状态之间区别的方法:它只保留每条记录的最新版本,并丢弃被覆盖的版本。
#### 不可变事件的优点
-数据库中的不变性是一个古老的想法。例如,会计师在数个世纪以来一直使用不变性财务簿记。当一笔交易发生时,它被记录在一个仅追加分类帐中,这本质上是描述货币,商品或服务已经转手的事件日志。账目,如损益或资产负债表,是从分类账中的交易中加起来得来的【53】。
+ 数据库中的不变性是一个古老的想法。例如,会计师在数个世纪以来一直使用不变性财务簿记。当一笔交易发生时,它被记录在一个仅追加分类帐中,这本质上是描述货币,商品或服务已经转手的事件日志。账目,如损益或资产负债表,是从分类账中的交易中加起来得来的【53】。
-如果发生错误,会计师不会删除或更改分类帐中的错误交易 - 而是增加另一笔交易,以补偿错误,例如退还不正确的费用。不正确的交易将永远保留在分类帐中,因为审计原因可能很重要。如果从不正确的分类账导出的错误数字已经公布,那么下一个会计期间的数字就包括一个更正。这个过程在会计中是完全正常的【54】。
+ 如果发生错误,会计师不会删除或更改分类帐中的错误交易 - 而是增加另一笔交易,以补偿错误,例如退还不正确的费用。不正确的交易将永远保留在分类帐中,因为审计原因可能很重要。如果从不正确的分类账导出的错误数字已经公布,那么下一个会计期间的数字就包括一个更正。这个过程在会计中是完全正常的【54】。
-尽管这种可审计性在金融系统中尤其重要,但对于不受这种严格管制的许多其他系统也是有益的。如“批处理输出的哲学”(第439页)中所述,如果您意外地部署了将错误数据写入数据库的错误代码,那么如果代码能够破坏性地覆盖数据,恢复将更加困难。通过不可变事件的追加日志,诊断发生的事情和从问题中恢复起来要容易得多。
+ 尽管这种可审计性在金融系统中尤其重要,但对于不受这种严格管制的许多其他系统也是有益的。如“批处理输出的哲学”(第439页)中所述,如果您意外地部署了将错误数据写入数据库的错误代码,那么如果代码能够破坏性地覆盖数据,恢复将更加困难。通过不可变事件的追加日志,诊断发生的事情和从问题中恢复起来要容易得多。
-不可变的事件也捕获比当前状态更多的信息。例如,在购物网站上,顾客可以将物品添加到他们的购物车,然后再将其移除。虽然第二个事件从订单履行角度取消了第一个事件,但为了分析目的,客户正在考虑某个特定项目,但是之后决定采取反对措施。也许他们会选择在未来购买,或者他们找到替代品。这个信息被记录在一个事件日志中,但是当它们从购物车中被删除时,这个信息会丢失在删除项目的数据库中【42】。
+ 不可变的事件也捕获比当前状态更多的信息。例如,在购物网站上,顾客可以将物品添加到他们的购物车,然后再将其移除。虽然第二个事件从订单履行角度取消了第一个事件,但为了分析目的,客户正在考虑某个特定项目,但是之后决定采取反对措施。也许他们会选择在未来购买,或者他们找到替代品。这个信息被记录在一个事件日志中,但是当它们从购物车中被删除时,这个信息会丢失在删除项目的数据库中【42】。
#### 从同一事件日志中获取多个视图
-而且,通过从不变事件日志中分离可变状态,可以从事件的相同日志中派生出几个不同的面向读取的表示。这就像一个流的多个消费者一样工作([图11-5](img/fig11-5.png)):例如,分析数据库Druid使用这种方法从Kafka直接获取【55】,Pista chio是一个分布式的键值存储,使用Kafka作为提交日志【56】,Kafka Connect接收器可以将来自Kafka的数据导出到各种不同的数据库和索引【41】。对于许多其他存储和索引系统(如搜索服务器)来说,类似地从分布式日志中获取输入也是有意义的(请参阅“[保持系统同步](#保持系统同步)”)。
+ 而且,通过从不变事件日志中分离可变状态,可以从事件的相同日志中派生出几个不同的面向读取的表示。这就像一个流的多个消费者一样工作([图11-5](img/fig11-5.png)):例如,分析数据库Druid使用这种方法从Kafka直接获取【55】,Pista chio是一个分布式的键值存储,使用Kafka作为提交日志【56】,Kafka Connect接收器可以将来自Kafka的数据导出到各种不同的数据库和索引【41】。对于许多其他存储和索引系统(如搜索服务器)来说,类似地从分布式日志中获取输入也是有意义的(请参阅“[保持系统同步](#保持系统同步)”)。
-从事件日志到数据库有一个明确的转换步骤,可以更容易地随时间推移您的应用程序:如果您想要引入一个以新的方式呈现现有数据的新功能,您可以使用事件日志来构建一个单独的新功能的读取优化视图,并与现有的一起运行
+ 从事件日志到数据库有一个明确的转换步骤,可以更容易地随时间推移您的应用程序:如果您想要引入一个以新的方式呈现现有数据的新功能,您可以使用事件日志来构建一个单独的新功能的读取优化视图,并与现有的一起运行
-系统而不必修改它们。并行运行旧系统和新系统通常比在现有系统中执行复杂的模式迁移更容易。一旦旧的系统不再需要,你可以简单地关闭它并回收它的资源【47,57】。
+ 系统而不必修改它们。并行运行旧系统和新系统通常比在现有系统中执行复杂的模式迁移更容易。一旦旧的系统不再需要,你可以简单地关闭它并回收它的资源【47,57】。
-如果您不必担心如何查询和访问数据,那么存储数据通常是非常简单的。模式设计,索引和存储引擎的许多复杂性都是希望支持某些查询和访问模式的结果(参见[第3章](ch3.md))。出于这个原因,通过将数据写入的形式与读取形式分开,并允许几个不同的读取视图,可以获得很大的灵活性。这个想法有时被称为命令查询责任分离(CQRS)【42,58,59】。
+ 如果您不必担心如何查询和访问数据,那么存储数据通常是非常简单的。模式设计,索引和存储引擎的许多复杂性都是希望支持某些查询和访问模式的结果(参见[第3章](ch3.md))。出于这个原因,通过将数据写入的形式与读取形式分开,并允许几个不同的读取视图,可以获得很大的灵活性。这个想法有时被称为命令查询责任分离(CQRS)【42,58,59】。
-数据库和模式设计的传统方法是基于数据必须以与查询相同的形式写入的谬误。有关正常化和非规范化的争论(参阅“[多对一和多对多的关系](ch2.md#多对一和多对多的关系)”),如果可以将数据从写入优化的事件日志转换为读取优化的应用程序状态,则变得基本无关紧要:在读取优化的视图中对数据进行非规范化是完全合理的,因为翻译过程为您提供了一种机制,使其与事件日志保持一致。
+ 数据库和模式设计的传统方法是基于数据必须以与查询相同的形式写入的谬误。有关正常化和非规范化的争论(参阅“[多对一和多对多的关系](ch2.md#多对一和多对多的关系)”),如果可以将数据从写入优化的事件日志转换为读取优化的应用程序状态,则变得基本无关紧要:在读取优化的视图中对数据进行非规范化是完全合理的,因为翻译过程为您提供了一种机制,使其与事件日志保持一致。
-在“[描述负载](ch1.md#描述负载)”中,我们讨论了推特主页时间表,最近一个特定用户正在关注的人(如邮箱)写的最近发布的推文缓存。这是阅读优化状态的另一个例子:家庭时间表高度变形,因为你的推文在所有跟随你的人的时间线上都是重复的。然而,扇出服务保持这种复制状态与新的推文和新的以下关系保持同步,这保持了复制的可管理性。
+ 在“[描述负载](ch1.md#描述负载)”中,我们讨论了推特主页时间表,最近一个特定用户正在关注的人(如邮箱)写的最近发布的推文缓存。这是阅读优化状态的另一个例子:家庭时间表高度变形,因为你的推文在所有跟随你的人的时间线上都是重复的。然而,扇出服务保持这种复制状态与新的推文和新的以下关系保持同步,这保持了复制的可管理性。
#### 并发控制
-事件采集和更改数据捕获的最大缺点是事件日志的消费者通常是异步的,所以用户可能会写入日志,然后从日志派生的视图中读取并查找他们的写作还没有反映在读取视图。我们在“[读己之写](ch5.md#读己之写)”中讨论了这个问题和潜在的解决方案。
+ 事件采集和更改数据捕获的最大缺点是事件日志的消费者通常是异步的,所以用户可能会写入日志,然后从日志派生的视图中读取并查找他们的写作还没有反映在读取视图。我们在“[读己之写](ch5.md#读己之写)”中讨论了这个问题和潜在的解决方案。
-一种解决方案是同步执行读取视图的更新,并将事件附加到日志中。这需要一个事务来将写入操作合并到一个原子单元中,所以要么需要将事件日志和读取视图保存在同一个存储系统中,要么需要跨不同系统的分布式事务。或者,您可以使用在“[使用全序广播实现线性化存储](ch9.md#使用全序广播实现线性化存储)”中讨论的方法。
+ 一种解决方案是同步执行读取视图的更新,并将事件附加到日志中。这需要一个事务来将写入操作合并到一个原子单元中,所以要么需要将事件日志和读取视图保存在同一个存储系统中,要么需要跨不同系统的分布式事务。或者,您可以使用在“[使用全序广播实现线性化存储](ch9.md#使用全序广播实现线性化存储)”中讨论的方法。
-另一方面,从事件日志导出当前状态也简化了并发控制的某些方面。对多个对象事务的需求(参阅“[单对象和多对象操作](ch7.md#单对象和多对象操作)”)源于单个用户操作,需要在多个不同的位置更改数据。通过事件采购,您可以设计一个事件,以便对用户操作进行独立的描述。用户操作只需要在一个地方进行一次写操作,即将事件附加到日志中,这很容易使原子化。
+ 另一方面,从事件日志导出当前状态也简化了并发控制的某些方面。对多个对象事务的需求(参阅“[单对象和多对象操作](ch7.md#单对象和多对象操作)”)源于单个用户操作,需要在多个不同的位置更改数据。通过事件代理,您可以设计一个事件,以便对用户操作进行独立的描述。用户操作只需要在一个地方进行一次写操作,即将事件附加到日志中,这很容易使原子化。
-如果事件日志和应用程序状态以相同的方式分区(例如,为分区3中的客户处理事件只需要更新应用程序状态的分区3),则直接的单线程日志消费者不需要并发控制(write-by)构造,它一次只处理一个事件(参阅“[真的的串行执行](ch7.md#真的的串行执行)”)。该日志通过在分区中定义事件的串行顺序来消除并发性的不确定性【24】。如果一个事件触及多个状态分区,那么需要做更多的工作,我们将在[第12章](ch12.md)讨论。
+ 如果事件日志和应用程序状态以相同的方式分区(例如,为分区3中的客户处理事件只需要更新应用程序状态的分区3),则直接的单线程日志消费者不需要并发控制(write-by)构造,它一次只处理一个事件(参阅“[真的的串行执行](ch7.md#真的的串行执行)”)。该日志通过在分区中定义事件的串行顺序来消除并发性的不确定性【24】。如果一个事件触及多个状态分区,那么需要做更多的工作,我们将在[第12章](ch12.md)讨论。
#### 不变性的限制
-许多不使用事件源模型的系统依赖于不可变性:各种数据库在内部使用不可变的数据结构或多版本数据来支持时间点快照(参见“[索引和快照隔离](ch7.md#索引和快照隔离)” )。 Git,Mercurial和Fossil等版本控制系统也依靠不可变的数据来保存文件的版本历史记录。
+ 许多不使用事件源模型的系统依赖于不可变性:各种数据库在内部使用不可变的数据结构或多版本数据来支持时间点快照(参见“[索引和快照隔离](ch7.md#索引和快照隔离)” )。 Git,Mercurial和Fossil等版本控制系统也依靠不可变的数据来保存文件的版本历史记录。
-永远保持所有变化的不变的历史在多大程度上是可行的?答案取决于数据集中的流失量。一些工作负载主要是添加数据,很少更新或删除;他们很容易使不变。其他工作负载在较小的数据集上有较高的更新和删除率;在这些情况下,不可改变的历史可能变得过于庞大,碎片化可能成为一个问题,压缩和垃圾收集的表现对于操作的鲁棒性变得至关重要【60,61】。
+ 永远保持所有变化的不变的历史在多大程度上是可行的?答案取决于数据集中的流失量。一些工作负载主要是添加数据,很少更新或删除;他们很容易使不变。其他工作负载在较小的数据集上有较高的更新和删除率;在这些情况下,不可改变的历史可能变得过于庞大,碎片化可能成为一个问题,压缩和垃圾收集的表现对于操作的鲁棒性变得至关重要【60,61】。
-除了性能方面的原因外,也可能出于管理方面的原因需要删除数据的情况,尽管这些数据都是不可变的。例如,隐私条例可能要求在关闭帐户后删除用户的个人信息,数据保护立法可能要求删除错误的信息,或者可能需要包含敏感信息的意外泄露。
+ 除了性能方面的原因外,也可能出于管理方面的原因需要删除数据的情况,尽管这些数据都是不可变的。例如,隐私条例可能要求在关闭帐户后删除用户的个人信息,数据保护立法可能要求删除错误的信息,或者可能需要包含敏感信息的意外泄露。
-在这种情况下,仅仅在日志中添加另一个事件来指示先前的数据应该被视为删除是不够的 —— 您实际上是想重写历史并假装数据从未写在第一位。例如,Datomic调用这个特性excision 【62】,而Fossil版本控制系统有一个类似的概念叫做shunning 【63】。
+ 在这种情况下,仅仅在日志中添加另一个事件来指示先前的数据应该被视为删除是不够的 —— 您实际上是想重写历史并假装数据从未写在第一位。例如,Datomic调用这个特性excision 【62】,而Fossil版本控制系统有一个类似的概念叫做shunning 【63】。
-真正的删除数据是非常困难的【64】,因为拷贝可以存在于很多地方:例如,存储引擎,文件系统和SSD通常写入一个新的位置,而不是覆盖到位【52】,而备份通常是故意不可改变的防止意外删除或腐败。删除更多的是“使检索数据更难”,而不是“使检索数据不可能”。无论如何,有时您必须尝试,正如我们在“[立法和自律](ch12.md#立法和自律)”中所看到的。
+ 真正的删除数据是非常困难的【64】,因为拷贝可以存在于很多地方:例如,存储引擎,文件系统和SSD通常写入一个新的位置,而不是覆盖到位【52】,而备份通常是故意不可改变的防止意外删除或腐败。删除更多的是“使检索数据更难”,而不是“使检索数据不可能”。无论如何,有时您必须尝试,正如我们在“[立法和自律](ch12.md#立法和自律)”中所看到的。
@@ -431,9 +431,9 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
在本章的其余部分中,我们将讨论选项3:处理流以产生其他派生流。处理这样的流的代码片段被称为操作员或作业。它与我们在[第10章](ch10.md)中讨论过的Unix进程和MapReduce作业密切相关,数据流的模式是相似的:一个流处理器以只读的方式使用输入流,并将其输出写入一个不同的位置时尚。
-流处理器中的分区和并行化模式也非常类似于[第10章](ch10.md)中介绍的MapReduce和数据流引擎,因此我们不在这里重复这些主题。基本的映射操作(如转换和过滤记录)也是一样的。
+ 流处理器中的分区和并行化模式也非常类似于[第10章](ch10.md)中介绍的MapReduce和数据流引擎,因此我们不在这里重复这些主题。基本的映射操作(如转换和过滤记录)也是一样的。
-批量作业的一个关键区别是流不会结束。这种差别有很多含义:正如本章开始部分所讨论的,排序对无界数据集没有意义,因此不能使用排序合并联接(请参阅“[减少连接和分组](ch10.md#减少连接和分组)”)。容错机制也必须改变:对于已经运行了几分钟的批处理作业,可以简单地从头开始重新启动失败的任务,但是对于已经运行数年的流作业,在开始后重新开始崩溃可能不是一个可行的选择。
+ 批量作业的一个关键区别是流不会结束。这种差别有很多含义:正如本章开始部分所讨论的,排序对无界数据集没有意义,因此不能使用排序合并联接(请参阅“[减少连接和分组](ch10.md#减少连接和分组)”)。容错机制也必须改变:对于已经运行了几分钟的批处理作业,可以简单地从头开始重新启动失败的任务,但是对于已经运行数年的流作业,在开始后重新开始崩溃可能不是一个可行的选择。
### 流处理的应用
@@ -448,17 +448,17 @@ Kafka Connect 【41】致力于将广泛的数据库系统的变更数据捕获
#### 复杂的事件处理
-复杂事件处理(CEP)是20世纪90年代为分析事件流而开发的一种方法,尤其适用于需要搜索某些事件模式的应用程序【65,66】。与正则表达式允许您在字符串中搜索特定字符模式的方式类似,CEP允许您指定规则以在流中搜索某些事件模式。
+ 复杂事件处理(CEP)是20世纪90年代为分析事件流而开发的一种方法,尤其适用于需要搜索某些事件模式的应用程序【65,66】。与正则表达式允许您在字符串中搜索特定字符模式的方式类似,CEP允许您指定规则以在流中搜索某些事件模式。
-CEP系统通常使用高级声明式查询语言(如SQL或图形用户界面)来描述应该检测到的事件模式。这些查询被提交给一个处理引擎,该引擎使用输入流并在内部维护一个执行所需匹配的状态机。当发现匹配时,引擎发出一个复杂的事件(因此名字)与事件模式的细节【67】。
+ CEP系统通常使用高级声明式查询语言(如SQL或图形用户界面)来描述应该检测到的事件模式。这些查询被提交给一个处理引擎,该引擎使用输入流并在内部维护一个执行所需匹配的状态机。当发现匹配时,引擎发出一个复杂的事件(因此名字)与事件模式的细节【67】。
-在这些系统中,查询和数据之间的关系与普通数据库相比是颠倒的。通常情况下,数据库会持久存储数据,并将查询视为暂时的:当查询进入时,数据库搜索与查询匹配的数据,然后在查询完成时忘记查询。 CEP引擎反转了这些角色:查询是长期存储的,来自输入流的事件不断流过它们,以搜索匹配事件模式的查询【68】。
+ 在这些系统中,查询和数据之间的关系与普通数据库相比是颠倒的。通常情况下,数据库会持久存储数据,并将查询视为暂时的:当查询进入时,数据库搜索与查询匹配的数据,然后在查询完成时忘记查询。 CEP引擎反转了这些角色:查询是长期存储的,来自输入流的事件不断流过它们,以搜索匹配事件模式的查询【68】。
-CEP的实现包括Esper 【69】,IBM InfoSphere Streams 【70】,Apama,TIBCO StreamBase和SQLstream。像Samza这样的分布式流处理器也获得了对流声明式查询的SQL支持【71】。
+ CEP的实现包括Esper 【69】,IBM InfoSphere Streams 【70】,Apama,TIBCO StreamBase和SQLstream。像Samza这样的分布式流处理器也获得了对流声明式查询的SQL支持【71】。
#### 流分析
-使用流处理的另一个领域是对流进行分析。 CEP和流分析之间的边界是模糊的,但作为一般规则,分析往往不太关心找到特定的事件序列,并且更倾向于聚合和统计度量大量的事件——例如:
+ 使用流处理的另一个领域是对流进行分析。 CEP和流分析之间的边界是模糊的,但作为一般规则,分析往往不太关心找到特定的事件序列,并且更倾向于聚合和统计度量大量的事件——例如:
* 测量某种类型事件的速率(每个时间间隔发生的频率)
* 计算一段时间内某个值的滚动平均值
@@ -466,31 +466,29 @@ CEP的实现包括Esper 【69】,IBM InfoSphere Streams 【70】,Apama,TIB
这些统计信息通常是在固定的时间间隔内进行计算的,例如,您可能想知道在过去5分钟内每秒对服务的平均查询次数,以及在此期间的第99百分位响应时间。在几分钟内平均,从一秒钟到下一秒钟平滑无关的波动,同时还能及时了解交通模式的任何变化。您汇总的时间间隔称为窗口,我们将在“[关于时间的推理](#关于时间的推理)”中更详细地讨论窗口。
+ 流分析系统有时使用概率算法,例如Bloom filter(我们在“[性能优化](ch3.md#性能优化)”中遇到过),设置成员资格,HyperLogLog 【72】基数估计以及各种百分比估计算法(请参阅“[实践中的百分位点](ch1.md#实践中的百分位点)“第16页)。概率算法产生近似的结果,但是具有在流处理器中比精确算法需要少得多的存储器的优点。近似算法的使用有时会使人们相信流处理系统总是有损和不精确的,但这是错误的:流处理没有任何内在的近似,而概率算法只是一个优化【73】。
-
-流分析系统有时使用概率算法,例如Bloom filter(我们在“[性能优化](ch3.md#性能优化)”中遇到过),设置成员资格,HyperLogLog 【72】基数估计以及各种百分比估计算法(请参阅“[实践中的百分位点](ch1.md#实践中的百分位点)“第16页)。概率算法产生近似的结果,但是具有在流处理器中比精确算法需要少得多的存储器的优点。近似算法的使用有时会使人们相信流处理系统总是有损和不精确的,但这是错误的:流处理没有任何内在的近似,而概率算法只是一个优化【73】。
-
-许多开源分布式流处理框架的设计都是以分析为基础的:例如Apache Storm,Spark Streaming,Flink,Concord,Samza和Kafka Streams 【74】。托管服务包括Google Cloud Dataflow和Azure Stream Analytics。
+ 许多开源分布式流处理框架的设计都是以分析为基础的:例如Apache Storm,Spark Streaming,Flink,Concord,Samza和Kafka Streams 【74】。托管服务包括Google Cloud Dataflow和Azure Stream Analytics。
#### 保持物化视图
-我们在“[数据库和数据流](#数据库和数据流)”中看到,可以使用数据库更改流来保持派生数据系统(如缓存,搜索索引和数据仓库)与源数据库保持最新。我们可以将这些示例视为维护实体化视图的具体情况(请参阅“[聚合:数据立方体和物化视图](ch3.md#聚合:数据立方体和物化视图)”):导出某个数据集的替代视图,以便可以高效地查询它,并在底层数据更改【50】。
+ 我们在“[数据库和数据流](#数据库和数据流)”中看到,可以使用数据库更改流来保持派生数据系统(如缓存,搜索索引和数据仓库)与源数据库保持最新。我们可以将这些示例视为维护实体化视图的具体情况(请参阅“[聚合:数据立方体和物化视图](ch3.md#聚合:数据立方体和物化视图)”):导出某个数据集的替代视图,以便可以高效地查询它,并在底层数据更改【50】。
-同样,在事件采购中,应用程序状态通过应用事件日志来维护;这里的应用状态也是一种物化视图。与流分析场景不同,在某个时间窗口内仅考虑事件通常是不够的:构建物化视图可能需要在任意时间段内的所有事件,除了可能由日志压缩丢弃的任何过时事件(请参阅“[日志压缩](#日志压缩)“)。实际上,您需要一个可以一直延伸到一开始的窗口。
+ 同样,在事件代理中,应用程序状态通过应用事件日志来维护;这里的应用状态也是一种物化视图。与流分析场景不同,在某个时间窗口内仅考虑事件通常是不够的:构建物化视图可能需要在任意时间段内的所有事件,除了可能由日志压缩丢弃的任何过时事件(请参阅“[日志压缩](#日志压缩)“)。实际上,您需要一个可以一直延伸到一开始的窗口。
-原则上,任何流处理器都可以用于物化视图维护,尽管永久维护事件的需要与一些主要在有限持续时间的窗口上运行的面向分析的框架的假设背道而驰。 Samza和Kafka Streams支持这种用法,建立在Kafka对夯实的支持上【75】。
+ 原则上,任何流处理器都可以用于物化视图维护,尽管永久维护事件的需要与一些主要在有限持续时间的窗口上运行的面向分析的框架的假设背道而驰。 Samza和Kafka Streams支持这种用法,建立在Kafka对夯实的支持上【75】。
#### 在流上搜索
-除了允许搜索由多个事件组成的模式的CEP外,还有时需要基于复杂的标准(例如全文搜索查询)来搜索单个事件。
+ 除了允许搜索由多个事件组成的模式的CEP外,还有时需要基于复杂的标准(例如全文搜索查询)来搜索单个事件。
-例如,媒体监测服务可以订阅新闻文章和媒体广播,并搜索任何关于公司,产品或感兴趣的话题的新闻。这是通过预先制定一个搜索查询来完成的,然后不断地将新闻项目流与这个查询进行匹配。在一些网站上也有类似的功能:例如,房地产网站的用户在市场上出现符合其搜索条件的新房产时,可以要求通知。 Elasticsearch 【76】的渗滤器功能是实现这种流式搜索的一种选择。
+ 例如,媒体监测服务可以订阅新闻文章和媒体广播,并搜索任何关于公司,产品或感兴趣的话题的新闻。这是通过预先制定一个搜索查询来完成的,然后不断地将新闻项目流与这个查询进行匹配。在一些网站上也有类似的功能:例如,房地产网站的用户在市场上出现符合其搜索条件的新房产时,可以要求通知。 Elasticsearch 【76】的渗滤器功能是实现这种流式搜索的一种选择。
-传统的搜索引擎首先索引文件,然后在索引上运行查询。相比之下,搜索一个数据流将会处理它的头部:查询被存储,文档通过查询运行,就像CEP一样。在最简单的情况下,您可以针对每个查询来测试每个文档,但是如果您有大量查询,这可能会变慢。为了优化过程,可以对查询和文档进行索引,从而缩小可能匹配的查询集合【77】。
+ 传统的搜索引擎首先索引文件,然后在索引上运行查询。相比之下,搜索一个数据流将会处理它的头部:查询被存储,文档通过查询运行,就像CEP一样。在最简单的情况下,您可以针对每个查询来测试每个文档,但是如果您有大量查询,这可能会变慢。为了优化过程,可以对查询和文档进行索引,从而缩小可能匹配的查询集合【77】。
#### 消息传递和RPC
-在第136页的“[消息传递数据流](ch4.md#消息传递数据流)”中,我们讨论了消息传递系统作为RPC的替代方案,即作为通信服务的机制,例如在参与者模型中所使用的。虽然这些系统也是基于消息和事件,但我们通常不会将它们视为流处理器:
+ 在第136页的“[消息传递数据流](ch4.md#消息传递数据流)”中,我们讨论了消息传递系统作为RPC的替代方案,即作为通信服务的机制,例如在参与者模型中所使用的。虽然这些系统也是基于消息和事件,但我们通常不会将它们视为流处理器:
Actor框架主要是管理通信模块的并发和分布式执行的机制,而流处理主要是数据管理技术。
@@ -499,29 +497,29 @@ Actor框架主要是管理通信模块的并发和分布式执行的机制,而
也就是说,RPC类系统和流处理之间有一些交叉区域。例如,Apache Storm有一个称为分布式RPC的功能,它允许将用户查询分散到一系列也处理事件流的节点上;这些查询然后与来自输入流的事件交织,结果可以被汇总并发回给用户【78】。 (另参阅“[多分区数据处理](ch12.md#多分区数据处理)”)
-也可以使用actor框架来处理流。但是,很多这样的框架在崩溃的情况下不能保证消息的传递,所以这个过程不是容错的,除非你实现了额外的重试逻辑。
+ 也可以使用actor框架来处理流。但是,很多这样的框架在崩溃的情况下不能保证消息的传递,所以这个过程不是容错的,除非你实现了额外的重试逻辑。
### 关于时间的推理
-流处理器通常需要处理时间,特别是在用于分析目的的时候,频繁使用时间窗口,例如“过去五分钟的平均时间”。“最后五分钟”的含义似乎应该是未知的,大而清晰,但不幸的是这个概念是令人惊讶的棘手。
+ 流处理器通常需要处理时间,特别是在用于分析目的的时候,频繁使用时间窗口,例如“过去五分钟的平均时间”。“最后五分钟”的含义似乎应该是未知的,大而清晰,但不幸的是这个概念是令人惊讶的棘手。
-在批处理过程中,处理任务通过大量的历史事件迅速收缩。如果需要按时间分类,批处理需要查看每个事件中嵌入的时间戳。查看运行批处理的机器的系统时钟没有意义,因为处理运行的时间与事件实际发生的时间无关。
+ 在批处理过程中,处理任务通过大量的历史事件迅速收缩。如果需要按时间分类,批处理需要查看每个事件中嵌入的时间戳。查看运行批处理的机器的系统时钟没有意义,因为处理运行的时间与事件实际发生的时间无关。
-批处理可以在几分钟内读取一年的历史事件;在大多数情况下,感兴趣的时间表是历史的一年,而不是几分钟的处理。而且,在事件中使用时间戳允许处理确定性的:在相同的输入上再次运行相同的处理过程会得到相同的结果(参阅“[故障容错](ch10.md#故障容错)”)。
+ 批处理可以在几分钟内读取一年的历史事件;在大多数情况下,感兴趣的时间表是历史的一年,而不是几分钟的处理。而且,在事件中使用时间戳允许处理确定性的:在相同的输入上再次运行相同的处理过程会得到相同的结果(参阅“[故障容错](ch10.md#故障容错)”)。
-另一方面,许多流处理框架使用处理机器上的本地系统时钟(处理时间)来确定窗口【79】。这种方法具有简单的优点,事件创建和事件处理之间的延迟可以忽略不计。然而,如果存在任何显着的处理滞后,即处理可能比事件实际发生的时间显着晚,则会中断处理。
+ 另一方面,许多流处理框架使用处理机器上的本地系统时钟(处理时间)来确定窗口【79】。这种方法具有简单的优点,事件创建和事件处理之间的延迟可以忽略不计。然而,如果存在任何显着的处理滞后,即处理可能比事件实际发生的时间显着晚,则会中断处理。
#### 事件时间与处理时间
-有许多原因可能会延迟处理:排队,网络故障(请参阅第267页的“[不可靠的网络](ch8.md#不可靠的网络)”),导致消息代理或处理器中出现争用的性能问题,重新启动流消费者或重新处理过去的事件(参阅“[重播旧消息](#重播旧消息)”),或者在修复代码中的错误之后进行恢复。
+ 有许多原因可能会延迟处理:排队,网络故障(请参阅第267页的“[不可靠的网络](ch8.md#不可靠的网络)”),导致消息代理或处理器中出现争用的性能问题,重新启动流消费者或重新处理过去的事件(参阅“[重播旧消息](#重播旧消息)”),或者在修复代码中的错误之后进行恢复。
-而且,消息延迟还可能导致消息的不可预知的排序。例如,假设用户首先发出一个Web请求(由Web服务器A处理),然后发出第二个请求(由服务器B处理)。 A和B发出描述他们处理的请求的事件,但是B的事件在A的事件发生之前到达消息代理。现在,流处理器将首先看到B事件,然后看到A事件,即使它们实际上是以相反的顺序发生的。
+ 而且,消息延迟还可能导致消息的不可预知的排序。例如,假设用户首先发出一个Web请求(由Web服务器A处理),然后发出第二个请求(由服务器B处理)。 A和B发出描述他们处理的请求的事件,但是B的事件在A的事件发生之前到达消息代理。现在,流处理器将首先看到B事件,然后看到A事件,即使它们实际上是以相反的顺序发生的。
-如果有一个类比的话,可以考虑一下“星球大战”的电影:第四集于1977年发行,1980年第五集,1983年第六集,之后分别在1999年,2002年和2005年发行第一,二,三集,以及2015年的第七集【80】[^ii]。如果你按照他们出来的顺序观看电影,你处理电影的顺序与他们叙述的顺序是不一致的。 (情节编号就像事件时间戳一样,观看电影的日期就是处理时间。)作为人类,我们能够应对这种不连续性,但是流处理算法需要专门编写以适应这种情况时间安排和订购问题。
+ 如果有一个类比的话,可以考虑一下“星球大战”的电影:第四集于1977年发行,1980年第五集,1983年第六集,之后分别在1999年,2002年和2005年发行第一,二,三集,以及2015年的第七集【80】[^ii]。如果你按照他们出来的顺序观看电影,你处理电影的顺序与他们叙述的顺序是不一致的。 (情节编号就像事件时间戳一样,观看电影的日期就是处理时间。)作为人类,我们能够应对这种不连续性,但是流处理算法需要专门编写以适应这种情况时间安排和订购问题。
[^ii]: 感谢Flink社区的Kostas Kloudas提出这个比喻。
-令人困惑的事件时间和处理时间导致错误的数据。例如,假设您有一个流处理器来测量请求率(计算每秒请求数)。如果您重新部署流处理器,则可能会关闭一分钟,并在事件恢复时处理积压的事件。如果您根据处理时间来衡量速率,那么看起来好像在处理积压时突然出现异常的请求高峰,而事实上请求的实际速率是稳定的([图11-7](img/fig11-7.png))。
+ 令人困惑的事件时间和处理时间导致错误的数据。例如,假设您有一个流处理器来测量请求率(计算每秒请求数)。如果您重新部署流处理器,则可能会关闭一分钟,并在事件恢复时处理积压的事件。如果您根据处理时间来衡量速率,那么看起来好像在处理积压时突然出现异常的请求高峰,而事实上请求的实际速率是稳定的([图11-7](img/fig11-7.png))。

@@ -529,11 +527,11 @@ Actor框架主要是管理通信模块的并发和分布式执行的机制,而
#### 知道什么时候你准备好了
-从事件时间的角度来定义窗口时,一个棘手的问题是,当你收到特定窗口的所有事件,或者是否还有事件发生时,你永远无法确定。
+ 从事件时间的角度来定义窗口时,一个棘手的问题是,当你收到特定窗口的所有事件,或者是否还有事件发生时,你永远无法确定。
-例如,假设您将事件分组为一分钟的窗口,以便您可以统计每分钟的请求数。你已经计算了一些事件,这些事件的时间戳是在第37分钟的时间落下的,时间已经推移了。现在大部分的事件都在一小时的第38和第39分钟之内。你什么时候宣布你已经完成了第37分钟的窗口,并输出其计数器值?
+ 例如,假设您将事件分组为一分钟的窗口,以便您可以统计每分钟的请求数。你已经计算了一些事件,这些事件的时间戳是在第37分钟的时间落下的,时间已经推移了。现在大部分的事件都在一小时的第38和第39分钟之内。你什么时候宣布你已经完成了第37分钟的窗口,并输出其计数器值?
-在一段时间没有看到任何新的事件之后,您可以超时并宣布一个窗口,但仍然可能发生某些事件被缓存在另一台计算机上,由于网络中断而延迟。您需要能够处理窗口已经声明完成后到达的这样的滞留事件。大体上,你有两个选择【1】:
+ 在一段时间没有看到任何新的事件之后,您可以超时并宣布一个窗口,但仍然可能发生某些事件被缓存在另一台计算机上,由于网络中断而延迟。您需要能够处理窗口已经声明完成后到达的这样的滞留事件。大体上,你有两个选择【1】:
1. 忽略这些零散的事件,因为它们在正常情况下可能只是一小部分事件。您可以将丢弃事件的数量作为度量标准进行跟踪,并在您开始丢弃大量数据时发出警报。
2. 发布一个更正,更新的窗口与包含散兵队员的价值。您可能还需要收回以前的输出。
@@ -542,11 +540,11 @@ Actor框架主要是管理通信模块的并发和分布式执行的机制,而
#### 你用的是什么时间?
-当事件可以在系统中的多个点缓冲时,为事件分配时间戳更加困难。例如,考虑将使用率度量的事件报告给服务器的移动应用程序。该应用程序可能会在设备处于脱机状态时使用,在这种情况下,它将在设备上本地缓冲事件,并在下一次可用的互联网连接(可能是几小时甚至几天)时将它们发送到服务器。对于这个流的任何消费者来说,这些事件将显示为极其滞后的落后者。
+ 当事件可以在系统中的多个点缓冲时,为事件分配时间戳更加困难。例如,考虑将使用率度量的事件报告给服务器的移动应用程序。该应用程序可能会在设备处于脱机状态时使用,在这种情况下,它将在设备上本地缓冲事件,并在下一次可用的互联网连接(可能是几小时甚至几天)时将它们发送到服务器。对于这个流的任何消费者来说,这些事件将显示为极其滞后的落后者。
-在这种情况下,根据移动设备的本地时钟,事件的时间戳实际上应该是发生用户交互的时间。但是,用户控制的设备上的时钟通常是不可信的,因为它可能会被意外或故意设置为错误的时间(请参见“[时钟同步与准确性](ch8.md#时钟同步与准确性)”)。服务器收到事件的时间(根据服务器的时钟)更可能是准确的,因为服务器在您的控制之下,但在描述用户交互方面意义不大。
+ 在这种情况下,根据移动设备的本地时钟,事件的时间戳实际上应该是发生用户交互的时间。但是,用户控制的设备上的时钟通常是不可信的,因为它可能会被意外或故意设置为错误的时间(请参见“[时钟同步与准确性](ch8.md#时钟同步与准确性)”)。服务器收到事件的时间(根据服务器的时钟)更可能是准确的,因为服务器在您的控制之下,但在描述用户交互方面意义不大。
-要调整不正确的设备时钟,一种方法是记录三个时间戳【82】:
+ 要调整不正确的设备时钟,一种方法是记录三个时间戳【82】:
* 事件发生的时间,根据设备时钟
* 根据设备时钟将事件发送到服务器的时间
@@ -554,60 +552,60 @@ Actor框架主要是管理通信模块的并发和分布式执行的机制,而
通过从第三个时间戳中减去第二个时间戳,可以估算设备时钟和服务器时钟之间的偏移量(假设网络延迟与所需的时间戳精度相比可忽略不计)。然后,您可以将该偏移量应用于事件时间戳,从而估计事件实际发生的真实时间(假设设备时钟偏移在事件发生的时间与发送到服务器的时间之间没有变化)。
-这个问题对于流处理来说并不是唯一的,批处理遇到了与时间推理完全相同的问题。在一个流式环境中,我们更加注意到时间的流逝。
+ 这个问题对于流处理来说并不是唯一的,批处理遇到了与时间推理完全相同的问题。在一个流式环境中,我们更加注意到时间的流逝。
#### 窗口的类型
-一旦你知道如何确定一个事件的时间戳,下一步就是决定如何定义一段时间的窗口。窗口然后可以用于聚合,例如计数事件,或计算窗口内的值的平均值。有几种窗口是常用的【79,83】:
+ 一旦你知道如何确定一个事件的时间戳,下一步就是决定如何定义一段时间的窗口。窗口然后可以用于聚合,例如计数事件,或计算窗口内的值的平均值。有几种窗口是常用的【79,83】:
-***Tumbling窗口***
+***滚动窗口(Tumbling Window)***
-一个翻滚的窗口有一个固定的长度,每个事件都属于一个窗口。例如,如果您有1分钟的翻滚窗口,则所有时间戳在`10:03:00`和`10:03:59`之间的事件会被分组到一个窗口中,`10:04:00`和`10:04:59`之间的事件下一个窗口,等等。您可以通过获取每个事件时间戳并将其四舍五入到最接近的分钟来确定它所属的窗口,从而实现1分钟的翻滚窗口。
+ 一个滚动窗口有一个固定的长度,每个事件都属于一个窗口。例如,如果您有1分钟的翻滚窗口,则所有时间戳在`10:03:00`和`10:03:59`之间的事件会被分组到一个窗口中,`10:04:00`和`10:04:59`之间的事件下一个窗口,等等。您可以通过获取每个事件时间戳并将其四舍五入到最接近的分钟来确定它所属的窗口,从而实现1分钟的翻滚窗口。
-***Hopping窗***
+***跳动窗口(Hopping Window)***
-跳频窗口也具有固定的长度,但允许窗口重叠以提供一些平滑。例如,1分钟跳跃大小的5分钟窗口将包含`10:03:00`至`10:07:59`之间的事件,则下一个窗口将覆盖`10:04:00`至`10:08`之间的事件: 59,等等。您可以通过首先计算1分钟滚动窗口,然后聚合在几个相邻的窗口上来实现此跳频窗口。
+ 跳频窗口也具有固定的长度,但允许窗口重叠以提供一些平滑。例如,1分钟跳跃大小的5分钟窗口将包含`10:03:00`至`10:07:59`之间的事件,则下一个窗口将覆盖`10:04:00`至`10:08`之间的事件: 59,等等。您可以通过首先计算1分钟滚动窗口,然后聚合在几个相邻的窗口上来实现此跳频窗口。
***滑动窗口***
-滑动窗口包含在彼此的某个间隔内发生的所有事件。例如,一个5分钟的滑动窗口将覆盖10点03分39秒和10点08分12秒的事件,因为它们相距不到5分钟(注意翻滚和跳跃的5分钟窗口不会把这两个事件在同一个窗口中,因为他们使用固定的边界)。滑动窗口可以通过保持按时间排序的事件缓冲区并在从窗口到期时移除旧事件来实现。
+ 滑动窗口包含在彼此的某个间隔内发生的所有事件。例如,一个5分钟的滑动窗口将覆盖10点03分39秒和10点08分12秒的事件,因为它们相距不到5分钟(注意翻滚和跳跃的5分钟窗口不会把这两个事件在同一个窗口中,因为他们使用固定的边界)。滑动窗口可以通过保持按时间排序的事件缓冲区并在从窗口到期时移除旧事件来实现。
***会话窗口***
-与其他窗口类型不同,会话窗口没有固定的持续时间。相反,它是通过将同一用户的所有事件分组在一起,并在时间上紧密地组合在一起来定义的,并且当用户在一段时间内不活动时(例如,如果30分钟内没有事件),窗口结束。会话化是网站分析的常见要求(参阅“[GROUP BY](ch10.md#GROUP BY)”)。
+ 与其他窗口类型不同,会话窗口没有固定的持续时间。相反,它是通过将同一用户的所有事件分组在一起,并在时间上紧密地组合在一起来定义的,并且当用户在一段时间内不活动时(例如,如果30分钟内没有事件),窗口结束。会话化是网站分析的常见要求(参阅“[GROUP BY](ch10.md#GROUP BY)”)。
### 流式连接
-在[第10章](ch10.md)中,我们讨论了批处理作业如何通过关键连接数据集,以及这种连接如何构成数据管道的重要组成部分。由于流处理将数据管道概括为对无界数据集进行增量处理,因此对流进行连接的需求也完全相同。
+ 在[第10章](ch10.md)中,我们讨论了批处理作业如何通过关键连接数据集,以及这种连接如何构成数据管道的重要组成部分。由于流处理将数据管道概括为对无界数据集进行增量处理,因此对流进行连接的需求也完全相同。
-然而,新事件随时可能出现在一个流中,这使得加入流比批处理作业更具挑战性。为了更好地理解情况,我们来区分三种不同类型的连接:流-流连接,流表连接和表连接【84】。在下面的章节中,我们将通过例子来说明。
+ 然而,新事件随时可能出现在一个流中,这使得加入流比批处理作业更具挑战性。为了更好地理解情况,我们来区分三种不同类型的连接:流-流连接,流表连接和表连接【84】。在下面的章节中,我们将通过例子来说明。
流 - 流连接(窗口连接)
-假设您的网站上有搜索功能,并且想要检测搜索到的网址的近期趋势。每次有人输入搜索查询时,都会记录包含查询和返回结果的事件。每当有人点击其中一个搜索结果时,就会记录另一个记录点击的事件。为了计算搜索结果中每个网址的点击率,您需要将搜索操作和点击操作的事件组合在一起,这些事件通过具有相同的会话ID进行连接。广告系统需要类似的分析【85】。
+ 假设您的网站上有搜索功能,并且想要检测搜索到的网址的近期趋势。每次有人输入搜索查询时,都会记录包含查询和返回结果的事件。每当有人点击其中一个搜索结果时,就会记录另一个记录点击的事件。为了计算搜索结果中每个网址的点击率,您需要将搜索操作和点击操作的事件组合在一起,这些事件通过具有相同的会话ID进行连接。广告系统需要类似的分析【85】。
-如果用户放弃他们的搜索,点击可能永远不会到来,即使它到了,搜索和点击之间的时间可能是高度可变的:在很多情况下,它可能是几秒钟,但可能长达几天或几周(如果用户运行搜索,忘记关于该浏览器选项卡,然后返回到选项卡,稍后再单击一个结果)。由于可变的网络延迟,点击事件甚至可能在搜索事件之前到达。您可以选择合适的加入窗口,例如,如果间隔至多一小时发生一次搜索,您可以选择加入搜索。
+ 如果用户放弃他们的搜索,点击可能永远不会到来,即使它到了,搜索和点击之间的时间可能是高度可变的:在很多情况下,它可能是几秒钟,但可能长达几天或几周(如果用户运行搜索,忘记关于该浏览器选项卡,然后返回到选项卡,稍后再单击一个结果)。由于可变的网络延迟,点击事件甚至可能在搜索事件之前到达。您可以选择合适的加入窗口,例如,如果间隔至多一小时发生一次搜索,您可以选择加入搜索。
-请注意,在click事件中嵌入搜索的细节并不等同于加入事件:这样做只会告诉您有关用户单击搜索结果的情况,而不是用户未点击任何搜索结果的搜索结果。为了衡量搜索质量,您需要准确的点击率,为此您需要搜索事件和点击事件。
+ 请注意,在click事件中嵌入搜索的细节并不等同于加入事件:这样做只会告诉您有关用户单击搜索结果的情况,而不是用户未点击任何搜索结果的搜索结果。为了衡量搜索质量,您需要准确的点击率,为此您需要搜索事件和点击事件。
-为了实现这种类型的连接,流处理器需要维护状态:例如,在最后一小时发生的所有事件,都由会话标识索引。无论何时发生搜索事件或点击事件,都会将其添加到适当的索引,并且流处理器还检查另一个索引,以查看是否已经到达同一会话ID的另一个事件。如果有匹配的事件,则发出说明哪个搜索结果被点击的事件。如果搜索事件过期而没有看到匹配的点击事件,则会发出说明哪些搜索结果未被点击的事件。
+ 为了实现这种类型的连接,流处理器需要维护状态:例如,在最后一小时发生的所有事件,都由会话标识索引。无论何时发生搜索事件或点击事件,都会将其添加到适当的索引,并且流处理器还检查另一个索引,以查看是否已经到达同一会话ID的另一个事件。如果有匹配的事件,则发出说明哪个搜索结果被点击的事件。如果搜索事件过期而没有看到匹配的点击事件,则会发出说明哪些搜索结果未被点击的事件。
#### 流表连接(stream enrichment)
-在“[示例:用户活动事件分析](ch10.md#示例:用户活动事件分析)”([图10-2](img/fig10-2.png))中,我们看到了加入两个数据集的批量作业示例:一组用户活动事件和一个用户配置文件数据库。将用户活动事件视为流,并在流处理器中连续执行相同的连接是很自然的:输入是包含用户ID的活动事件流,并且输出是活动事件流,其中用户ID已经用关于用户的简档信息来扩充。这个过程有时被称为使用来自数据库的信息来丰富活动事件。
+ 在“[示例:用户活动事件分析](ch10.md#示例:用户活动事件分析)”([图10-2](img/fig10-2.png))中,我们看到了加入两个数据集的批量作业示例:一组用户活动事件和一个用户配置文件数据库。将用户活动事件视为流,并在流处理器中连续执行相同的连接是很自然的:输入是包含用户ID的活动事件流,并且输出是活动事件流,其中用户ID已经用关于用户的简档信息来扩充。这个过程有时被称为使用来自数据库的信息来丰富活动事件。
-要执行此联接,流程过程需要一次查看一个活动事件,在数据库中查找事件的用户标识,并将该概要信息添加到活动事件中。数据库查询可以通过查询远程数据库来实现;但是,正如在“[示例:分析用户活动事件](ch10.md#示例:分析用户活动事件)”一节中讨论的,此类远程查询可能会很慢并且有可能导致数据库过载【75】。
+ 要执行此联接,流程过程需要一次查看一个活动事件,在数据库中查找事件的用户标识,并将该概要信息添加到活动事件中。数据库查询可以通过查询远程数据库来实现;但是,正如在“[示例:分析用户活动事件](ch10.md#示例:分析用户活动事件)”一节中讨论的,此类远程查询可能会很慢并且有可能导致数据库过载【75】。
-另一种方法是将数据库副本加载到流处理器中,以便在本地进行查询而无需网络往返。这种技术与我们在“[Map端连接](ch10.md#Map端连接)”中讨论的散列连接非常相似:如果数据库的本地副本足够小,则本地副本可能是内存中的散列表,或者是本地磁盘上的索引。
+ 另一种方法是将数据库副本加载到流处理器中,以便在本地进行查询而无需网络往返。这种技术与我们在“[Map端连接](ch10.md#Map端连接)”中讨论的散列连接非常相似:如果数据库的本地副本足够小,则本地副本可能是内存中的散列表,或者是本地磁盘上的索引。
-与批处理作业的区别在于批处理作业使用数据库的时间点快照作为输入,而流处理器是长时间运行的,并且数据库的内容可能随时间而改变,所以流处理器数据库的本地副本需要保持最新。这个问题可以通过更改数据捕获来解决:流处理器可以订阅用户配置文件数据库的更新日志以及活动事件流。在创建或修改配置文件时,流处理器会更新其本地副本。因此,我们获得两个流之间的连接:活动事件和配置文件更新。
+ 与批处理作业的区别在于批处理作业使用数据库的时间点快照作为输入,而流处理器是长时间运行的,并且数据库的内容可能随时间而改变,所以流处理器数据库的本地副本需要保持最新。这个问题可以通过更改数据捕获来解决:流处理器可以订阅用户配置文件数据库的更新日志以及活动事件流。在创建或修改配置文件时,流处理器会更新其本地副本。因此,我们获得两个流之间的连接:活动事件和配置文件更新。
-流表连接实际上非常类似于流 - 流连接;最大的区别在于对于表changelog流,连接使用一个可以回溯到“开始时间”(概念上是无限的窗口)的窗口,新版本的记录会覆盖较早的版本。对于流输入,连接可能根本没有维护窗口。
+ 流表连接实际上非常类似于流 - 流连接;最大的区别在于对于表changelog流,连接使用一个可以回溯到“开始时间”(概念上是无限的窗口)的窗口,新版本的记录会覆盖较早的版本。对于流输入,连接可能根本没有维护窗口。
#### 表格表连接(物化视图维护)
-考虑我们在“[描述负载](ch1.md#描述负载)”中讨论的推特时间线例子。我们说过,当用户想要查看他们的主页时间线时,对用户所关注的所有人进行迭代是非常昂贵的,推文,并合并它们。
+ 考虑我们在“[描述负载](ch1.md#描述负载)”中讨论的推特时间线例子。我们说过,当用户想要查看他们的主页时间线时,对用户所关注的所有人进行迭代是非常昂贵的,推文,并合并它们。
-相反,我们需要一个时间线缓存:一种每个用户的“收件箱”,在发送推文的时候写入这些信息,以便读取时间线是一次查询。实现和维护此缓存需要以下事件处理:
+ 相反,我们需要一个时间线缓存:一种每个用户的“收件箱”,在发送推文的时候写入这些信息,以便读取时间线是一次查询。实现和维护此缓存需要以下事件处理:
* 当用户发送新的推文时,它将被添加到每个跟随你的用户的时间线上。
* 用户删除推文时,将从所有用户的时间表中删除。
@@ -615,6 +613,7 @@ Actor框架主要是管理通信模块的并发和分布式执行的机制,而
* 当用户u1取消关注用户u2时,u1的推文将从u1的时间线中移除。
要在流处理器中实现这种缓存维护,需要用于推文(发送和删除)和跟随关系(跟随和取消跟随)的事件流。流过程需要维护一个包含每个用户关注者集合的数据库,以便知道当一个新的tweet到达时需要更新哪些时间轴【86】。
+
查看这个流过程的另一种方式是维护一个连接两个表(tweet和follow)的查询的物化视图,如下所示:
```sql
@@ -625,100 +624,97 @@ JOIN follows ON follows.followee_id = tweets.sender_id
GROUP BY follows.follower_id
```
-流的连接直接对应于该查询中的表的连接。时间轴实际上是这个查询结果的缓存,每当基础表发生变化时都会更新[^iii]。
+ 流的连接直接对应于该查询中的表的连接。时间轴实际上是这个查询结果的缓存,每当基础表发生变化时都会更新[^iii]。
[^iii]: 如果你把一个流视为一个表的衍生物,如[图11-6](img/fig11-6.png)所示,并且把一个连接看作是两个表u·v的乘积,那么会发生一些有趣的事情:物化连接的变化流遵循产品规则(u·v)'= u'v + uv'(u·v)'= u'v + uv'。 换句话说,任何tweets的变化都与当前的追随者联系在一起,任何追随者的变化都与当前的tweets 【49,50】相结合。
#### 连接的时间依赖性
-这就产生了一个问题:如果不同的事件发生在相似的时间周围,他们按照何种顺序进行处理?在流表连接示例中,如果用户更新其配置文件,哪些活动事件与旧配置文件(在配置文件更新之前处理)结合,哪些与新配置文件结合(在配置文件更新之后处理)?换句话说:如果状态随着时间的推移而改变,并且你加入了某个状态,那么你使用什么时间点来加入【45】?
+ 这就产生了一个问题:如果不同的事件发生在相似的时间周围,他们按照何种顺序进行处理?在流表连接示例中,如果用户更新其配置文件,哪些活动事件与旧配置文件(在配置文件更新之前处理)结合,哪些与新配置文件结合(在配置文件更新之后处理)?换句话说:如果状态随着时间的推移而改变,并且你加入了某个状态,那么你使用什么时间点来加入【45】?
-这种时间依赖性可能发生在许多地方。例如,如果您销售东西,则需要对发票进行适当的税率,这取决于国家或州,产品类型和销售日期(因为税率会随时变化)。将销售额加入税率表时,如果您正在重新处理历史数据,您可能希望加入销售时的税率,这可能与当前的税率不同。
+ 这种时间依赖性可能发生在许多地方。例如,如果您销售东西,则需要对发票进行适当的税率,这取决于国家或州,产品类型和销售日期(因为税率会随时变化)。将销售额加入税率表时,如果您正在重新处理历史数据,您可能希望加入销售时的税率,这可能与当前的税率不同。
-如果跨流的事件排序是未确定的,那么这个连接变得不确定【87】,这意味着你不能在相同的输入上重新运行相同的工作,并且必然会得到相同的结果:输入流上的事件可能交织在当你再次运行这个工作时,采用不同的方式
+ 如果跨流的事件排序是未确定的,那么这个连接变得不确定【87】,这意味着你不能在相同的输入上重新运行相同的工作,并且必然会得到相同的结果:输入流上的事件可能交织在当你再次运行这个工作时,采用不同的方式
-在数据仓库中,这个问题被称为缓慢变化的维度(SCD),通常通过对特定版本的联合记录使用唯一的标识符来解决:例如,每当税率改变时,新的标识符,并且发票包括销售时的税率标识符【88,89】。这种变化使连接成为确定性的,但是由于表中所有记录的版本都需要保留,导致日志压缩是不可能的。
+ 在数据仓库中,这个问题被称为缓慢变化的维度(SCD),通常通过对特定版本的联合记录使用唯一的标识符来解决:例如,每当税率改变时,新的标识符,并且发票包括销售时的税率标识符【88,89】。这种变化使连接成为确定性的,但是由于表中所有记录的版本都需要保留,导致日志压缩是不可能的。
### 容错
-在本章的最后一节中,让我们考虑流处理器如何容忍错误。我们在[第10章](ch10.md)中看到,批处理框架可以很容易地容忍错误:如果MapReduce作业中的任务失败,可以简单地在另一台机器上重新启动,并且丢弃失败任务的输出。这种透明的重试是可能的,因为输入文件是不可变的,每个任务都将其输出写入到HDFS上的单独文件,并且输出仅在任务成功完成时可见。
+ 在本章的最后一节中,让我们考虑流处理器如何容忍错误。我们在[第10章](ch10.md)中看到,批处理框架可以很容易地容忍错误:如果MapReduce作业中的任务失败,可以简单地在另一台机器上重新启动,并且丢弃失败任务的输出。这种透明的重试是可能的,因为输入文件是不可变的,每个任务都将其输出写入到HDFS上的单独文件,并且输出仅在任务成功完成时可见。
-特别是,批处理容错方法可确保批处理作业的输出与没有出错的情况相同,即使事实上某些任务失败了。看起来好像每个输入记录都被处理了一次 —— 没有记录被跳过,而且没有处理两次。尽管重新启动任务意味着实际上可能会多次处理记录,但输出中的可见效果好像只处理过一次。这个原则被称为一次语义学,虽然有效 —— 一次将是一个更具描述性的术语【90】。
+ 特别是,批处理容错方法可确保批处理作业的输出与没有出错的情况相同,即使事实上某些任务失败了。看起来好像每个输入记录都被处理了一次 —— 没有记录被跳过,而且没有处理两次。尽管重新启动任务意味着实际上可能会多次处理记录,但输出中的可见效果好像只处理过一次。这个原则被称为一次语义学,虽然有效 —— 一次将是一个更具描述性的术语【90】。
-在流处理过程中也出现了同样的容错问题,但是处理起来不那么直观:等到某个任务完成之后才使其输出可见,因为流是无限的,因此您永远无法完成处理。
+ 在流处理过程中也出现了同样的容错问题,但是处理起来不那么直观:等到某个任务完成之后才使其输出可见,因为流是无限的,因此您永远无法完成处理。
#### 小批量和检查点
-一个解决方案是将流分解成小块,并像小型批处理一样处理每个块。这种方法被称为**小批量(microbatching)**,它被用于Spark Streaming 【91】。批处理大小通常约为1秒,这是性能折中的结果:较小的批次会导致更大的调度和协调开销,而较大的批次意味着流处理器的结果变得可见之前的较长延迟。
+ 一个解决方案是将流分解成小块,并像小型批处理一样处理每个块。这种方法被称为**小批量(microbatching)**,它被用于Spark Streaming 【91】。批处理大小通常约为1秒,这是性能折中的结果:较小的批次会导致更大的调度和协调开销,而较大的批次意味着流处理器的结果变得可见之前的较长延迟。
-微缩也隐含地提供了与批量大小相等的翻滚窗口(通过处理时间而不是事件时间戳)。任何需要更大窗口的作业都需要明确地将状态从一个微阵列转移到下一个微阵列。
+ 微缩也隐含地提供了与批量大小相等的翻滚窗口(通过处理时间而不是事件时间戳)。任何需要更大窗口的作业都需要明确地将状态从一个微阵列转移到下一个微阵列。
-Apache Flink中使用的一种变体方法是定期生成状态滚动检查点并将其写入持久存储器【92,93】。如果流操作符崩溃,它可以从最近的检查点重新启动,并放弃在最后一个检查点和崩溃之间生成的任何输出。检查点由消息流中的条形码触发,类似于微型图形之间的边界,但不强制特定的窗口大小。
+ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点并将其写入持久存储器【92,93】。如果流操作符崩溃,它可以从最近的检查点重新启动,并放弃在最后一个检查点和崩溃之间生成的任何输出。检查点由消息流中的条形码触发,类似于微型图形之间的边界,但不强制特定的窗口大小。
-在流处理框架的范围内,微观网格化和检查点方法提供了与批处理一样的一次语义。但是,只要输出离开流处理器(例如,通过写入数据库,向外部消息代理发送消息或发送电子邮件),框架将不再能够放弃失败批处理的输出。在这种情况下,重新启动失败的任务会导致外部副作用发生两次,单独使用微配量或检查点不足以防止此问题。
+ 在流处理框架的范围内,微观网格化和检查点方法提供了与批处理一样的一次语义。但是,只要输出离开流处理器(例如,通过写入数据库,向外部消息代理发送消息或发送电子邮件),框架将不再能够放弃失败批处理的输出。在这种情况下,重新启动失败的任务会导致外部副作用发生两次,单独使用微配量或检查点不足以防止此问题。
#### 原子提交重访
-为了在出现故障时给出精确的一次处理,我们需要确保处理事件的所有输出和副作用只有当处理成功时才会生效。这些影响包括发送给下游运营商或外部消息传递系统(包括电子邮件或推送通知)的任何消息,任何数据库写入,对运营商状态的任何更改以及对输入消息的任何确认(包括将消费者偏移量向前移动基于日志的消息代理)。
+ 为了在出现故障时给出精确的一次处理,我们需要确保处理事件的所有输出和副作用只有当处理成功时才会生效。这些影响包括发送给下游运营商或外部消息传递系统(包括电子邮件或推送通知)的任何消息,任何数据库写入,对运营商状态的任何更改以及对输入消息的任何确认(包括将消费者偏移量向前移动基于日志的消息代理)。
-这些事情要么都是原子地发生,要么都不发生,但是不应该彼此不同步。如果这种方法听起来很熟悉,那是因为我们在分布式事务和两阶段提交的情况下,在第360页的“准确一次的消息处理”中讨论了它。
+ 这些事情要么都是原子地发生,要么都不发生,但是不应该彼此不同步。如果这种方法听起来很熟悉,那是因为我们在分布式事务和两阶段提交的情况下,在第360页的“准确一次的消息处理”中讨论了它。
-在[第9章](ch9.md)中,我们讨论了分布式交易(如XA)的传统实现中的问题。然而,在更受限制的环境中,可以有效地实现这样的原子提交设施。 Google云数据流【81,92】和VoltDB 【94】中使用了这种方法,并计划在Apache Kafka 【95,96】中添加类似的功能。与XA不同,这些实现不会尝试跨异构技术提供事务,而是通过在流处理框架中管理状态更改和消息传递来保持内部事务。事务协议的开销可以通过在单个事务中处理几个输入消息来分摊。
+ 在[第9章](ch9.md)中,我们讨论了分布式交易(如XA)的传统实现中的问题。然而,在更受限制的环境中,可以有效地实现这样的原子提交设施。 Google云数据流【81,92】和VoltDB 【94】中使用了这种方法,并计划在Apache Kafka 【95,96】中添加类似的功能。与XA不同,这些实现不会尝试跨异构技术提供事务,而是通过在流处理框架中管理状态更改和消息传递来保持内部事务。事务协议的开销可以通过在单个事务中处理几个输入消息来分摊。
#### 幂等
-我们的目标是放弃任何失败的任务的部分输出,以便他们可以安全地重试,而不会两次生效。分布式事务是实现这一目标的一种方式,但另一种方式是依赖幂等性【97】。
+ 我们的目标是放弃任何失败的任务的部分输出,以便他们可以安全地重试,而不会两次生效。分布式事务是实现这一目标的一种方式,但另一种方式是依赖幂等性【97】。
-幂等操作是可以多次执行的操作,并且与只执行一次操作具有相同的效果。例如,将键值存储中的某个键设置为某个固定值是幂等的(再次写入该值会覆盖具有相同值的值),而递增计数器不是幂等的(再次执行递增意味着该值递增两次)。
+ 幂等操作是可以多次执行的操作,并且与只执行一次操作具有相同的效果。例如,将键值存储中的某个键设置为某个固定值是幂等的(再次写入该值会覆盖具有相同值的值),而递增计数器不是幂等的(再次执行递增意味着该值递增两次)。
-即使一个操作不是天生的幂等,它往往可以与一些额外的元数据幂等。例如,在使用来自卡夫卡的消息时,每条消息都有一个持续的,单调递增的偏移量。将值写入外部数据库时,可以将触发上次写入的消息的偏移量与值包含在一起。因此,您可以判断是否已应用更新,并避免再次执行相同的更新。
+ 即使一个操作不是天生的幂等,它往往可以与一些额外的元数据幂等。例如,在使用来自Kafka的消息时,每条消息都有一个持续的,单调递增的偏移量。将值写入外部数据库时,可以将触发上次写入的消息的偏移量与值包含在一起。因此,您可以判断是否已应用更新,并避免再次执行相同的更新。
-风暴三叉戟的状态处理基于类似的想法【78】。依赖幂等性意味着一些假设:重启一个失败的任务必须以相同的顺序重播相同的消息(一个基于日志的消息代理这样做),处理必须是确定性的,其他节点不能同时更新相同的值【98,99】。
+ 风暴三叉戟的状态处理基于类似的想法【78】。依赖幂等性意味着一些假设:重启一个失败的任务必须以相同的顺序重播相同的消息(一个基于日志的消息代理这样做),处理必须是确定性的,其他节点不能同时更新相同的值【98,99】。
-当从一个处理节点故障转移到另一个处理节点时,可能需要进行防护(参阅“[领导和锁](ch8.md#领导和锁)”),以防止被认为是死的节点的干扰
+ 当从一个处理节点故障转移到另一个处理节点时,可能需要进行防护(参阅“[领导和锁](ch8.md#领导和锁)”),以防止被认为是死的节点的干扰
#### 失败后重建状态
-任何需要状态的流进程(例如,任何窗口聚合(例如计数器,平均值和直方图)以及用于连接的任何表和索引)都必须确保在失败之后可以恢复此状态。
+ 任何需要状态的流进程(例如,任何窗口聚合(例如计数器,平均值和直方图)以及用于连接的任何表和索引)都必须确保在失败之后可以恢复此状态。
-一种选择是将状态保持在远程数据存储中并复制它,尽管如每个单独消息的远程数据库查询速度可能会很慢,正如在“[流表连接](#流表连接)”中所述。另一种方法是保持流处理器的本地状态,并定期复制。然后,当流处理器从故障中恢复时,新任务可以读取复制状态并恢复处理而不丢失数据。
+ 一种选择是将状态保持在远程数据存储中并复制它,尽管如每个单独消息的远程数据库查询速度可能会很慢,正如在“[流表连接](#流表连接)”中所述。另一种方法是保持流处理器的本地状态,并定期复制。然后,当流处理器从故障中恢复时,新任务可以读取复制状态并恢复处理而不丢失数据。
-例如,Flink定期捕获操作员状态的快照,并将它们写入HDFS等持久存储器中【92,93】。 Samza和Kafka Streams通过将状态更改发送到具有日志压缩功能的专用Kafka主题来复制状态更改,这类似于更改数据捕获【84,100】。 VoltDB通过冗余处理多个节点上的每个输入消息来复制状态(参阅“[真的串行执行](ch7.md#真的串行执行)”)。
+ 例如,Flink定期捕获操作员状态的快照,并将它们写入HDFS等持久存储器中【92,93】。 Samza和Kafka Streams通过将状态更改发送到具有日志压缩功能的专用Kafka主题来复制状态更改,这类似于更改数据捕获【84,100】。 VoltDB通过冗余处理多个节点上的每个输入消息来复制状态(参阅“[真的串行执行](ch7.md#真的串行执行)”)。
-在某些情况下,甚至可能不需要复制状态,因为它可以从输入流重建。例如,如果状态由一个相当短的窗口中的聚合组成,则它可能足够快,以便重放与该窗口相对应的输入事件。如果状态是通过更改数据捕获维护的数据库的本地副本,那么也可以从日志压缩的更改流重建数据库(请参阅“[日志压缩](#日志压缩)”一节)。
+ 在某些情况下,甚至可能不需要复制状态,因为它可以从输入流重建。例如,如果状态由一个相当短的窗口中的聚合组成,则它可能足够快,以便重放与该窗口相对应的输入事件。如果状态是通过更改数据捕获维护的数据库的本地副本,那么也可以从日志压缩的更改流重建数据库(请参阅“[日志压缩](#日志压缩)”一节)。
-但是,所有这些权衡取决于底层基础架构的性能特征:在某些系统中,网络延迟可能低于磁盘访问延迟,网络带宽可能与磁盘带宽相当。在所有情况下都没有普遍理想的权衡,随着存储和网络技术的发展,本地和远程状态的优点也可能会发生变化。
+ 但是,所有这些权衡取决于底层基础架构的性能特征:在某些系统中,网络延迟可能低于磁盘访问延迟,网络带宽可能与磁盘带宽相当。在所有情况下都没有普遍理想的权衡,随着存储和网络技术的发展,本地和远程状态的优点也可能会发生变化。
## 本章小结
-在本章中,我们讨论了事件流,他们所服务的目的以及如何处理它们。在某些方面,流处理非常类似于我们在[第10章](ch10.md)讨论的批处理,而是在无限的(永无止境的)流而不是固定大小的输入上持续进行。从这个角度来看,消息代理和事件日志可以作为文件系统的流媒体。
+ 在本章中,我们讨论了事件流,他们所服务的目的以及如何处理它们。在某些方面,流处理非常类似于我们在[第10章](ch10.md)讨论的批处理,而是在无限的(永无止境的)流而不是固定大小的输入上持续进行。从这个角度来看,消息代理和事件日志可以作为文件系统的流媒体。
-我们花了一些时间比较两种消息代理:
+ 我们花了一些时间比较两种消息代理:
***AMQP/JMS风格的消息代理***
-
- 经纪人将个人消息分配给消费者,消费者在成功处理个人消息时确认消息。消息被确认后从代理中删除。这种方法适合作为RPC的异步形式(另请参阅“[消息传递数据流](ch4.md#消息传递数据流)”),例如在任务队列中,消息处理的确切顺序并不重要,没有在处理之后,需要重新读取旧消息。
+ 代理将个人消息分配给消费者,消费者在成功处理个人消息时确认消息。消息被确认后从代理中删除。这种方法适合作为RPC的异步形式(另请参阅“[消息传递数据流](ch4.md#消息传递数据流)”),例如在任务队列中,消息处理的确切顺序并不重要,没有在处理之后,需要重新读取旧消息。
***基于日志的消息代理***
代理将分区中的所有消息分配给相同的使用者节点,并始终以相同的顺序传递消息。并行性是通过划分来实现的,消费者通过检查他们所处理的最后一个消息的偏移来跟踪他们的进度。代理将消息保留在磁盘上,因此如有必要,可以跳回并重新读取旧消息。
+ 基于日志的方法与数据库中的复制日志(参见[第5章](ch5.md))和日志结构存储引擎(请参阅[第3章](ch3.md))具有相似之处。我们看到,这种方法特别适用于消耗输入流并生成派生状态或派生输出流的流处理系统。
+ 就流的来源而言,我们讨论了几种可能性:用户活动事件,提供定期读数的传感器和数据馈送(例如金融市场数据)自然地表示为流。我们看到,将数据写入数据流也是有用的:我们可以捕获更改日志 —— 即对数据库所做的所有更改的历史记录 —— 隐式地通过更改数据捕获或通过事件明确地捕获代理。日志压缩允许流保留数据库 内容的完整副本。
-基于日志的方法与数据库中的复制日志(参见[第5章](ch5.md))和日志结构存储引擎(请参阅[第3章](ch3.md))具有相似之处。我们看到,这种方法特别适用于消耗输入流并生成派生状态或派生输出流的流处理系统。
+ 将数据库表示为流为系统集成提供了强大的机会。您可以通过使用更改日志并将其应用于派生系统,使派生的数据系统(如搜索索引,缓存和分析系统)保持最新。您甚至可以从头开始,从开始一直到现在消耗更改的日志,从而为现有数据构建新的视图。
-就流的来源而言,我们讨论了几种可能性:用户活动事件,提供定期读数的传感器和数据馈送(例如金融市场数据)自然地表示为流。我们看到,将数据写入数据流也是有用的:我们可以捕获更改日志 —— 即对数据库所做的所有更改的历史记录 —— 隐式地通过更改数据捕获或通过事件明确地捕获采购。日志压缩允许流保留数据库内容的完整副本。
+ 将状态保持为流并重放消息的设施也是在各种流处理框架中实现流连接和容错的技术的基础。我们讨论了流处理的几个目的,包括搜索事件模式(复杂事件处理),计算加窗聚合(流分析)以及保持派生数据系统处于最新状态(物化视图)。
-将数据库表示为流为系统集成提供了强大的机会。您可以通过使用更改日志并将其应用于派生系统,使派生的数据系统(如搜索索引,缓存和分析系统)保持最新。您甚至可以从头开始,从开始一直到现在消耗更改的日志,从而为现有数据构建新的视图。
+ 然后我们讨论了在流处理器中推理时间的困难,包括处理时间和事件时间戳之间的区别,以及在你认为窗口完成之后处理到达的离散事件的问题。
-将状态保持为流并重放消息的设施也是在各种流处理框架中实现流连接和容错的技术的基础。我们讨论了流处理的几个目的,包括搜索事件模式(复杂事件处理),计算加窗聚合(流分析)以及保持派生数据系统处于最新状态(物化视图)。
-
-然后我们讨论了在流处理器中推理时间的困难,包括处理时间和事件时间戳之间的区别,以及在你认为窗口完成之后处理到达的离散事件的问题。
-
-我们区分了可能出现在流程中的三种类型的连接:
+ 我们区分了可能出现在流程中的三种类型的连接:
***流式流连接***
@@ -732,343 +728,208 @@ Apache Flink中使用的一种变体方法是定期生成状态滚动检查点
两个输入流都是数据库更新日志。在这种情况下,一方的每一个变化都与另一方的最新状态相结合。结果是对两个表之间的连接的物化视图进行了一系列更改。
-最后,我们讨论了在流处理器中实现容错和一次语义的技术。与批处理一样,我们需要放弃任何失败任务的部分输出。然而,由于流程长时间运行并持续产生输出,所以不能简单地丢弃所有的输出。相反,可以使用更细粒度的恢复机制,基于微博,检查点,事务或幂等写入。
+ 最后,我们讨论了在流处理器中实现容错和一次语义的技术。与批处理一样,我们需要放弃任何失败任务的部分输出。然而,由于流程长时间运行并持续产生输出,所以不能简单地丢弃所有的输出。相反,可以使用更细粒度的恢复机制,基于微博,检查点,事务或幂等写入。
## 参考文献
-1. Tyler Akidau, Robert Bradshaw, Craig Chambers, et al.:
- “[The Dataflow Model: A Practical Approach to Balancing Correctness, Latency, and Cost in Massive-Scale, Unbounded, Out-of-Order Data Processing](http://www.vldb.org/pvldb/vol8/p1792-Akidau.pdf),”
- *Proceedings of the VLDB Endowment*, volume 8, number 12, pages 1792–1803, August 2015.
- [doi:10.14778/2824032.2824076](http://dx.doi.org/10.14778/2824032.2824076)
+1. Tyler Akidau, Robert Bradshaw, Craig Chambers, et al.: “[The Dataflow Model: A Practical Approach to Balancing Correctness, Latency, and Cost in Massive-Scale, Unbounded, Out-of-Order Data Processing](http://www.vldb.org/pvldb/vol8/p1792-Akidau.pdf),”
+ *Proceedings of the VLDB Endowment*, volume 8, number 12, pages 1792–1803, August 2015. [doi:10.14778/2824032.2824076](http://dx.doi.org/10.14778/2824032.2824076)
-1. Harold Abelson, Gerald Jay Sussman, and Julie Sussman:
- *Structure and Interpretation of Computer Programs*,
- 2nd edition. MIT Press, 1996. ISBN: 978-0-262-51087-5, available online at *mitpress.mit.edu*
+1. Harold Abelson, Gerald Jay Sussman, and Julie Sussman: *Structure and Interpretation of Computer Programs*, 2nd edition. MIT Press, 1996. ISBN: 978-0-262-51087-5, available online at *mitpress.mit.edu*
-1. Patrick Th. Eugster, Pascal A. Felber,
- Rachid Guerraoui, and Anne-Marie Kermarrec:
- “[The Many Faces of Publish/Subscribe](http://www.cs.ru.nl/~pieter/oss/manyfaces.pdf),”
- *ACM Computing Surveys*, volume 35, number 2, pages 114–131, June 2003.
- [doi:10.1145/857076.857078](http://dx.doi.org/10.1145/857076.857078)
+1. Patrick Th. Eugster, Pascal A. Felber, Rachid Guerraoui, and Anne-Marie Kermarrec: “[The Many Faces of Publish/Subscribe](http://www.cs.ru.nl/~pieter/oss/manyfaces.pdf),” *ACM Computing Surveys*, volume 35, number 2, pages 114–131, June 2003.
+ [doi:10.1145/857076.857078](http://dx.doi.org/10.1145/857076.857078)
-1. Joseph M. Hellerstein and Michael Stonebraker:
- *Readings in Database Systems*, 4th edition.
- MIT Press, 2005. ISBN: 978-0-262-69314-1, available online at *redbook.cs.berkeley.edu*
+1. Joseph M. Hellerstein and Michael Stonebraker: *Readings in Database Systems*, 4th edition. MIT Press, 2005. ISBN: 978-0-262-69314-1, available online at *redbook.cs.berkeley.edu*
-1. Don Carney, Uğur Çetintemel, Mitch Cherniack, et al.:
- “[Monitoring Streams – A New Class of Data Management Applications](http://www.vldb.org/conf/2002/S07P02.pdf),” at *28th International Conference on Very Large Data Bases*
- (VLDB), August 2002.
+1. Don Carney, Uğur Çetintemel, Mitch Cherniack, et al.: “[Monitoring Streams – A New Class of Data Management Applications](http://www.vldb.org/conf/2002/S07P02.pdf),” at *28th International Conference on Very Large Data Bases* (VLDB), August 2002.
-1. Matthew Sackman:
- “[Pushing Back](http://www.lshift.net/blog/2016/05/05/pushing-back/),”
- *lshift.net*, May 5, 2016.
+1. Matthew Sackman: “[Pushing Back](http://www.lshift.net/blog/2016/05/05/pushing-back/),” *lshift.net*, May 5, 2016. Vicent Martí: “[Brubeck, a statsd-Compatible Metrics Aggregator](http://githubengineering.com/brubeck/),” *githubengineering.com*, June 15, 2015. Seth Lowenberger: “[MoldUDP64 Protocol Specification V 1.00](http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/moldudp64.pdf),” *nasdaqtrader.com*, July 2009.
-1. Vicent Martí:
- “[Brubeck, a statsd-Compatible Metrics Aggregator](http://githubengineering.com/brubeck/),” *githubengineering.com*, June 15, 2015.
+1. Pieter Hintjens: *ZeroMQ – The Guide*. O'Reilly Media, 2013. ISBN: 978-1-449-33404-8
-1. Seth Lowenberger:
- “[MoldUDP64 Protocol Specification V 1.00](http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/moldudp64.pdf),” *nasdaqtrader.com*, July 2009.
+1. Ian Malpass: “[Measure Anything, Measure Everything](https://codeascraft.com/2011/02/15/measure-anything-measure-everything/),” *codeascraft.com*, February 15, 2011.
-1. Pieter Hintjens:
- *ZeroMQ – The Guide*. O'Reilly Media, 2013.
- ISBN: 978-1-449-33404-8
+1. Dieter Plaetinck: “[25 Graphite, Grafana and statsd Gotchas](https://blog.raintank.io/25-graphite-grafana-and-statsd-gotchas/),” *blog.raintank.io*, March 3, 2016.
-1. Ian Malpass:
- “[Measure Anything, Measure Everything](https://codeascraft.com/2011/02/15/measure-anything-measure-everything/),” *codeascraft.com*, February 15, 2011.
+1. Jeff Lindsay: “[Web Hooks to Revolutionize the Web](http://progrium.com/blog/2007/05/03/web-hooks-to-revolutionize-the-web/),” *progrium.com*, May 3, 2007.
-1. Dieter Plaetinck:
- “[25 Graphite, Grafana and statsd Gotchas](https://blog.raintank.io/25-graphite-grafana-and-statsd-gotchas/),” *blog.raintank.io*, March 3, 2016.
+1. Jim N. Gray: “[Queues Are Databases](http://research.microsoft.com/pubs/69641/tr-95-56.pdf),” Microsoft Research Technical Report MSR-TR-95-56, December 1995.
-1. Jeff Lindsay:
- “[Web Hooks to Revolutionize the Web](http://progrium.com/blog/2007/05/03/web-hooks-to-revolutionize-the-web/),” *progrium.com*, May 3, 2007.
+1. Mark Hapner, Rich Burridge, Rahul Sharma, et al.: “[JSR-343 Java Message Service (JMS) 2.0 Specification](https://jcp.org/en/jsr/detail?id=343),” *jms-spec.java.net*, March 2013.
-1. Jim N. Gray:
- “[Queues Are Databases](http://research.microsoft.com/pubs/69641/tr-95-56.pdf),”
- Microsoft Research Technical Report MSR-TR-95-56, December 1995.
-
-1. Mark Hapner, Rich Burridge, Rahul Sharma, et al.:
- “[JSR-343 Java Message Service (JMS) 2.0 Specification](https://jcp.org/en/jsr/detail?id=343),” *jms-spec.java.net*, March 2013.
-
-1. Sanjay Aiyagari, Matthew Arrott, Mark Atwell, et al.:
- “[AMQP: Advanced Message Queuing Protocol Specification](http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf),” Version 0-9-1, November 2008.
+1. Sanjay Aiyagari, Matthew Arrott, Mark Atwell, et al.: “[AMQP: Advanced Message Queuing Protocol Specification](http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf),” Version 0-9-1, November 2008.
1. “[Google Cloud Pub/Sub: A Google-Scale Messaging Service](https://cloud.google.com/pubsub/architecture),” *cloud.google.com*, 2016.
1. “[Apache Kafka 0.9 Documentation](http://kafka.apache.org/documentation.html),” *kafka.apache.org*, November 2015.
-1. Jay Kreps, Neha Narkhede, and Jun Rao:
- “[Kafka: A Distributed Messaging System for Log Processing](http://www.longyu23.com/doc/Kafka.pdf),” at *6th International Workshop on
- Networking Meets Databases* (NetDB), June 2011.
+1. Jay Kreps, Neha Narkhede, and Jun Rao: “[Kafka: A Distributed Messaging System for Log Processing](http://www.longyu23.com/doc/Kafka.pdf),” at *6th International Workshop on Networking Meets Databases* (NetDB), June 2011.
1. “[Amazon Kinesis Streams Developer Guide](http://docs.aws.amazon.com/streams/latest/dev/introduction.html),” *docs.aws.amazon.com*, April 2016.
-1. Leigh Stewart and Sijie Guo:
- “[Building DistributedLog: Twitter’s High-Performance Replicated Log Service](https://blog.twitter.com/2015/building-distributedlog-twitter-s-high-performance-replicated-log-service),” *blog.twitter.com*,
- September 16, 2015.
+1. Leigh Stewart and Sijie Guo: “[Building DistributedLog: Twitter’s High-Performance Replicated Log Service](https://blog.twitter.com/2015/building-distributedlog-twitter-s-high-performance-replicated-log-service),” *blog.twitter.com*, September 16, 2015.
-1. “[DistributedLog Documentation](http://distributedlog.incubator.apache.org/docs/latest/),” Twitter, Inc., *distributedlog.io*, May 2016.
+1. “[DistributedLog Documentation](http://distributedlog.incubator.apache.org/docs/latest/),” Twitter, Inc., *distributedlog.io*, May 2016. Jay Kreps:
-1. Jay Kreps:
- “[Benchmarking Apache Kafka: 2 Million Writes Per Second (On Three Cheap Machines)](https://engineering.linkedin.com/kafka/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines),” *engineering.linkedin.com*,
- April 27, 2014.
+ “[Benchmarking Apache Kafka: 2 Million Writes Per Second (On Three Cheap Machines)](https://engineering.linkedin.com/kafka/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines),” *engineering.linkedin.com*, April 27, 2014.
-1. Kartik Paramasivam:
- “[How We’re Improving and Advancing Kafka at LinkedIn](https://engineering.linkedin.com/apache-kafka/how-we_re-improving-and-advancing-kafka-linkedin),” *engineering.linkedin.com*, September 2, 2015.
+1. Kartik Paramasivam: “[How We’re Improving and Advancing Kafka at LinkedIn](https://engineering.linkedin.com/apache-kafka/how-we_re-improving-and-advancing-kafka-linkedin),” *engineering.linkedin.com*, September 2, 2015.
-1. Jay Kreps:
- “[The Log: What Every Software Engineer Should Know About Real-Time Data's Unifying Abstraction](http://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying),”
- *engineering.linkedin.com*, December 16, 2013.
+1. Jay Kreps: “[The Log: What Every Software Engineer Should Know About Real-Time Data's Unifying Abstraction](http://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying),” *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.
+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.
-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
- Networked Systems Design and Implementation* (NSDI), May 2015.
+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 Networked Systems Design and Implementation* (NSDI), May 2015.
-1. P. P. S. Narayan:
- “[Sherpa Update](http://web.archive.org/web/20160801221400/https://developer.yahoo.com/blogs/ydn/sherpa-7992.html),”
- *developer.yahoo.com*, June 8, .
+1. P. P. S. Narayan: “[Sherpa Update](http://web.archive.org/web/20160801221400/https://developer.yahoo.com/blogs/ydn/sherpa-7992.html),” *developer.yahoo.com*, June 8, .
-1. Martin Kleppmann:
- “[Bottled Water: Real-Time Integration of PostgreSQL and Kafka](http://martin.kleppmann.com/2015/04/23/bottled-water-real-time-postgresql-kafka.html),” *martin.kleppmann.com*, April 23, 2015.
+1. Martin Kleppmann: “[Bottled Water: Real-Time Integration of PostgreSQL and Kafka](http://martin.kleppmann.com/2015/04/23/bottled-water-real-time-postgresql-kafka.html),” *martin.kleppmann.com*, April 23, 2015.
-1. Ben Osheroff:
- “[Introducing Maxwell, a mysql-to-kafka Binlog Processor](https://developer.zendesk.com/blog/introducing-maxwell-a-mysql-to-kafka-binlog-processor),” *developer.zendesk.com*, August 20, 2015.
+1. Ben Osheroff: “[Introducing Maxwell, a mysql-to-kafka Binlog Processor](https://developer.zendesk.com/blog/introducing-maxwell-a-mysql-to-kafka-binlog-processor),” *developer.zendesk.com*, August 20, 2015.
-1. Randall Hauch:
- “[Debezium 0.2.1 Released](http://debezium.io/blog/2016/06/10/Debezium-0/),” *debezium.io*,
- June 10, 2016.
+1. Randall Hauch: “[Debezium 0.2.1 Released](http://debezium.io/blog/2016/06/10/Debezium-0/),” *debezium.io*, June 10, 2016.
-1. Prem Santosh Udaya Shankar:
- “[Streaming MySQL Tables in Real-Time to Kafka](https://engineeringblog.yelp.com/2016/08/streaming-mysql-tables-in-real-time-to-kafka.html),” *engineeringblog.yelp.com*, August 1, 2016.
+1. Prem Santosh Udaya Shankar: “[Streaming MySQL Tables in Real-Time to Kafka](https://engineeringblog.yelp.com/2016/08/streaming-mysql-tables-in-real-time-to-kafka.html),” *engineeringblog.yelp.com*, August 1, 2016.
-1. “[Mongoriver](https://github.com/stripe/mongoriver),”
- Stripe, Inc., *github.com*, September 2014.
+1. “[Mongoriver](https://github.com/stripe/mongoriver),” Stripe, Inc., *github.com*, September 2014.
-1. Dan Harvey:
- “[Change Data Capture with Mongo + Kafka](http://www.slideshare.net/danharvey/change-data-capture-with-mongodb-and-kafka),” at *Hadoop Users Group UK*, August 2015.
+1. Dan Harvey: “[Change Data Capture with Mongo + Kafka](http://www.slideshare.net/danharvey/change-data-capture-with-mongodb-and-kafka),” at *Hadoop Users Group UK*, August 2015.
1. “[Oracle GoldenGate 12c: Real-Time Access to Real-Time Information](http://www.oracle.com/us/products/middleware/data-integration/oracle-goldengate-realtime-access-2031152.pdf),” Oracle White Paper, March 2015.
1. “[Oracle GoldenGate Fundamentals: How Oracle GoldenGate Works](https://www.youtube.com/watch?v=6H9NibIiPQE),” Oracle Corporation, *youtube.com*, November 2012.
-1. Slava Akhmechet:
- “[Advancing the Realtime Web](http://rethinkdb.com/blog/realtime-web/),” *rethinkdb.com*,
- January 27, 2015.
+1. Slava Akhmechet: “[Advancing the Realtime Web](http://rethinkdb.com/blog/realtime-web/),” *rethinkdb.com*, January 27, 2015.
1. “[Firebase Realtime Database Documentation](https://firebase.google.com/docs/database/),” Google, Inc., *firebase.google.com*, May 2016.
1. “[Apache CouchDB 1.6 Documentation](http://docs.couchdb.org/en/latest/),” *docs.couchdb.org*, 2014.
-1. Matt DeBergalis:
- “[Meteor 0.7.0: Scalable Database Queries Using MongoDB Oplog Instead of Poll-and-Diff](http://info.meteor.com/blog/meteor-070-scalable-database-queries-using-mongodb-oplog-instead-of-poll-and-diff),” *info.meteor.com*,
- December 17, 2013.
+1. Matt DeBergalis: “[Meteor 0.7.0: Scalable Database Queries Using MongoDB Oplog Instead of Poll-and-Diff](http://info.meteor.com/blog/meteor-070-scalable-database-queries-using-mongodb-oplog-instead-of-poll-and-diff),” *info.meteor.com*, December 17, 2013.
1. “[Chapter 15. Importing and Exporting Live Data](https://docs.voltdb.com/UsingVoltDB/ChapExport.php),” VoltDB 6.4 User Manual, *docs.voltdb.com*, June 2016.
-1. Neha Narkhede:
- “[Announcing Kafka Connect: Building Large-Scale Low-Latency Data Pipelines](http://www.confluent.io/blog/announcing-kafka-connect-building-large-scale-low-latency-data-pipelines),” *confluent.io*,
- February 18, 2016.
+1. Neha Narkhede: “[Announcing Kafka Connect: Building Large-Scale Low-Latency Data Pipelines](http://www.confluent.io/blog/announcing-kafka-connect-building-large-scale-low-latency-data-pipelines),” *confluent.io*, February 18, 2016.
-1. Greg Young:
- “[CQRS and Event Sourcing](https://www.youtube.com/watch?v=JHGkaShoyNs),” at *Code on the Beach*, August 2014.
+1. Greg Young: “[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*,
- December 12, 2005.
+1. Martin Fowler: “[Event Sourcing](http://martinfowler.com/eaaDev/EventSourcing.html),” *martinfowler.com*, December 12, 2005.
-1. Vaughn Vernon:
- *Implementing Domain-Driven Design*.
- Addison-Wesley Professional, 2013. ISBN: 978-0-321-83457-7
+1. Vaughn Vernon: *Implementing Domain-Driven Design*. 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.
- [doi:10.1145/212433.220201](http://dx.doi.org/10.1145/212433.220201)
+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. [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.
+1. Martin Kleppmann: *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.
+1. Sander Mak: “[Event-Sourced Architectures with Akka](http://www.slideshare.net/SanderMak/eventsourced-architectures-with-akka),” at *JavaOne*, September 2014.
-1. Julian Hyde:
- [personal communication](https://twitter.com/julianhyde/status/743374145006641153),
- June 2016.
+1. Julian Hyde: [personal communication](https://twitter.com/julianhyde/status/743374145006641153), June 2016.
-1. Ashish Gupta and Inderpal Singh Mumick:
- *Materialized Views: Techniques, Implementations, and Applications*. MIT Press, 1999.
- ISBN: 978-0-262-57122-7
+1. Ashish Gupta and Inderpal Singh Mumick: *Materialized Views: Techniques, Implementations, and Applications*. MIT Press, 1999. 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.
- [doi:10.1145/223784.223849](http://dx.doi.org/10.1145/223784.223849)
+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. [doi:10.1145/223784.223849](http://dx.doi.org/10.1145/223784.223849)
-1. Pat Helland:
- “[Immutability Changes Everything](http://www.cidrdb.org/cidr2015/Papers/CIDR15_Paper16.pdf),” at *7th Biennial Conference on Innovative Data Systems Research* (CIDR),
- January 2015.
+1. Pat Helland: “[Immutability Changes Everything](http://www.cidrdb.org/cidr2015/Papers/CIDR15_Paper16.pdf),” at *7th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2015.
-1. Martin Kleppmann:
- “[Accounting for Computer Scientists](http://martin.kleppmann.com/2011/03/07/accounting-for-computer-scientists.html),” *martin.kleppmann.com*, March 7, 2011.
+1. Martin Kleppmann: “[Accounting for Computer Scientists](http://martin.kleppmann.com/2011/03/07/accounting-for-computer-scientists.html),” *martin.kleppmann.com*, March 7, 2011.
-1. Pat Helland:
- “[Accountants Don't Use Erasers](https://blogs.msdn.microsoft.com/pathelland/2007/06/14/accountants-dont-use-erasers/),” *blogs.msdn.com*, June 14, 2007.
+1. Pat Helland: “[Accountants Don't Use Erasers](https://blogs.msdn.microsoft.com/pathelland/2007/06/14/accountants-dont-use-erasers/),” *blogs.msdn.com*, June 14, 2007.
-1. Fangjin Yang:
- “[Dogfooding with Druid, Samza, and Kafka: Metametrics at Metamarkets](https://metamarkets.com/2015/dogfooding-with-druid-samza-and-kafka-metametrics-at-metamarkets/),” *metamarkets.com*, June 3, 2015.
+1. Fangjin Yang: “[Dogfooding with Druid, Samza, and Kafka: Metametrics at Metamarkets](https://metamarkets.com/2015/dogfooding-with-druid-samza-and-kafka-metametrics-at-metamarkets/),” *metamarkets.com*, June 3, 2015.
-1. Gavin Li, Jianqiu Lv, and Hang Qi:
- “[Pistachio: Co-Locate the Data and Compute for Fastest Cloud Compute](http://yahoohadoop.tumblr.com/post/116365275781/pistachio-co-locate-the-data-and-compute-for),” *yahoohadoop.tumblr.com*, April 13, 2015.
+1. Gavin Li, Jianqiu Lv, and Hang Qi: “[Pistachio: Co-Locate the Data and Compute for Fastest Cloud Compute](http://yahoohadoop.tumblr.com/post/116365275781/pistachio-co-locate-the-data-and-compute-for),” *yahoohadoop.tumblr.com*, April 13, 2015.
-1. Kartik Paramasivam:
- “[Stream Processing Hard Problems – Part 1: Killing Lambda](https://engineering.linkedin.com/blog/2016/06/stream-processing-hard-problems-part-1-killing-lambda),” *engineering.linkedin.com*, June 27, 2016.
+1. Kartik Paramasivam: “[Stream Processing Hard Problems – Part 1: Killing Lambda](https://engineering.linkedin.com/blog/2016/06/stream-processing-hard-problems-part-1-killing-lambda),” *engineering.linkedin.com*, June 27, 2016.
-1. Martin Fowler:
- “[CQRS](http://martinfowler.com/bliki/CQRS.html),” *martinfowler.com*, July 14, 2011.
+1. Martin Fowler: “[CQRS](http://martinfowler.com/bliki/CQRS.html),” *martinfowler.com*, July 14, 2011.
-1. Greg Young:
- “[CQRS Documents](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf),”
- *cqrs.files.wordpress.com*, November 2010.
+1. Greg Young: “[CQRS Documents](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf),” *cqrs.files.wordpress.com*, November 2010.
-1. Baron Schwartz:
- “[Immutability, MVCC, and Garbage Collection](http://www.xaprb.com/blog/2013/12/28/immutability-mvcc-and-garbage-collection/),” *xaprb.com*, December 28, 2013.
+1. Baron Schwartz: “[Immutability, MVCC, and Garbage Collection](http://www.xaprb.com/blog/2013/12/28/immutability-mvcc-and-garbage-collection/),” *xaprb.com*, December 28, 2013.
-1. Daniel Eloff, Slava Akhmechet, Jay Kreps, et al.:
- ["Re: Turning the Database Inside-out with Apache Samza](https://news.ycombinator.com/item?id=9145197)," Hacker News discussion, *news.ycombinator.com*, March 4, 2015.
+1. Daniel Eloff, Slava Akhmechet, Jay Kreps, et al.: ["Re: Turning the Database Inside-out with Apache Samza](https://news.ycombinator.com/item?id=9145197)," *Hacker News discussion, news.ycombinator.com*, March 4, 2015.
1. “[Datomic Development Resources: Excision](http://docs.datomic.com/excision.html),” Cognitect, Inc., *docs.datomic.com*.
1. “[Fossil Documentation: Deleting Content from Fossil](http://fossil-scm.org/index.html/doc/trunk/www/shunning.wiki),” *fossil-scm.org*, 2016.
-1. Jay Kreps:
- “[The irony of distributed systems is that data loss is really easy but deleting data is surprisingly hard,](https://twitter.com/jaykreps/status/582580836425330688)” *twitter.com*, March 30,
- 2015.
+1. Jay Kreps: “[The irony of distributed systems is that data loss is really easy but deleting data is surprisingly hard,](https://twitter.com/jaykreps/status/582580836425330688)” *twitter.com*, March 30, 2015.
-1. David C. Luckham:
- “[What’s the Difference Between ESP and CEP?](http://www.complexevents.com/2006/08/01/what%E2%80%99s-the-difference-between-esp-and-cep/),” *complexevents.com*, August 1, 2006.
+1. David C. Luckham: “[What’s the Difference Between ESP and CEP?](http://www.complexevents.com/2006/08/01/what%E2%80%99s-the-difference-between-esp-and-cep/),” *complexevents.com*, August 1, 2006.
-1. Srinath Perera:
- “[How Is Stream Processing and Complex Event Processing (CEP) Different?](https://www.quora.com/How-is-stream-processing-and-complex-event-processing-CEP-different),” *quora.com*, December 3, 2015.
+1. Srinath Perera: “[How Is Stream Processing and Complex Event Processing (CEP) Different?](https://www.quora.com/How-is-stream-processing-and-complex-event-processing-CEP-different),” *quora.com*, December 3, 2015.
-1. Arvind Arasu, Shivnath Babu, and Jennifer Widom:
- “[The CQL Continuous Query Language: Semantic Foundations and Query Execution](http://research.microsoft.com/pubs/77607/cql.pdf),” *The VLDB Journal*, volume 15, number 2, pages
-121–142, June 2006.
- [doi:10.1007/s00778-004-0147-z](http://dx.doi.org/10.1007/s00778-004-0147-z)
+1. Arvind Arasu, Shivnath Babu, and Jennifer Widom: “[The CQL Continuous Query Language: Semantic Foundations and Query Execution](http://research.microsoft.com/pubs/77607/cql.pdf),” *The VLDB Journal*, volume 15, number 2, pages 121–142, June 2006. [doi:10.1007/s00778-004-0147-z](http://dx.doi.org/10.1007/s00778-004-0147-z)
-1. Julian Hyde:
- “[Data in Flight: How Streaming SQL Technology Can Help Solve the Web 2.0 Data Crunch](http://queue.acm.org/detail.cfm?id=1667562),” *ACM Queue*, volume 7, number 11, December 2009.
- [doi:10.1145/1661785.1667562](http://dx.doi.org/10.1145/1661785.1667562)
+1. Julian Hyde: “[Data in Flight: How Streaming SQL Technology Can Help Solve the Web 2.0 Data Crunch](http://queue.acm.org/detail.cfm?id=1667562),” *ACM Queue*, volume 7, number 11, December 2009. [doi:10.1145/1661785.1667562](http://dx.doi.org/10.1145/1661785.1667562)
1. “[Esper Reference, Version 5.4.0](http://www.espertech.com/esper/release-5.4.0/esper-reference/html_single/index.html),” EsperTech, Inc., *espertech.com*, April 2016.
1. Zubair Nabi, Eric Bouillet, Andrew Bainbridge, and Chris Thomas:
“[Of Streams and Storms](https://developer.ibm.com/streamsdev/wp-content/uploads/sites/15/2014/04/Streams-and-Storm-April-2014-Final.pdf),” IBM technical report, *developer.ibm.com*, April 2014.
-1. Milinda Pathirage, Julian Hyde, Yi Pan, and Beth Plale:
- “[SamzaSQL: Scalable Fast Data Management with Streaming SQL](https://github.com/milinda/samzasql-hpbdc2016/blob/master/samzasql-hpbdc2016.pdf),” at *IEEE International Workshop on
- High-Performance Big Data Computing* (HPBDC), May 2016.
- [doi:10.1109/IPDPSW.2016.141](http://dx.doi.org/10.1109/IPDPSW.2016.141)
+1. Milinda Pathirage, Julian Hyde, Yi Pan, and Beth Plale: “[SamzaSQL: Scalable Fast Data Management with Streaming SQL](https://github.com/milinda/samzasql-hpbdc2016/blob/master/samzasql-hpbdc2016.pdf),” at *IEEE International Workshop on High-Performance Big Data Computing* (HPBDC), May 2016. [doi:10.1109/IPDPSW.2016.141](http://dx.doi.org/10.1109/IPDPSW.2016.141)
-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.
+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.
-1. Jay Kreps:
- “[Questioning the Lambda Architecture](https://www.oreilly.com/ideas/questioning-the-lambda-architecture),” *oreilly.com*, July 2, 2014.
+1. Jay Kreps: “[Questioning the Lambda Architecture](https://www.oreilly.com/ideas/questioning-the-lambda-architecture),” *oreilly.com*, July 2, 2014.
-1. Ian Hellström:
- “[An Overview of Apache Streaming Technologies](https://databaseline.wordpress.com/2016/03/12/an-overview-of-apache-streaming-technologies/),” *databaseline.wordpress.com*, March 12, 2016.
+1. Ian Hellström: “[An Overview of Apache Streaming Technologies](https://databaseline.wordpress.com/2016/03/12/an-overview-of-apache-streaming-technologies/),” *databaseline.wordpress.com*, March 12, 2016.
-1. Jay Kreps:
- “[Why Local State Is a Fundamental Primitive in Stream Processing](https://www.oreilly.com/ideas/why-local-state-is-a-fundamental-primitive-in-stream-processing),” *oreilly.com*, July 31, 2014.
+1. Jay Kreps: “[Why Local State Is a Fundamental Primitive in Stream Processing](https://www.oreilly.com/ideas/why-local-state-is-a-fundamental-primitive-in-stream-processing),” *oreilly.com*, July 31, 2014.
-1. Shay Banon:
- “[Percolator](https://www.elastic.co/blog/percolator),” *elastic.co*, February 8,
- 2011.
+1. Shay Banon: “[Percolator](https://www.elastic.co/blog/percolator),” *elastic.co*, February 8, 2011.
-1. Alan Woodward and Martin Kleppmann:
- “[Real-Time Full-Text Search with Luwak and Samza](http://martin.kleppmann.com/2015/04/13/real-time-full-text-search-luwak-samza.html),” *martin.kleppmann.com*, April 13, 2015.
+1. Alan Woodward and Martin Kleppmann: “[Real-Time Full-Text Search with Luwak and Samza](http://martin.kleppmann.com/2015/04/13/real-time-full-text-search-luwak-samza.html),” *martin.kleppmann.com*, April 13, 2015.
1. “[Apache Storm 1.0.1 Documentation](https://storm.apache.org/releases/1.0.1/index.html),” *storm.apache.org*, May 2016.
-1. Tyler Akidau:
- “[The World Beyond Batch: Streaming 102](https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-102),” *oreilly.com*, January 20, 2016.
+1. Tyler Akidau: “[The World Beyond Batch: Streaming 102](https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-102),” *oreilly.com*, January 20, 2016.
-1. Stephan Ewen:
- “[Streaming Analytics with Apache Flink](http://www.confluent.io/kafka-summit-2016-systems-advanced-streaming-analytics-with-apache-flink-and-apache-kafka),” at *Kafka Summit*, April
- 2016.
+1. Stephan Ewen: “[Streaming Analytics with Apache Flink](http://www.confluent.io/kafka-summit-2016-systems-advanced-streaming-analytics-with-apache-flink-and-apache-kafka),” at *Kafka Summit*, April 2016.
-1. Tyler Akidau, Alex Balikov, Kaya Bekiroğlu, et al.:
- “[MillWheel: Fault-Tolerant Stream Processing at Internet Scale](http://research.google.com/pubs/pub41378.html),” at *39th International Conference on Very Large Data Bases* (VLDB),
- August 2013.
+1. Tyler Akidau, Alex Balikov, Kaya Bekiroğlu, et al.: “[MillWheel: Fault-Tolerant Stream Processing at Internet Scale](http://research.google.com/pubs/pub41378.html),” at *39th International Conference on Very Large Data Bases* (VLDB), August 2013.
-1. Alex Dean:
- “[Improving Snowplow's Understanding of Time](http://snowplowanalytics.com/blog/2015/09/15/improving-snowplows-understanding-of-time/),” *snowplowanalytics.com*, September 15, 2015.
+1. Alex Dean: “[Improving Snowplow's Understanding of Time](http://snowplowanalytics.com/blog/2015/09/15/improving-snowplows-understanding-of-time/),” *snowplowanalytics.com*, September 15, 2015.
1. “[Windowing (Azure Stream Analytics)](https://msdn.microsoft.com/en-us/library/azure/dn835019.aspx),” Microsoft Azure Reference, *msdn.microsoft.com*, April 2016.
1. “[State Management](http://samza.apache.org/learn/documentation/0.10/container/state-management.html),” Apache Samza 0.10 Documentation, *samza.apache.org*, December 2015.
-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.
- [doi:10.1145/2463676.2465272](http://dx.doi.org/10.1145/2463676.2465272)
+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. [doi:10.1145/2463676.2465272](http://dx.doi.org/10.1145/2463676.2465272)
-1. Martin Kleppmann:
- “[Samza Newsfeed Demo](https://github.com/ept/newsfeed),” *github.com*,
- September 2014.
+1. Martin Kleppmann: “[Samza Newsfeed Demo](https://github.com/ept/newsfeed),” *github.com*, September 2014.
-1. Ben Kirwin:
- “[Doing the Impossible: Exactly-Once Messaging Patterns in Kafka](http://ben.kirw.in/2014/11/28/kafka-patterns/),” *ben.kirw.in*, November 28, 2014.
+1. Ben Kirwin: “[Doing the Impossible: Exactly-Once Messaging Patterns in Kafka](http://ben.kirw.in/2014/11/28/kafka-patterns/),” *ben.kirw.in*, November 28, 2014.
-1. Pat Helland:
- “[Data on the Outside Versus Data on the Inside](http://cidrdb.org/cidr2005/papers/P12.pdf),” at *2nd Biennial Conference on Innovative Data Systems Research* (CIDR), January
- 2005.
+1. Pat Helland: “[Data on the Outside Versus Data on the Inside](http://cidrdb.org/cidr2005/papers/P12.pdf),” at *2nd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2005.
-1. Ralph Kimball and Margy Ross:
- *The Data Warehouse Toolkit: The Definitive Guide to Dimensional Modeling*,
- 3rd edition. John Wiley & Sons, 2013. ISBN: 978-1-118-53080-1
+1. Ralph Kimball and Margy Ross: *The Data Warehouse Toolkit: The Definitive Guide to Dimensional Modeling*, 3rd edition. John Wiley & Sons, 2013. ISBN: 978-1-118-53080-1
-1. Viktor Klang:
- “[I'm coining the phrase 'effectively-once' for message processing with at-least-once + idempotent operations](https://twitter.com/viktorklang/status/789036133434978304),”
- *twitter.com*, October 20, 2016.
+1. Viktor Klang: “[I'm coining the phrase 'effectively-once' for message processing with at-least-once + idempotent operations](https://twitter.com/viktorklang/status/789036133434978304),” *twitter.com*, October 20, 2016.
-1. Matei Zaharia, Tathagata Das, Haoyuan Li, et al.:
- “[Discretized Streams: An Efficient and Fault-Tolerant Model for Stream Processing on Large Clusters](https://www.usenix.org/system/files/conference/hotcloud12/hotcloud12-final28.pdf),” at
- *4th USENIX Conference in Hot Topics in Cloud Computing* (HotCloud), June 2012.
+1. Matei Zaharia, Tathagata Das, Haoyuan Li, et al.: “[Discretized Streams: An Efficient and Fault-Tolerant Model for Stream Processing on Large Clusters](https://www.usenix.org/system/files/conference/hotcloud12/hotcloud12-final28.pdf),” at *4th USENIX Conference in Hot Topics in Cloud Computing* (HotCloud), June 2012.
-1. Kostas Tzoumas, Stephan Ewen, and Robert Metzger:
- “[High-Throughput, Low-Latency, and Exactly-Once Stream Processing with Apache Flink](http://data-artisans.com/high-throughput-low-latency-and-exactly-once-stream-processing-with-apache-flink/),” *data-artisans.com*, August 5, 2015.
+1. Kostas Tzoumas, Stephan Ewen, and Robert Metzger: “[High-Throughput, Low-Latency, and Exactly-Once Stream Processing with Apache Flink](http://data-artisans.com/high-throughput-low-latency-and-exactly-once-stream-processing-with-apache-flink/),” *data-artisans.com*, August 5, 2015.
-1. Paris Carbone, Gyula Fóra, Stephan Ewen, et al.:
- “[Lightweight Asynchronous Snapshots for Distributed Dataflows](http://arxiv.org/abs/1506.08603),” arXiv:1506.08603 [cs.DC], June 29, 2015.
+1. Paris Carbone, Gyula Fóra, Stephan Ewen, et al.: “[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.
+1. Ryan Betts and John Hugg: *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.
+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.
1. Jason Gustafson, Flavio Junqueira, Apurva Mehta, Sriram Subramanian, and Guozhang Wang: “[KIP-98 – Exactly Once Delivery and Transactional Messaging](https://cwiki.apache.org/confluence/display/KAFKA/KIP-98+-+Exactly+Once+Delivery+and+Transactional+Messaging),” *cwiki.apache.org*, November 2016.
-1. Pat Helland:
- “[Idempotence Is Not a Medical Condition](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.401.1539&rep=rep1&type=pdf),” *Communications of the ACM*, volume 55, number 5, page 56, May 2012.
- [doi:10.1145/2160718.2160734](http://dx.doi.org/10.1145/2160718.2160734)
+1. Pat Helland: “[Idempotence Is Not a Medical Condition](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.401.1539&rep=rep1&type=pdf),” *Communications of the ACM*, volume 55, number 5, page 56, May 2012. [doi:10.1145/2160718.2160734](http://dx.doi.org/10.1145/2160718.2160734)
-1. Jay Kreps:
- “[Re: Trying to Achieve Deterministic Behavior on Recovery/Rewind](http://mail-archives.apache.org/mod_mbox/samza-dev/201409.mbox/%3CCAOeJiJg%2Bc7Ei%3DgzCuOz30DD3G5Hm9yFY%3DUJ6SafdNUFbvRgorg%40mail.gmail.com%3E),” email to *samza-dev* mailing list,
- September 9, 2014.
+1. Jay Kreps: “[Re: Trying to Achieve Deterministic Behavior on Recovery/Rewind](http://mail-archives.apache.org/mod_mbox/samza-dev/201409.mbox/%3CCAOeJiJg%2Bc7Ei%3DgzCuOz30DD3G5Hm9yFY%3DUJ6SafdNUFbvRgorg%40mail.gmail.com%3E),” email to *samza-dev* mailing list, September 9, 2014.
-1. E. N. (Mootaz) Elnozahy,
- Lorenzo Alvisi, Yi-Min Wang, and David B. Johnson:
- “[A Survey of Rollback-Recovery Protocols in Message-Passing Systems](http://www.cs.utexas.edu/~lorenzo/papers/SurveyFinal.pdf),” *ACM Computing Surveys*, volume 34, number 3,
- pages 375–408, September 2002.
- [doi:10.1145/568522.568525](http://dx.doi.org/10.1145/568522.568525)
+1. E. N. (Mootaz) Elnozahy, Lorenzo Alvisi, Yi-Min Wang, and David B. Johnson: “[A Survey of Rollback-Recovery Protocols in Message-Passing Systems](http://www.cs.utexas.edu/~lorenzo/papers/SurveyFinal.pdf),” *ACM Computing Surveys*, volume 34, number 3, pages 375–408, September 2002. [doi:10.1145/568522.568525](http://dx.doi.org/10.1145/568522.568525)
-1. Adam Warski:
- “[Kafka Streams – How Does It Fit the Stream Processing Landscape?](https://softwaremill.com/kafka-streams-how-does-it-fit-stream-landscape/),” *softwaremill.com*, June 1, 2016.
+1. Adam Warski: “[Kafka Streams – How Does It Fit the Stream Processing Landscape?](https://softwaremill.com/kafka-streams-how-does-it-fit-stream-landscape/),” *softwaremill.com*, June 1, 2016.
diff --git a/ch12.md b/ch12.md
index 59e6667..4511121 100644
--- a/ch12.md
+++ b/ch12.md
@@ -10,142 +10,144 @@
[TOC]
-到目前为止,本书主要描述的是目前的情况。在这最后一章中,我们将转向未来,讨论应该如何做:我将提出一些想法和方法,我相信这些方法可以从根本上改进我们设计和构建应用程序的方式。
+ 到目前为止,本书主要描述的是目前的情况。在这最后一章中,我们将转向未来,讨论应该如何做:我将提出一些想法和方法,我相信这些方法可以从根本上改进我们设计和构建应用的方式。
-对未来的看法和猜测当然是主观的,所以我在写这篇个人意见的时候会用到第一人称。我们欢迎您不同意并形成自己的观点,但我希望本章的观点至少可以成为一个富有成效的讨论的出发点,并为经常混淆的概念提供一些清晰。
+ 对未来的看法和猜测当然是主观的,所以我在写这篇个人意见的时候会用到第一人称。我们欢迎您不同意并形成自己的观点,但我希望本章的观点至少可以成为一个富有成效的讨论的出发点,并为经常混淆的概念提供一些清晰。
-第1章概述了本书的目标:探索如何创建可靠,可伸缩和可维护的应用程序和系统。这些主题贯穿了所有的章节:例如,我们讨论了许多有助于提高可靠性的容错算法,提高可扩展性的分区,以及提高可维护性的进化和抽象机制。在本章中,我们将把所有这些想法结合在一起,并以这些想法为基础来设想未来。我们的目标是发现如何设计比今天更好的应用程序——强大,正确,可演化,并最终对人类有益。
+ [第1章](ch1.md)概述了本书的目标:探索如何创建可靠,可扩展和可维护的应用和系统。这些主题贯穿了所有的章节:例如,我们讨论了许多有助于提高可靠性的容错算法,提高可扩展性的分区,以及提高可维护性的演化和抽象机制。在本章中,我们将把所有这些想法结合在一起,并以这些想法为基础来设想未来。我们的目标是发现如何设计比今天更好的应用程序——强大,正确,可演化,并最终对人类有益。
## 数据集成
-本书中反复出现的主题是,对于任何给定的问题,都有几种解决方案,所有这些解决方案都有不同的优点,缺点和折衷。例如,在[第3章](ch3.md)讨论存储引擎时,我们看到了日志结构存储,B树和列存储。在[第5章](ch5.md)讨论复制时,我们看到了单领导,多领导和无领导的方法。
+ 本书中反复出现的主题是,对于任何给定的问题,都有几种解决方案,所有这些解决方案都有不同的优点,缺点和权衡。例如,在[第3章](ch3.md)讨论存储引擎时,我们看到了日志结构存储,B树和列存储。在[第5章](ch5.md)讨论复制时,我们看到了单主,多主和无主的方法。
-如果你有一个问题,例如“我想存储一些数据并稍后再查询”,那么没有一个正确的解决方案,但是在不同的情况下,每种方法都是适当的。软件实现通常必须选择一种特定的方法。要使一个代码路径健壮并且很好地尝试在一个软件中执行所有操作几乎可以保证实现效果很差,这很难。
+ 如果你有一个类似于“我想存储一些数据并稍后再查询”的问题,那么并没有一种正确的解决方案。但对于不同的具体环境,总会有不同的合适方法。软件实现通常必须选择一种特定的方法。使用单一代码路径,且能做到稳定健壮,表现良好是一件非常困难的事情 —— 尝试在单个软件中完成所有事情,几乎可以保证实现效果会非常差。
-因此,软件工具的最合适的选择也取决于情况。每一个软件,甚至是所谓的“通用”数据库,都是针对特定的使用模式而设计的。
+ 因此,软件工具的最佳选择也取决于情况。每一个软件,甚至是所谓“通用”数据库,都是针对特定的使用模式而设计的。
-面对如此众多的替代品,第一个挑战就是弄清楚软件产品与其所处的环境之间的映射关系。供应商可以理解地不愿意告诉你他们的软件不适合的工作负载类型,但是希望以前的章节能够给你提供一些问题,以便在各行之间阅读并更好地理解这些权衡。
+ 面对如此众多的替代品,第一个挑战就是弄清楚软件产品与其所适应环境之间的映射关系。供应商可以理解地不愿意告诉你他们的软件不适合的工作负载类型,但是希望以前的章节能够给你提供一些问题,以便在各行之间阅读并更好地理解这些权衡。
-但是,即使您完全理解工具与其使用环境之间的映射,还有一个挑战:在复杂的应用程序中,数据通常以多种不同的方式使用。不太可能存在适用于所有不同数据使用环境的软件,因此您不可避免地需要拼凑几个不同的软件以提供您的应用程序的功能。
+ 但是,即使您完全理解工具与其使用环境之间的映射,还有一个挑战:在复杂的应用程序中,数据通常以多种不同的方式使用。不太可能存在适用于所有不同数据使用环境的软件,因此您不可避免地需要拼凑几个不同的软件以提供您的应用程序的功能。
### 组合使用衍生数据的工具
-例如,为了处理任意关键字的查询,需要将OLTP数据库与全文搜索索引集成在一起是很常见的。尽管一些数据库(如PostgreSQL)包含了全文索引功能,可以满足简单的应用需求【1】,但更复杂的搜索工具需要专业的信息检索工具。相反,搜索索引通常不适合作为一个持久的记录系统,因此许多应用程序需要结合两种不同的工具来满足所有要求。
+ 例如,为了处理任意关键字的查询,需要将OLTP数据库与全文搜索索引集成在一起是很常见的。尽管一些数据库(如PostgreSQL)包含了全文索引功能,可以满足简单的应用需求【1】,但更复杂的搜索工具需要专业的信息检索工具。相反,搜索索引通常不适合作为一个持久的记录系统,因此许多应用程序需要结合两种不同的工具来满足所有要求。
-我们谈到了将数据系统集成到“[使系统保持同步]()”(第452页)的问题。随着数据的不同表示数量的增加,集成问题变得更加困难。除了数据库和搜索索引之外,也许您需要保留分析系统(数据仓库或批处理和流处理系统)中的数据副本。维护从原始数据衍生的对象的高速缓存或非规范化版本;通过机器学习,分类,排名或推荐系统传递数据;或根据对数据的更改发送通知。
+ 我们谈到了将数据系统集成到“[使系统保持同步]()”(第452页)的问题。随着数据的不同表示数量的增加,集成问题变得更加困难。除了数据库和搜索索引之外,也许您需要保留分析系统(数据仓库或批处理和流处理系统)中的数据副本。维护从原始数据衍生的对象的高速缓存或非规范化版本;通过机器学习,分类,排名或推荐系统传递数据;或根据数据变更发送通知。
-令人惊讶的是,我经常看到软件工程师做出如下陈述:“根据我的经验,99%的人只需要X”或“......不需要X”(对于X的各种值)。我认为这样的陈述更多地讲述了讲话者的经验,而不是技术的实际有用性。您可能想要对数据执行的各种操作范围非常广泛。一个人认为是一个模糊和毫无意义的功能,可能是别人的核心要求。如果缩小数据流并考虑整个组织的数据流,那么对数据集成的需求往往就会变得明显。
+ 令人惊讶的是,我经常看到软件工程师做出如下陈述:“根据我的经验,99%的人只需要X”或者 “......不需要X”(对于各种各样的X)。我认为这样的陈述更多地是关于发言者自己的经验,而不是技术的实际有用性。对数据执行的各种操作,其范围可能极其宽广。某人认为鸡肋与毫无意义的功能,可能是别人的核心需求。当你拉高视角并考虑跨越整个组织范围的数据流时,对数据集成的需求往往就会变得明显。
#### 推理数据流
-当需要在多个存储系统中维护相同数据的副本以满足不同的访问模式时,您需要非常清楚输入和输出:哪些数据先写入,哪些表示来自哪些源?如何以正确的格式将数据导入所有正确的地方?
+ 当需要在多个存储系统中维护相同数据的副本以满足不同的访问模式时,您需要对输入和输出了如指掌:哪些数据先写入,哪些数据表示来自哪些来源?如何以正确的格式将数据导入所有正确的地方?
-例如,您可能会首先将数据写入记录数据库系统,捕获对该数据库所做的更改(请参阅第454页上的“[捕获变更数据]()”),然后将更改应用于数据库中的搜索索引相同的顺序。如果更改数据捕获(CDC)是更新索引的唯一方式,则可以确定该索引完全来自记录系统,因此与其保持一致(禁止软件中的错误)。写入数据库是向该系统提供新输入的唯一方式。
+ 例如,您可能会首先将数据写入**记录数据库**系统,捕获对该数据库所做的变更(参阅“[捕获数据变更](ch11.md#捕获数据变更)”),然后将变更应用于数据库中的搜索索引相同的顺序。如果变更数据捕获(CDC)是更新索引的唯一方式,则可以确定该索引完全派生自记录系统,因此与其保持一致(除软件错误外)。写入数据库是向该系统提供新输入的唯一方式。
-允许应用程序直接写入搜索索引和数据库引入了[图11-4]()所示的问题,其中两个客户端同时发送冲突写入,并且两个存储系统按不同顺序处理它们。在这种情况下,既不是数据库也不是
+ 允许应用程序直接写入搜索索引和数据库引入了如[图11-4](img/fig11-4.png)所示的问题,其中两个客户端同时发送冲突的写入,且两个存储系统按不同顺序处理它们。在这种情况下,既不是数据库说了算,也不是搜索索引说了算,所以它们做出了相反的决定,进入彼此间持久性的不一致状态。
-基于事件日志更新衍生数据系统通常可以做出决定性的和幂等的(参见第478页的“[幂等性]()”),使得从故障中恢复相当容易。
+ 如果您可以通过单个系统来提供所有用户输入,从而决定所有写入的排序,则通过按相同顺序处理写入,可以更容易地衍生出其他数据表示。 这是状态机复制方法的一个应用,我们在“[全序广播](ch9.md#全序广播)”中看到。无论您使用变更数据捕获还是事件源日志,都不如仅对全局顺序达成共识更重要。
+
+ 基于事件日志来更新衍生数据的系统,通常可以做到**确定性**与**幂等性**(参见第478页的“[幂等性]()”),使得从故障中恢复相当容易。
#### 衍生数据与分布式事务
-保持不同数据系统彼此一致的经典方法涉及分布式事务,如第354页的“[原子提交和两阶段提交(2PC)]()”中所述。与分布式事务相比,使用衍生数据系统的方法如何?
+ 保持不同数据系统彼此一致的经典方法涉及分布式事务,如“[原子提交和两阶段提交(2PC)](ch9.md#原子提交和两阶段提交(2PC))”中所述。与分布式事务相比,使用衍生数据系统的方法如何?
-在抽象层面,他们通过不同的方式达到类似的目标。分布式事务通过使用锁定进行互斥来决定写入的顺序(请参阅第257页上的“[两阶段锁定(2PL)]()”),而CDC和事件源使用日志进行排序。分布式事务使用原子提交来确保更改只生效一次,而基于日志的系统通常基于确定性重试和幂等性。
+ 在抽象层面,它们通过不同的方式达到类似的目标。分布式事务通过**锁**进行互斥来决定写入的顺序(参阅“[两阶段锁定(2PL)](ch7.md#两阶段锁定(2PL))”),而CDC和事件源使用日志进行排序。分布式事务使用原子提交来确保变更只生效一次,而基于日志的系统通常基于**确定性重试**和**幂等性**。
-最大的不同之处在于事务系统通常提供线性(请参阅第324页的“[线性一致性]()”),这意味着有用的保证,例如读取自己的写入(请参阅第162页的“[读己之写]()”)。另一方面,衍生数据系统通常是异步更新的,因此它们不会默认提供相同的时间保证。
+ 最大的不同之处在于事务系统通常提供**[线性一致性](ch9.md#线性一致性)**,这包含着有用的保证,例如[读己之写](ch5.md#读己之写)。另一方面,衍生数据系统通常是异步更新的,因此它们默认不会提供相同的时机保证。
-在愿意支付分布式交易成本的有限环境中,它们已被成功使用。但是,我认为XA的容错能力和性能特征较差(请参阅第364页的“[实践中的分布式事务]()”),这严重限制了它的实用性。我相信有可能为分布式事务创建一个更好的协议,但是将这样一个协议广泛采用并与现有工具集成将是具有挑战性的,并且不太可能很快发生。
+ 在愿意为分布式事务付出代价的有限场景中,它们已被成功应用。但是,我认为XA的容错能力和性能很差劲(参阅“[实践中的分布式事务](ch9.md#实践中的分布式事务)”),这严重限制了它的实用性。我相信为分布式事务设计一种更好的协议是可行的。但使这样一种协议被现有工具广泛接受是很有挑战的,且不是立竿见影的事。
-在没有广泛支持良好分布式事务协议的情况下,我认为基于日志的衍生数据是集成不同数据系统的最有前途的方法。然而,诸如阅读自己写作的保证是有用的,我认为告诉每个人“最终的一致性是不可避免的 —— 吸收它并学会处理它”是没有成果的(至少不是没有良好的指导如何处理它)。
+ 在没有广泛支持的良好分布式事务协议的情况下,我认为基于日志的衍生数据是集成不同数据系统的最有前途的方法。然而,诸如读己之写的保证是有用的,我认为告诉所有人“最终一致性是不可避免的 —— 忍一忍并学会和它打交道”是没有什么建设性的(至少在缺乏**如何**应对的良好指导时)。
-在第51页的“瞄准正确性”中,我们将讨论一些在异步衍生系统之上实现更强保障的方法,并在分布式事务和异步基于对数系统之间建立中间立场。
+ 在“[将事情做正确](#将事情做正确)”中,我们将讨论一些在异步衍生系统之上实现更强保障的方法,并迈向分布式事务和基于日志的异步系统之间的中间地带。
#### 全局有序的限制
-对于足够小的系统,构建一个完全有序的事件日志是完全可行的(正如单主复制数据库的流行所证明的那样,这正好构建了这样一个日志)。但是,随着系统向更大更复杂的工作负载扩展,限制开始出现:
+ 对于足够小的系统,构建一个完全有序的事件日志是完全可行的(正如单主复制数据库的流行所证明的那样,这正好建立了这样一种日志)。但是,随着系统向更大更复杂的工作负载扩展,限制开始出现:
-* 在大多数情况下,构建完全有序的日志需要所有事件通过决定订购的单个领导节点。如果事件吞吐量大于单台计算机可处理的事件,则需要将其分割到多台计算机上(请参见第446页的“[分区日志]()”)。然后,两个不同分区中的事件顺序不明确。
+* 在大多数情况下,构建完全有序的日志,需要所有事件通过决定顺序的单个领导节点。如果事件吞吐量大于单台计算机的处理能力,则需要将其分割到多台计算机上(参见“[分区日志](ch11.md#分区日志)”)。然后两个不同分区中的事件顺序关系就不明确了。
-* 如果服务器分布在多个地理位置分散的数据中心上,例如为了容忍整个数据中心脱机,您通常在每个数据中心都有单独的领导者,因为网络延迟会导致同步的跨数据中心协调效率低下(请参阅“[多主复制]()“)。这意味着源自两个不同数据中心的事件的未定义排序。
+* 如果服务器分布在多个**地理位置分散**的数据中心上,例如为了容忍整个数据中心掉线,您通常在每个数据中心都有单独的主库,因为网络延迟会导致同步的跨数据中心协调效率低下(请参阅“[多主复制](ch5.md#多主复制)“)。这意味着源自两个不同数据中心的事件顺序未定义。
-* 将应用程序部署为微服务时(请参阅第125页上的“[通过服务进行数据流:REST和RPC]()”),常见的设计选择是将每个服务及其持久状态作为独立单元进行部署,服务之间不共享持久状态。当两个事件来自不同的服务时,这些事件没有定义的顺序。
+* 将应用程序部署为微服务时(请参阅第125页上的“[服务中的数据流:REST与RPC](ch4.md#服务中的数据流:REST与RPC)”),常见的设计选择是将每个服务及其持久状态作为独立单元进行部署,服务之间不共享持久状态。当两个事件来自不同的服务时,这些事件间的顺序未定义。
-* 某些应用程序保持客户端状态,该状态在用户输入时立即更新(无需等待服务器确认),甚至可以继续脱机工作(请参阅第170页的“[脱机操作的客户端]()”)。有了这样的应用程序,客户端和服务器很可能以不同的顺序看到事件。
+* 某些应用程序在客户端保存状态,该状态在用户输入时立即更新(无需等待服务器确认),甚至可以继续脱机工作(参阅“[脱机操作的客户端](ch5.md#脱机操作的客户端)”)。有了这样的应用程序,客户端和服务器很可能以不同的顺序看到事件。
-在形式上,决定事件的总次序称为总次序广播,相当于共识(请参阅第366页上的“[共识算法和全序广播]()”)。大多数共识算法都是针对单个节点的吞吐量足以处理整个事件流的情况而设计的,并且这些算法不提供多个节点共享事件排序工作的机制。设计共识算法仍然是一个开放的研究问题,它可以扩展到单个节点的吞吐量之外,并且在地理上分散的环境中工作良好。
+ 在形式上,决定事件的全局顺序称为**全序广播**,相当于**共识**(参阅“[共识算法和全序广播](ch9.md#共识算法和全序广播)”)。大多数共识算法都是针对单个节点的吞吐量足以处理整个事件流的情况而设计的,并且这些算法不提供多个节点共享事件排序工作的机制。设计可以扩展到单个节点的吞吐量之上,且在地理散布环境中仍然工作良好的的共识算法仍然是一个开放的研究问题。
-#### 订购事件以捕捉因果关系
+#### 排序事件以捕捉因果关系
-在事件之间不存在因果关系的情况下,缺乏全部命令并不是一个大问题,因为并发事件可以任意排序。其他一些情况很容易处理:例如,当同一对象有多个更新时,它们可以通过将特定对象ID的所有更新路由到相同的日志分区来完全排序。然而,因果关系有时会以更微妙的方式出现(另请参阅“[顺序和因果关系]()”第319页)。
+ 在事件之间不存在因果关系的情况下,缺乏全局顺序并不是一个大问题,因为并发事件可以任意排序。其他一些情况很容易处理:例如,当同一对象有多个更新时,它们可以通过将特定对象ID的所有更新路由到相同的日志分区来完全排序。然而,因果关系有时会以更微妙的方式出现(参阅“[顺序和因果关系](ch8.md#顺序和因果关系)”)。
-例如,考虑一个社交网络服务,以及两个相互关系但刚分手的用户。其中一个用户将另一个作为朋友移除,然后向其余的朋友发送消息,抱怨他们的前伴侣。用户的意图是他们的前配偶不应该看到粗鲁的信息,因为信息是在朋友状态被撤销后发送的。
+ 例如,考虑一个社交网络服务,以及一对曾处于恋爱关系但刚分手的用户。其中一个用户将另一个用户从好友中移除,然后向剩余的好友发送消息,抱怨他们的前任。用户的心思是他们的前任不应该看到这些粗鲁的消息,因为消息是在好友状态解除后发送的。
-但是,在一个地方存储友谊状态并在另一个地方存储消息的系统中,不友好事件和消息发送事件之间的顺序依赖关系可能会丢失。如果未捕获到因果依赖关系,则发送有关新消息的通知的服务可能会在不友好事件之前处理消息发送事件,从而错误地向前伙伴发送通知。
+ 但是如果好友关系状态与消息存储在不同的地方,在这样一个系统中,可能会出现**解除好友**事件与**发送消息**事件之间的因果依赖丢失的情况。如果因果依赖关系没有被捕捉到,则发送有关新消息的通知的服务可能会在**解除好友**事件之前处理**发送消息**事件,从而错误地向前任发送通知。
-在本例中,通知实际上是消息和好友列表之间的连接,使得它与我们先前讨论的连接的时间问题有关(请参阅第475页的“[连接的时间依赖性]()”)。不幸的是,这个问题似乎并没有一个简单的答案【2,3】。起点包括:
+ 在本例中,通知实际上是消息和好友列表之间的连接,使得它与我们先前讨论的连接的时间问题有关(请参阅第475页的“[连接的时间依赖性](ch11.md#连接的时间依赖性)”)。不幸的是,这个问题似乎并没有一个简单的答案【2,3】。起点包括:
-* 逻辑时间戳可以提供没有协调的全部订购(请参见“[序列号排序]()”第页343),因此它们可能有助于全序广播不可行的情况。但是,他们仍然要求收件人处理不按顺序发送的事件,并且需要传递其他元数据。
+* 逻辑时间戳可以提供无需协调的全局顺序(参见“[序列号排序](ch8.md#序列号排序)”),因此它们可能有助于全序广播不可行的情况。但是,他们仍然要求收件人处理不按顺序发送的事件,并且需要传递其他元数据。
* 如果您可以记录一个事件来记录用户在做出决定之前所看到的系统状态,并给该事件一个唯一的标识符,那么后面的任何事件都可以引用该事件标识符来记录因果关系【4】 。我们将在第513页的“[读也是事件](#读也是事件)”中回到这个想法。
-* 冲突解决算法(请参阅“[自动冲突解决]()”(第165页))有助于处理以意外顺序传递的事件。它们对于维护状态很有用,但如果行为有外部副作用(例如,也许,随着时间的推移,应用程序开发模式将出现,使得能够有效地捕获因果依赖关系,并且保持正确的衍生状态,而不会迫使所有事件经历全部命令广播的瓶颈。
+* 冲突解决算法(请参阅“[自动冲突解决](ch5.md#自动冲突解决)”)有助于处理以意外顺序传递的事件。它们对于维护状态很有用,但如果行为有外部副作用(例如,也许,随着时间的推移,应用程序开发模式将出现,使得能够有效地捕获因果依赖关系,并且保持正确的衍生状态,而不会迫使所有事件经历全序广播的瓶颈。
### 批量处理与流处理
-我会说数据集成的目标是确保数据在所有正确的地方以正确的形式结束。这样做需要消耗投入,转化,加入,过滤,汇总,培训模型,评估并最终写入适当的输出。批处理和流处理器是实现这一目标的工具。
+ 我会说数据集成的目标是,确保数据最终能在所有正确的地方表现出正确的形式。这样做需要消费输入,转换,连接,过滤,聚合,训练模型,评估,以及最终写出适当的输出。批处理和流处理是实现这一目标的工具。
-批处理和流处理的输出是衍生的数据集,例如搜索索引,实例化视图,向用户显示的建议,聚合度量等(请参阅“[批处理工作流的输出]()”(第417页)和“[流处理的用法]()”第465页)。
+ 批处理和流处理的输出是衍生的数据集,例如搜索索引,实例化视图,向用户显示的建议,聚合度量等(请参阅“[批处理工作流的输出](ch10.md#批处理工作流的输出)”和“[流处理的用法](ch11.md#流处理的用法)”)。
-正如我们在[第10章](ch10.md)和[第11章](ch11.md)中看到的,批处理和流处理有许多共同的原则,主要的根本区别在于流处理器在无界数据集上运行,而批处理输入是已知的有限大小。处理引擎的实现方式也有很多细节上的差异,但是这些区别开始模糊。
+ 正如我们在[第10章](ch10.md)和[第11章](ch11.md)中看到的,批处理和流处理有许多共同的原则,主要的根本区别在于流处理器在无界数据集上运行,而批处理输入是已知的有限大小。处理引擎的实现方式也有很多细节上的差异,但是这些区别开始模糊。
-Spark在批处理引擎上执行流处理,将流分解为微格式,而Apache Flink则在流处理引擎上执行批处理【5】。原则上,一种类型的处理可以在另一种类型上仿真,但是性能特征会有所不同:例如,在跳跃或滑动窗口时,微博可能表现不佳【6】。
+ Spark在批处理引擎上执行流处理,将流分解为**微批量(microbatches)**,而Apache Flink则在流处理引擎上执行批处理【5】。原则上,一种类型的处理可以在另一种类型上仿真,但是性能特征会有所不同:例如,在跳跃或滑动窗口时,**小批量**可能表现不佳【6】。
-#### 保持衍生状态
+#### 维护衍生状态
-批处理具有非常强大的功能特性(即使代码不是用函数式编程语言编写的):它鼓励确定性的纯函数,其输出仅依赖于输入,除了显式输出外没有副作用,处理输入作为不可变的,并作为附加的输出。流处理类似,但它扩展了运算符以允许受管理的容错状态(请参阅第478页的“[重建失败后的状态”]())。
+ 批处理有着很强的函数式风格(即使其代码不是用函数式语言编写的):它鼓励确定性的纯函数,其输出仅依赖于输入,除了显式输出外没有副作用,将输入视作不可变的,且输出是仅追加的。流处理与之类似,但它扩展了运算符以允许受管理的容错状态(请参阅第478页的“[失败后重建状态”](ch11.md#失败后重建状态))。
-具有良好定义的输入和输出的确定性函数的原理不仅有利于容错(请参见第478页的“[幂等性]()”),但也简化了有关组织中数据流的推理【7】。无论衍生数据是搜索索引,统计模型还是缓存,从数据管道角度来看,从另一个衍生出一件事情,通过功能应用程序代码推送一个系统中的状态更改和应用对衍生系统的影响。
+ 具有良好定义的输入和输出的确定性函数的原理不仅有利于容错(参见“[幂等性](ch11.md#幂等性)”),但也简化了有关组织中数据流的推理【7】。无论衍生数据是搜索索引,统计模型还是缓存,从数据管道角度来看,从另一个衍生出一件事情,通过功能应用程序代码推送一个系统中的状态变更和应用对衍生系统的影响。
-原则上,衍生数据系统可以同步维护,就像关系数据库在与被索引表写入操作相同的事务中同步更新辅助索引一样。然而,异步是基于事件日志的系统稳健的原因:它允许系统的一部分故障被本地包含,而如果任何一个参与者失败,分布式事务将中止,因此他们倾向于通过将故障扩展到系统的其余部分(请参阅第363页的“[分布式事务的限制]()”)。
+ 原则上,衍生数据系统可以同步地维护,就像关系数据库在与被索引表写入操作相同的事务中同步更新辅助索引一样。然而,异步是基于事件日志的系统稳健的原因:它允许系统的一部分故障被抑制在本地,而如果任何一个参与者失败,分布式事务将中止,因此它们倾向于通过将故障传播到系统的其余部分来放大故障(请参阅“[分布式事务的限制](ch9.md#分布式事务的限制)”)。
-我们在第206页的“分区和二级索引”中看到,二级索引经常跨越分区边界。具有二级索引的分区系统需要将写入发送到多个分区(如果索引是分词)或将读取发送到所有分区(如果索引是文档分区的话)。如果索引是异步维护的,这种交叉分区通信也是最可靠和可扩展的【8】(另请参阅“[多分区数据处理]()”第479页)。
+ 我们在“[分区和二级索引](ch6.md#分区和二级索引)”中看到,二级索引经常跨越分区边界。具有二级索引的分区系统需要将写入发送到多个分区(如果索引是分词)或将读取发送到所有分区(如果索引是文档分区的话)。如果索引是异步维护的,这种交叉分区通信也是最可靠和可扩展的【8】(另请参阅“[多分区数据处理](ch11.md#多分区数据处理)”)。
-#### 为应用程序演变重新处理数据
+#### 应用演化后重新处理数据
-在维护衍生数据时,批处理和流处理都是有用的。流处理允许将输入中的变化以低延迟反映在衍生视图中,而批处理允许重新处理大量累积的历史数据以便将新视图导出到现有数据集上。
+ 在维护衍生数据时,批处理和流处理都是有用的。流处理允许将输入中的变化以低延迟反映在衍生视图中,而批处理允许重新处理大量累积的历史数据以便将新视图导出到现有数据集上。
-特别是,重新处理现有数据为维护系统提供了一个良好的机制,并将其发展为支持新功能和变更需求(参见[第4章](ch4.md))。如果不进行重新处理,模式演化就会局限于简单的变化,例如向记录中添加新的可选字段或添加新类型的记录。无论是在写模式还是在读模式中都是如此(请参阅第39页的“[文档模型中的模式灵活性]()”)。另一方面,通过重新处理,可以将数据集重组为一个完全不同的模型,以便更好地满足新的要求。
+ 特别是,重新处理现有数据为维护系统提供了一个良好的机制,演化并支持新功能和需求变更(参见[第4章](ch4.md))。不需要重新进行处理,模式演化仅限于简单的变化,例如向记录中添加新的可选字段或添加新类型的记录。无论是在写模式还是在读模式中都是如此(参阅“[文档模型中的模式灵活性](ch2.md#文档模型中的模式灵活性)”)。另一方面,通过重新处理,可以将数据集重组为一个完全不同的模型,以便更好地满足新的要求。
-> ### 在铁路上的模式迁移
+> ### 铁路上的模式迁移
>
-> 大规模的“模式迁移”也发生在非计算机系统中。例如,在19世纪英国铁路建设初期,轨距(两轨之间的距离)就有了各种各样的交易标准。为一个测量仪而建的列车不能在另一个测量仪的轨道上运行,这限制了火车网络中可能的相互连接【9】。
+> 大规模的“模式迁移”也发生在非计算机系统中。例如,在19世纪英国铁路建设初期,轨距(两轨之间的距离)就有了各种各样的竞争标准。为一种轨距而建的列车不能在另一种轨距的轨道上运行,这限制了火车网络中可能的相互连接【9】。
>
-> 在1846年最终确定了一个标准仪表之后,其他仪表的轨道必须转换 —— 但是如何在不关闭火车线路的情况下进行数月甚至数年?解决的办法是首先将轨道转换为双轨或混合轨距,方法是增加第三轨。这种转换可以逐渐完成,当完成时,两个仪表的列车可以在三条轨道中的两条轨道上运行。事实上,一旦所有的列车都转换成标准轨距,那么可以移除提供非标准轨距的轨道。
+> 在1846年最终确定了一个标准轨距之后,其他轨距的轨道必须转换 —— 但是如何在不停运火车线路的情况下进行数月甚至数年的迁移?解决的办法是首先通过添加第三条轨道将轨道转换为**双轨距(dual guage)**或**混合轨距**。这种转换可以逐渐完成,当完成时,两种轨距的列车都可以在线路上跑,使用三条轨道中的两条。事实上,一旦所有的列车都转换成标准轨距,那么可以移除提供非标准轨距的轨道。
>
-> 以这种方式“再加工”现有的轨道,让新旧版本并存,可以在几年的时间内逐渐改变轨距。然而,这是一项昂贵的事业,这就是今天非标准仪表仍然存在的原因。例如,旧金山湾区的BART系统使用与美国大部分地区不同的仪表。
+> 以这种方式“再加工”现有的轨道,让新旧版本并存,可以在几年的时间内逐渐改变轨距。然而,这是一项昂贵的事业,这就是今天非标准轨距仍然存在的原因。例如,旧金山湾区的BART系统使用与美国大部分地区不同的轨距。
-衍生视图允许逐步演变。如果您想重新构建数据集,则不需要执行迁移作为突然切换。相反,您可以将旧架构和新架构并排维护为相同基础数据上的两个独立衍生视图。然后,您可以开始将少量用户转移到新视图,以测试其性能并发现任何错误,而大多数用户仍然会被路由到旧视图。逐渐地,您可以增加访问新视图的用户比例,最终您可以删除旧视图【10】。
+ 衍生视图允许**渐进演化(gradual evolution)**。如果您想重新构建数据集,不需要执行迁移,例如**突然切换**。相反,您可以将旧架构和新架构并排维护为相同基础数据上的两个独立衍生视图。然后,您可以开始将少量用户转移到新视图,以测试其性能并发现任何错误,而大多数用户仍然会被路由到旧视图。逐渐地,您可以增加访问新视图的用户比例,最终您可以删除旧视图【10】。
-这种逐渐迁移的美妙之处在于,如果出现问题,每个阶段的过程都很容易逆转:您始终有一个可以回到的工作系统。通过降低不可逆损害的风险,您可以更有信心继续前进,从而更快地改善您的系统【11】。
+ 这种逐渐迁移的美妙之处在于,如果出现问题,每个阶段的过程都很容易逆转:您始终有一个可以回滚的可用系统。通过降低不可逆损害的风险,您可以更有信心继续前进,从而更快地改善您的系统【11】。
#### Lambda架构
-如果批处理用于重新处理历史数据,并且流处理用于处理最近的更新,那么您如何将这两者结合起来?拉姆达体系结构【12】是这方面的一个建议,引起了很多关注。
+ 如果批处理用于重新处理历史数据,并且流处理用于处理最近的更新,那么您如何将这两者结合起来?Lambda架构【12】是这方面的一个建议,引起了很多关注。
-lambda体系结构的核心思想是通过将不可变事件附加到不断增长的数据集来记录传入数据,这类似于事件源(请参阅第457页上的“[事件源]()”)。从这些事件中,推导出读取优化的视图。 Lambda体系结构建议并行运行两个不同的系统:批处理系统(如Hadoop MapReduce)和独立的流处理系统(如Storm)。
+ lambda体系结构的核心思想是通过将不可变事件附加到不断增长的数据集来记录传入数据,这类似于事件源(参阅“[事件源](ch11.md#事件源)”)。从这些事件中,推导出读取优化的视图。 Lambda体系结构建议并行运行两个不同的系统:批处理系统(如Hadoop MapReduce)和独立的流处理系统(如Storm)。
-在lambda方法中,流处理器消耗事件并快速生成对视图的近似更新;批处理器稍后将使用同一组事件并生成衍生视图的更正版本。这个设计背后的原因是批处理更简单,因此不易出错,而流处理器被认为是不太可靠和难以容错的(请参阅“[故障容错]()”)。而且,流处理可以使用快速近似算法,而批处理使用较慢的精确算法。
+ 在lambda方法中,流处理器消耗事件并快速生成对视图的近似更新;批处理器稍后将使用同一组事件并生成衍生视图的更正版本。这个设计背后的原因是批处理更简单,因此不易出错,而流处理器被认为是不太可靠和难以容错的(请参阅“[故障容错]()”)。而且,流处理可以使用快速近似算法,而批处理使用较慢的精确算法。
-拉姆达体系结构是一个有影响力的想法,它将数据系统的设计变得更好,尤其是通过推广将视图衍生到不可变事件流和在需要时重新处理事件的原则。但是,我也认为它有一些实际问题:
+ Lambda架构是一个有影响力的想法,它将数据系统的设计变得更好,尤其是通过推广这样的原则:在不可变事件流上建立衍生视图,并在需要时重新处理事件。但是我也认为它有一些实际问题:
-* 必须保持相同的逻辑才能在批处理和流处理框架中运行,这是额外的工作。虽然像Summingbird 【13】这样的库提供了一个抽象的计算,可以在一个批处理或流的上下文中运行,调试,调整和维护两个不同系统的操作复杂性仍然【14】。
-* 由于流管道和批处理管道产生单独的输出,因此需要合并它们以响应用户请求。如果计算是通过滚动窗口的简单聚合,则合并相当容易,但如果使用更复杂的操作(例如连接和会话化)导出视图,或者输出不是时间序列,则显得非常困难。
-* 尽管有能力重新处理整个历史数据集是很好的,但在大型数据集上这样做经常会很昂贵。因此,批处理流水线通常需要设置为处理增量批处理(例如,在每小时结束时处理一小时的数据),而不是重新处理所有内容。这引发了第468页的“[关于时间的推理]()”中讨论的问题,例如处理分段器和处理跨批次边界的窗口。增加批量计算会增加复杂性,使其更类似于流式传输层,这与保持批处理层尽可能简单的目标背道而驰。
+* 在批处理和流处理框架中维护相同的逻辑是很显著的额外工作。虽然像Summingbird 【13】这样的库提供了一种可以在批处理和流处理的上下文中运行的计算抽象。调试,调整和维护两个不同系统的操作复杂性依然存在【14】。
+* 由于流管道和批处理管道产生独立的输出,因此需要合并它们以响应用户请求。如果计算是基于滚动窗口的简单聚合,则合并相当容易,但如果视图基于更复杂的操作(例如连接和会话化)而导出,或者输出不是时间序列,则会变得非常困难。
+* 尽管有能力重新处理整个历史数据集是很好的,但在大型数据集上这样做经常会开销巨大。因此,批处理流水线通常需要设置为处理增量批处理(例如,在每小时结束时处理一小时的数据),而不是重新处理所有内容。这引发了“[关于时间的推理](ch11.md#关于时间的推理)”中讨论的问题,例如处理分段器和处理跨批次边界的窗口。增加批量计算会增加复杂性,使其更类似于流式传输层,这与保持批处理层尽可能简单的目标背道而驰。
#### 统一批处理和流处理
-最近的工作使得lambda体系结构的优点在没有其缺点的情况下得以实现,允许批处理计算(重新处理历史数据)和流计算(处理事件到达时)在同一个系统中实现【15】。
+ 最近的工作使得lambda体系结构的优点在没有其缺点的情况下得以实现,允许批处理计算(重新处理历史数据)和流计算(处理事件到达时)在同一个系统中实现【15】。
在一个系统中统一批处理和流处理需要以下功能,这些功能越来越广泛:
@@ -157,304 +159,302 @@ lambda体系结构的核心思想是通过将不可变事件附加到不断增
+## 分拆数据库
+ 在最抽象的层面上,数据库,Hadoop和操作系统都发挥相同的功能:它们存储一些数据,并允许你处理和查询这些数据【16】。数据库将数据存储为特定数据模型的记录(表中的行、文档、图中的顶点等),而操作系统的文件系统则将数据存储在文件中 —— 但其核心都是“信息管理”系统【17】。正如我们在[第10章](ch10.md)中看到的,Hadoop生态系统有点像Unix的分布式版本。
-## 拆分数据库
+ 当然,有很多实际的差异。例如,许多文件系统都不能很好地处理包含1000万个小文件的目录,而包含1000万个小记录的数据库完全是寻常而不起眼的。无论如何,操作系统和数据库之间的相似之处和差异值得探讨。
-在最抽象的层面上,数据库,Hadoop和操作系统都执行相同的功能:它们存储一些数据,并允许您处理和查询数据【16】。数据库将数据存储在某些数据模型(表中的文档,文档中的顶点,图形中的顶点等)的记录中,而操作系统的文件系统则将数据存储在文件中——但在其核心上,都是“信息管理”系统【17】。正如我们在[第10章](ch10.md)中看到的,Hadoop生态系统有点像Unix的分布式版本。
+ Unix和关系数据库以非常不同的哲学来处理信息管理问题。 Unix认为它的目的是为程序员提供一种相当低层次的硬件的逻辑抽象,而关系数据库则希望为应用程序员提供一种高层次的抽象,以隐藏磁盘上数据结构的复杂性,并发性,崩溃恢复以及等等。 Unix发展出的管道和文件只是字节序列,而数据库则发展出了SQL和事务。
-当然,有很多实际的差异。例如,许多文件系统不能很好地处理包含1000万个小文件的目录,而包含1000万个小记录的数据库是完全正常且不起眼的。无论如何,操作系统和数据库之间的相似之处和差异值得探讨。
+ 哪种方法更好?当然这取决于你想要的是什么。 Unix是“简单的”,因为它是硬件资源相当薄的包装;关系数据库是“更简单”的,因为一个简短的声明性查询可以利用很多强大的基础设施(查询优化,索引,连接方法,并发控制,复制等),而不需要查询的作者理解其实现细节。
-Unix和关系数据库以非常不同的哲学来处理信息管理问题。 Unix认为它的目的是为程序员提供一种逻辑但相当低层次的硬件抽象,而关系数据库则希望为应用程序员提供一种高层次的抽象,以隐藏磁盘上数据结构的复杂性,并发性,崩溃恢复以及等等。 Unix开发的管道和文件只是字节序列,而数据库则开发了SQL和事务。
+ 这些哲学之间的矛盾已经持续了几十年(Unix和关系模型都出现在70年代初),仍然没有解决。例如,我将NoSQL运动解释为,希望将类Unix的低级别抽象方法应用于分布式OLTP数据存储的领域。
-哪种方法更好?当然,这取决于你想要的。 Unix是“简单的”,因为它是硬件资源相当薄的包装;关系数据库是“更简单”的,因为一个简短的声明性查询可以利用很多强大的基础设施(查询优化,索引,连接方法,并发控制,复制等),而不需要查询作者理解实施细节。
-
-这些哲学之间的矛盾已经持续了几十年(Unix和关系模型都出现在70年代初),仍然没有解决。例如,我将NoSQL运动解释为希望将低级别抽象方法应用于分布式OLTP数据存储领域。
-
-在这一部分我将试图调和这两个哲学,希望我们可以结合两全其美。
+ 在这一部分我将试图调和这两个哲学,希望我们能各取其美。
### 组合使用数据存储技术
在本书的过程中,我们讨论了数据库提供的各种功能及其工作原理,其中包括:
-* 二级索引,使您可以根据字段的值有效地搜索记录(请参阅第79页上的“[其他索引结构]()”)
-* 物化视图,这是一种预先计算的查询结果缓存(请参阅“[聚合:数据立方体和物化视图]()”,第101页)
-* 复制日志,保持其他节点上数据的副本最新(请参阅第158页中的“[复制日志的实现]()”)
-* 全文搜索索引,允许在文本中进行关键字搜索(请参见第88页上的“[全文搜索和模糊索引]()”)以及内置于某些关系数据库【1】
+* 次级索引,使您可以根据字段的值有效地搜索记录(参阅“[其他索引结构](ch3.md#其他索引结构)”)
+* 物化视图,这是一种预计算的查询结果缓存(参阅“[聚合:数据立方体和物化视图](ch3.md#聚合:数据立方体和物化视图)”)
+* 复制日志,保持其他节点上数据的副本最新(参阅“[复制日志的实现](ch5.md#复制日志的实现)”)
+* 全文搜索索引,允许在文本中进行关键字搜索(参见“[全文搜索和模糊索引](ch3.md#全文搜索和模糊索引)”)内置于某些关系数据库【1】
-在第十章和第十一章中,出现了类似的主题。我们讨论了如何构建全文搜索索引(请参阅第357页上的“[批处理工作流的输出]()”),了解有关实例化视图维护(请参阅“[维护实例化视图]()”一节第437页)以及有关将更改从数据库复制到衍生数据系统(请参阅第454页的“[更改数据捕获]()”)。
+ 在[第10章](ch10.md)和[第11章](ch11.md)中,出现了类似的主题。我们讨论了如何构建全文搜索索引(请参阅第357页上的“[批处理工作流的输出]()”),了解有关实例化视图维护(请参阅“[维护实例化视图]()”一节第437页)以及有关将变更从数据库复制到衍生数据系统(请参阅第454页的“[变更数据捕获]()”)。
-数据库中内置的功能与人们用批处理和流处理器构建的衍生数据系统似乎有相似之处。
+ 数据库中内置的功能与人们用批处理和流处理器构建的衍生数据系统似乎有相似之处。
-#### 创建一个索引
+#### 创建索引
-想想当你运行`CREATE INDEX`在关系数据库中创建一个新的索引时会发生什么。数据库必须扫描表的一致性快照,挑选出所有被索引的字段值,对它们进行排序,然后写出索引。然后它必须处理自一致快照以来所做的写入操作(假设表在创建索引时未被锁定,所以写操作可能会继续)。一旦完成,只要事务写入表中,数据库就必须继续保持索引最新。
+ 想想当你运行`CREATE INDEX`在关系数据库中创建一个新的索引时会发生什么。数据库必须扫描表的一致性快照,挑选出所有被索引的字段值,对它们进行排序,然后写出索引。然后它必须处理自一致快照以来所做的写入操作(假设表在创建索引时未被锁定,所以写操作可能会继续)。一旦完成,只要事务写入表中,数据库就必须继续保持索引最新。
-此过程非常类似于设置新的追随者副本(请参阅第155页的“[设置新的追随者]()”),也非常类似于流系统中的引导更改数据捕获(请参阅第455页的“[初始快照]()”)。
+ 此过程非常类似于设置新的从库副本(参阅“[设置新的追随者]()”),也非常类似于流处理系统中的**引导(bootstrap)**变更数据捕获(请参阅第455页的“[初始快照]()”)。
-无论何时运行CREATE INDEX,数据库都会重新处理现有数据集(如第494页的“[重新处理应用程序数据的演变数据]()”中所述),并将该索引作为新视图导出到现有数据上。现有数据可能是状态的快照,而不是所有发生变化的日志,但两者密切相关(请参阅“[状态,数据流和不变性]()”第459页)。
+ 无论何时运行`CREATE INDEX`,数据库都会重新处理现有数据集(如第494页的“[重新处理应用程序数据的演变数据]()”中所述),并将该索引作为新视图导出到现有数据上。现有数据可能是状态的快照,而不是所有发生变化的日志,但两者密切相关(请参阅“[状态,数据流和不变性]()”第459页)。
#### 一切的元数据库
-有鉴于此,我认为整个组织的数据流开始像一个巨大的数据库【7】。每当批处理,流或ETL过程将数据从一个地方传输到另一个地方并形成表单时,就像数据库子系统一样,使索引或物化视图保持最新。
+ 有鉴于此,我认为整个组织的数据流开始像一个巨大的数据库【7】。每当批处理,流或ETL过程将数据从一个地方传输到另一个地方并组装时,它表现地就像数据库子系统一样,使索引或物化视图保持最新。
-像这样看,批处理和流处理器就像触发器,存储过程和物化视图维护例程的精细实现。他们维护的衍生数据系统就像不同的索引类型。例如,关系数据库可能支持B树索引,散列索引,空间索引(请参阅第79页的“[多列索引]()”)以及其他类型的索引。在新兴的衍生数据系统体系结构中,不是将这些设施作为单个集成数据库产品的功能实现,而是由各种不同的软件提供,运行在不同的机器上,由不同的团队管理。
+ 从这种角度来看,批处理和流处理器就像触发器,存储过程和物化视图维护例程的精细实现。他们维护的衍生数据系统就像不同的索引类型。例如,关系数据库可能支持B树索引,散列索引,空间索引(请参阅第79页的“[多列索引]()”)以及其他类型的索引。在新兴的衍生数据系统架构中,不是将这些设施作为单个集成数据库产品的功能实现,而是由各种不同的软件提供,运行在不同的机器上,由不同的团队管理。
-这些发展在未来将会把我们带到哪里?如果我们从没有适合所有访问模式的单一数据模型或存储格式的前提出发,我推测有两种途径可以将不同的存储和处理工具组合成一个有凝聚力的系统:
+ 这些发展在未来将会把我们带到哪里?如果我们从没有适合所有访问模式的单一数据模型或存储格式的前提出发,我推测有两种途径可以将不同的存储和处理工具组合成一个有凝聚力的系统:
**联合数据库:统一读取**
-可以为各种各样的底层存储引擎和处理方法提供一个统一的查询接口 —— 一种称为联邦数据库或多存储的方法【18,19】。例如,PostgreSQL的外部数据包装功能符合这种模式【20】。需要专用数据模型或查询接口的应用程序仍然可以直接访问底层存储引擎,而想要组合来自不同位置的数据的用户可以通过联合接口轻松完成操作。
+ 可以为各种各样的底层存储引擎和处理方法提供一个统一的查询接口 —— 一种称为**联合数据库(federated database)**或**多态存储(polystore)**的方法【18,19】。例如,PostgreSQL的外部数据包装器功能符合这种模式【20】。需要专用数据模型或查询接口的应用程序仍然可以直接访问底层存储引擎,而想要组合来自不同位置的数据的用户可以通过联合接口轻松完成操作。
-联合查询接口遵循单一集成系统的关系传统,具有高级查询语言和优雅的语义,但却是一个复杂的实现。
+ 联合查询接口遵循着单一集成系统与关系型模型的传统,带有高级查询语言和优雅的语义,但实现起来非常复杂。
-**非捆绑数据库:统一写入**
+**分拆数据库:统一写入**
-虽然联合会解决了跨多个不同系统的只读查询问题,但它并没有很好的解决跨系统同步写入的问题。我们说过,在单个数据库中,创建一致的索引是一项内置功能。当我们构建多个存储系统时,我们同样需要确保所有数据更改都会在所有正确的位置结束,即使在出现故障时也是如此。将存储系统可靠地插接在一起(例如,通过更改数据捕获和事件日志)更容易,就像将数据库的索引维护功能以可以跨不同技术同步写入的方式分开【7,21】。
+ 虽然联合能解决跨多个不同系统的只读查询问题,但它并没有很好的解决跨系统**同步**写入的问题。我们说过,在单个数据库中,创建一致的索引是一项内置功能。当我们构建多个存储系统时,我们同样需要确保所有数据变更都会在所有正确的位置结束,即使在出现故障时也是如此。将存储系统可靠地插接在一起(例如,通过变更数据捕获和事件日志)更容易,就像将数据库的索引维护功能以可以跨不同技术同步写入的方式分开【7,21】。
-非捆绑方法遵循Unix传统的小型工具,它可以很好地完成一件事【22】,通过统一的低级API(管道)进行通信,并且可以使用更高级别的语言(shell)【16】 。
+ 分拆方法遵循Unix传统的小型工具,它可以很好地完成一件事【22】,通过统一的低级API(管道)进行通信,并且可以使用更高级的语言进行组合(shell)【16】 。
#### 开展分拆工作
-联邦和非捆绑是同一个硬币的两个方面:用不同的组件构成可靠,可扩展和可维护的系统。联合只读联邦和非捆绑是同一个硬币的两个方面:用不同的组件构成可靠,可扩展和可维护的系统。联合只读查询需要将一个数据模型映射到另一个数据模型,这需要一些思考,但最终还是一个可管理的问题。我认为保持写入到几个存储系统是同步的更困难的工程问题,所以我将重点关注它。
+ 联合和分拆是一个硬币的两面:用不同的组件构成可靠,可扩展和可维护的系统。联合只读查询需要将一个数据模型映射到另一个数据模型,这需要一些思考,但最终还是一个可解决的问题。我认为同步写入到几个存储系统是更困难的工程问题,所以我将重点关注它。
-传统的同步写入方法需要跨异构存储系统的分布式事务【18】,我认为这是错误的解决方案(请参阅“[导出的数据与分布式事务]()”第495页)。单个存储或流处理系统内的事务是可行的,但是当数据跨越不同技术之间的边界时,我认为具有幂等写入的异步事件日志是一种更加健壮和实用的方法。
+ 传统的同步写入方法需要跨异构存储系统的分布式事务【18】,我认为这是错误的解决方案(请参阅“[导出的数据与分布式事务]()”第495页)。单个存储或流处理系统内的事务是可行的,但是当数据跨越不同技术之间的边界时,我认为具有幂等写入的异步事件日志是一种更加健壮和实用的方法。
-例如,分布式事务在某些流处理器中使用,以精确匹配一次语义(请参阅第477页的“[重新访问原子提交]()”),这可以很好地工作。然而,当事务需要涉及由不同人群编写的系统时(例如,当数据从流处理器写入分布式键值存储或搜索索引时),缺乏标准化的事务协议会使集成更难。有幂等消费者的事件的有序日志(参见第478页的“[幂等性]()”)是一种更简单的抽象,因此在异构系统中实现更加可行【7】。
+ 例如,分布式事务在某些流处理组件内部使用,以匹配**恰好一次(exactly-once)**语义(请参阅第477页的“[重新访问原子提交]()”),这可以很好地工作。然而,当事务需要涉及由不同人群编写的系统时(例如,当数据从流处理组件写入分布式键值存储或搜索索引时),缺乏标准化的事务协议会使集成更难。有幂等消费者的事件的有序事件日志(参见第478页的“[幂等性]()”)是一种更简单的抽象,因此在异构系统中实现更加可行【7】。
-基于日志的集成的一大优势是各个组件之间的松散耦合,这体现在两个方面:
+基于日志的集成的一大优势是各个组件之间的**松散耦合(loose coupling)**,这体现在两个方面:
1. 在系统级别,异步事件流使整个系统对各个组件的中断或性能下降更加稳健。如果使用者运行缓慢或失败,那么事件日志可以缓冲消息(请参阅“[磁盘空间使用情况]()”第369页),以便生产者和任何其他使用者可以继续不受影响地运行。有问题的消费者可以在固定时赶上,因此不会错过任何数据,并且包含故障。相比之下,分布式事务的同步交互往往会将本地故障升级为大规模故障(请参见第363页的“[分布式事务的限制]()”)。
-2. 在人力方面,分拆数据系统允许不同的团队独立开发,改进和维护不同的软件组件和服务。专业化使得每个团队都可以专注于做好一件事,并与其他团队的系统进行明确的界面。事件日志提供了一个足够强大的接口,以捕获相当强的一致性属性(由于持久性和事件的顺序),但也足够普遍适用于几乎任何类型的数据。
+2. 在人力方面,分拆数据系统允许不同的团队独立开发,改进和维护不同的软件组件和服务。专业化使得每个团队都可以专注于做好一件事,并与其他团队的系统以明确的接口交互。事件日志提供了一个足够强大的接口,以捕获相当强的一致性属性(由于持久性和事件的顺序),但也足够普适于几乎任何类型的数据。
-#### 非捆绑与集成系统
+#### 分拆系统vs集成系统
-如果分拆确实成为未来的方式,它将不会取代目前形式的数据库 —— 它们仍然会像以往一样需要。数据库仍然需要维护流处理器中的状态,并且为批处理和流处理器的输出提供查询服务(请参阅第419页上的“[批处理工作流的输出]()”和第464页上的“[处理流]()”)。专门的查询引擎将继续对特定的工作负载非常重要:例如,MPP数据仓库中的查询引擎针对探索性分析查询进行了优化,并且能够很好地处理这种类型的工作负载(请参阅第417页的“[将Hadoop与分布式数据库进行比较]()” 。
+ 如果分拆确实成为未来的方式,它也不会取代目前形式的数据库 —— 它们仍然会像以往一样需要。为了维护流处理组件中的状态,数据库仍然是需要的,并且为批处理和流处理器的输出提供查询服务(请参阅第419页上的“[批处理工作流的输出]()”和第464页上的“[处理流]()”)。专用查询引擎对于特定的工作负载仍然非常重要:例如,MPP数据仓库中的查询引擎针对探索性分析查询进行了优化,并且能够很好地处理这种类型的工作负载(请参阅第417页的“[将Hadoop与分布式数据库进行比较]()” 。
-运行几个不同基础架构的复杂性可能是一个问题:每一个软件都有一个学习曲线,配置问题和操作怪癖,因此值得部署尽可能少的移动部件。与由应用程序代码【23】组成的多个工具组成的系统相比,单一集成软件产品也可以在其设计的工作负载类型上实现更好,更可预测的性能。正如我在前言中所说的那样,为了扩大规模而建设你不需要的是浪费精力,并且可能会将你锁定在一个不灵活的设计中。实际上,这是一种过早优化的形式。
+ 运行几种不同基础设施的复杂性可能是一个问题:每种软件都有一个学习曲线,配置问题和操作怪癖,因此部署尽可能少的移动部件是很有必要的。比起使用应用代码拼接多个工具而成的系统,单一集成软件产品也可以在其设计应对的工作负载类型上实现更好,更可预测的性能【23】。正如我在前言中所说的那样,为了你不需要的规模建立系统是白费精力,而且可能会将你锁定在一个不灵活的设计中。实际上,这是一种过早优化的形式。
-分拆的目标不是要针对个别数据库与特定工作负载的性能进行竞争;我们的目标是允许您结合多个不同的数据库,以便在比单个软件可能实现的更广泛的工作负载范围内实现更好的性能。这是关于广度,而不是深度 —— 与我们在第414页上的“[比较Hadoop与分布式数据库]()”中讨论的存储和处理模型的多样性一样。
+ 分拆的目标不是要针对个别数据库与特定工作负载的性能进行竞争;我们的目标是允许您结合多个不同的数据库,以便在比单个软件可能实现的更广泛的工作负载范围内实现更好的性能。这是关于广度,而不是深度 —— 与我们在第414页上的“[比较Hadoop与分布式数据库]()”中讨论的存储和处理模型的多样性一样。
-因此,如果有一项技术可以满足您的所有需求,那么您最好使用该产品,而不是试图用低级组件重新实现它。只有当没有单一软件满足您的所有需求时,才会出现拆分和合成的优势。
+ 因此,如果有一项技术可以满足您的所有需求,那么您最好使用该产品,而不是试图用低级组件重新实现它。只有当没有单一软件满足您的所有需求时,才会出现拆分和联合的优势。
-#### 少了什么东西?
+#### 少了什么?
-用于组成数据系统的工具正在变得越来越好,但我认为缺少一个主要部分:我们还没有Unix shell的非捆绑式数据库(即,用于组成存储和处理系统的高级语言简单和陈述的方式)。
+ 用于组成数据系统的工具正在变得越来越好,但我认为还缺少一个主要的东西:我们还没有与Unix shell类似的分拆数据库(即,一种声明式的,简单的,用于组装存储和处理系统的高级语言)。
-例如,如果我们可以简单地声明`mysql |elasticsearch`,类似于Unix管道【22】,这将成为`CREATE INDEX`的非捆绑等价物:它将MySQL数据库中的所有文档并将其索引到Elasticsearch集群中。然后它会不断捕获对数据库所做的所有更改,并自动将它们应用于搜索索引,而无需编写自定义应用程序代码。几乎任何类型的存储或索引系统都可以实现这种集成。
+ 例如,如果我们可以简单地声明`mysql |elasticsearch`,类似于Unix管道【22】,成为`CREATE INDEX`的分拆等价物:它将MySQL数据库中的所有文档并将其索引到Elasticsearch集群中。然后它会不断捕获对数据库所做的所有变更,并自动将它们应用于搜索索引,而无需编写自定义应用程序代码。几乎任何类型的存储或索引系统都可以实现这种集成。
-同样,能够更容易地预先计算和更新缓存将是一件好事。回想一下,物化视图本质上是一个预先计算的缓存,所以您可以通过为复杂查询声明指定物化视图来创建缓存,包括图上的递归查询(请参阅第49页上的“[图数据模型]()”)和应用逻辑。在这方面有一些有趣的早期研究,如差异数据流【24,25】,我希望这些想法能够在生产系统中找到自己的方法。
+ 同样,能够更容易地预先计算和更新缓存将是一件好事。回想一下,物化视图本质上是一个预先计算的缓存,所以您可以通过为复杂查询声明指定物化视图来创建缓存,包括图上的递归查询(参阅“[图数据模型](ch2.md#图数据模型)”)和应用逻辑。在这方面有一些有趣的早期研究,如**差分数据流(differential dataflow)**【24,25】,我希望这些想法能够在生产系统中找到自己的方法。
### 围绕数据流设计应用
-通过使用应用程序代码组成专门的存储和处理系统来分离数据库的方法也被称为“数据库内外”方法【26】,在2014年的一次会议演讲标题之后【27】。然而,称它为“新架构”太宏大。我把它看作是一个设计模式,一个讨论的起点,我们只是简单地给它起一个名字,以便我们可以更好地谈论它。
+ 使用应用代码组合专用存储与处理系统来分拆数据库的方法,也被称为“**数据库由内而外**”方法【26】,在我在2014年的一次会议演讲标题之后【27】。然而称它为“新架构”过于宏大。我将其看作是一种设计模式,一个讨论的起点,我们只是简单地给它起一个名字,以便我们能更好地讨论它。
-这些想法不是我的;他们只是我认为我们应该学习的其他人思想的融合。尤其是,数据流语言(如Oz 【28】和Juttle 【29】),功能反应式编程(FRP)语言(如Elm 【30,31】)和逻辑编程语言(如Bloom [ 32。 Jay Kreps 【7】提出了在这种背景下解绑的术语。
+ 这些想法不是我的;它们是很多人的思想的融合,这些思想非常值得我们学习。尤其是,以Oz 【28】和Juttle 【29】为代表的数据流语言,以Elm【30,31】为代表的**函数式响应式编程(functional reactive programming, FRP)**,以Bloom【32】为代表的逻辑编程语言。在这一语境中的术语**分拆(unbundling)**是由Jay Kreps 提出的【7】。
-即使电子表格也具有比大多数主流编程语言遥远的数据流编程功能【33】。在电子表格中,可以将公式放入一个单元格中(例如,另一列中的单元格总和),并且只要公式的任何输入发生更改,公式的结果都会自动重新计算。这正是我们在数据系统级所需要的:当数据库中的记录发生更改时,我们希望自动更新该记录的任何索引,并且自动刷新依赖于记录的任何缓存视图或聚合。您不必担心这种刷新如何发生的技术细节,但能够简单地相信它可以正常工作。
+ 即使是**电子表格**也在数据流编程能力上甩开大多数主流编程语言几条街【33】。在电子表格中,可以将公式放入一个单元格中(例如,另一列中的单元格求和值),并且只要公式的任何输入发生变更,公式的结果都会自动重新计算。这正是我们在数据系统层次所需要的:当数据库中的记录发生变更时,我们希望自动更新该记录的任何索引,并且自动刷新依赖于记录的任何缓存视图或聚合。您不必担心这种刷新如何发生的技术细节,但能够简单地相信它可以正常工作。
-因此,我认为绝大多数数据系统仍然可以从VisiCalc在1979年已经具备的功能中学习【34】。与电子表格的不同之处在于,今天的数据系统需要具有容错性,可扩展性以及持久存储数据。他们还需要能够整合不同人群编写的不同技术,并重用现有的库和服务:期望使用某种特定语言,框架或工具开发所有软件是不切实际的。
+ 因此,我认为绝大多数数据系统仍然可以从VisiCalc在1979年已经具备的功能中学习【34】。与电子表格的不同之处在于,今天的数据系统需要具有容错性,可扩展性以及持久存储数据。他们还需要能够整合不同人群编写的不同技术,并重用现有的库和服务:期望使用某种特定语言,框架或工具开发所有软件是不切实际的。
-在本节中,我将详细介绍这些想法,并探讨一些围绕非捆绑数据库和数据流的想法构建应用程序的方法。
+ 在本节中,我将详细介绍这些想法,并探讨一些围绕分拆数据库和数据流的想法构建应用的方法。
-#### 应用程序代码作为衍生函数
+#### 应用代码作为衍生函数
-当一个数据集来自另一个数据集时,它会经历某种转换函数。例如:
+ 当一个数据集衍生自另一个数据集时,它会经历某种转换函数。例如:
-* 辅助索引是一种具有直接转换函数的衍生数据集:对于基表中的每一行或文档,它挑选被索引的列或字段中的值,并按这些值排序(假设B树或SSTable索引,按键排序,如[第3章](ch3.md)所述)。
-* 通过应用各种自然语言处理功能(如语言检测,分词,词干或词汇化,拼写纠正和同义词识别)创建全文搜索索引,然后构建用于高效查找的数据结构(例如作为倒排索引)。
-* 在机器学习系统中,我们可以将模型视为通过应用各种特征提取和统计分析功能从训练数据中导出。当模型应用于新的输入数据时,模型的输出是从输入和模型(因此间接地从训练数据)中导出的。
-* 缓存通常包含将以用户界面(UI)显示的形式的数据聚合。因此填充缓存需要知道UI中引用的字段;UI中的更改可能需要更新缓存填充方式的定义以及重建缓存。
+* 次级索引是由一种直白的转换函数生成的衍生数据集:对于基础表中的每行或每个文档,它挑选被索引的列或字段中的值,并按这些值排序(假设使用B树或SSTable索引,按键排序,如[第3章](ch3.md)所述)。
+* 全文搜索索引是通过应用各种自然语言处理函数而创建的,诸如语言检测,分词,词干或词汇化,拼写纠正和同义词识别)创建全文搜索索引,然后构建用于高效查找的数据结构(例如倒排索引)。
+* 在机器学习系统中,我们可以将模型视作从训练数据通过应用各种特征提取,统计分析函数衍生的数据,当模型应用于新的输入数据时,模型的输出是从输入和模型(因此间接地从训练数据)中衍生的。
+* 缓存通常包含将以用户界面(UI)显示的形式的数据聚合。因此填充缓存需要知道UI中引用的字段;UI中的变更可能需要更新缓存填充方式的定义,并重建缓存。
-辅助索引的衍生函数通常是必需的,因此它作为核心特性被构建到许多数据库中,您可以仅通过说`CREATE INDEX`来调用它。对于全文索引,常见语言的基本语言特征可能内置到数据库中,但更复杂的特征通常需要特定于域的调整。在机器学习中,特征工程是众所周知的特定于应用程序的特征,并且通常必须包含关于应用程序的用户交互和部署的详细知识【35】。
+用于次级索引的衍生函数是如此常用的需求,以致于它作为核心功能被内建至许多数据库中,你可以简单地通过`CREATE INDEX`来调用它。对于全文索引,常见语言的基本语言特征可能内置到数据库中,但更复杂的特征通常需要领域特定的调整。在机器学习中,特征工程是众所周知的特定于应用的特征,通常需要包含很多关于用户交互与应用部署的详细知识【35】。
+ 当创建衍生数据集的函数不是像创建二级索引那样的标准搬砖函数时,需要自定义代码来处理特定于应用程序的东西。而这个自定义代码是让许多数据库挣扎的地方,虽然关系数据库通常支持触发器,存储过程和用户定义的函数,它们可以用来在数据库中执行应用代码,但它们有点像数据库设计中的事后反思。(参阅“[传输事件流](ch11.md#传输事件流)”(第447页))。
-当创建衍生数据集的函数不是像创建二级索引那样的标准Cookie切割函数时,需要自定义代码来处理特定于应用程序的方面。而这个自定义代码是许多数据库难以抗争虽然关系数据库通常支持触发器,存储过程和用户定义的函数,它们可以用来在数据库中执行应用程序代码,但它们在数据库设计中已经有所反应了(请参阅“[传输事件流]()”(第447页))。
+#### 应用代码和状态的分离
-#### 应用程序代码和状态的分离
+ 理论上,数据库可以是任意应用代码的部署环境,就如同操作系统一样。然而实践中它们对这一目标适配的很差。它们不满足现代应用开发的要求,例如依赖性和软件包管理,版本控制,滚动升级,可演化性,监控,指标,对网络服务的调用以及与外部系统的集成。
-理论上,数据库可以是任意应用程序代码的部署环境,如操作系统。但是,实际上他们已经变得不适合这个目的。它们不适合现代应用程序开发的要求,例如依赖性和软件包管理,版本控制,滚动升级,可演化性,监控,指标,对网络服务的调用以及与外部系统的集成。
+ 另一方面,Mesos,YARN,Docker,Kubernetes等部署和集群管理工具专为运行应用代码而设计。通过专注于做好一件事情,他们能够做得比将数据库作为其众多功能之一执行用户定义的功能要好得多。我认为让系统的某些部分专门用于持久数据存储以及专门运行应用程序代码的其他部分是有意义的。这两者可以在保持独立的同时互动。
-另一方面,Mesos,YARN,Docker,Kubernetes等部署和集群管理工具专为运行应用程序代码而设计。通过专注于做好一件事情,他们能够做得比将数据库作为其众多功能之一执行用户定义的功能要好得多。
-我认为让系统的某些部分专门用于持久数据存储以及专门运行应用程序代码的其他部分是有意义的。这两者可以在保持独立的同时互动。
+ 现在大多数Web应用程序都是作为无状态服务部署的,其中任何用户请求都可以路由到任何应用程序服务器,并且服务器在发送响应后会忘记所有请求。这种部署方式很方便,因为可以随意添加或删除服务器,但状态必须到某个地方:通常是数据库。趋势是将无状态应用程序逻辑与状态管理(数据库)分开:不将应用程序逻辑放入数据库中,也不将持久状态置于应用程序中【36】。正如职能规划界人士喜欢开玩笑说的那样,“我们相信**教会(Church)**与**国家(state)**的分离”【37】 [^i]
-现在大多数Web应用程序都是作为无状态服务部署的,其中任何用户请求都可以路由到任何应用程序服务器,并且服务器在发送响应后会忘记所有请求。这种部署方式很方便,因为可以随意添加或删除服务器,但状态必须到某个地方:通常是数据库。趋势是将无状态应用程序逻辑与状态管理(数据库)分开:不将应用程序逻辑放入数据库中,也不将持久状态置于应用程序中【36】。正如职能规划界人士喜欢开玩笑说的那样,“我们相信教会与国家的分离”【37】 [^i]
+[^i]: 解释笑话很少能让人感觉更好,但我不想让任何人感到被遗漏。 在这里,Church指代的是数学家的阿隆佐·邱奇,他创立了lambda演算,这是计算的早期形式,是大多数函数式编程语言的基础。 lambda演算不具有可变状态(即没有变量可以被覆盖),所以可以说可变状态与Church的工作是分离的。
-[^i]: 解释一个笑话很少改进它,但我不想让任何人感到被遗漏。 在这里,Church是数学家Alonzo Church的参考资料,他创建了lambda演算,这是早期的计算形式,是大多数函数式编程语言的基础。 lambda演算不具有可变状态(即没有变量可以被覆盖),所以可以说可变状态与Church的工作是分开的。
+ 在这个典型的Web应用模型中,数据库充当一种可以通过网络同步访问的可变共享变量。应用程序可以读取和更新变量,而数据库负责维持它的持久性,提供一些诸如并发控制和容错的功能。
-在这个典型的Web应用程序模型中,数据库充当一种可以通过网络同步访问的可变共享变量。应用程序可以读取和更新变量,并且数据库负责保持持久性,提供一些并发控制和容错。
+ 但是,在大多数编程语言中,你无法订阅可变变量中的变更 —— 你只能定期读取它。与电子表格不同,如果变量的值发生变化,变量的读者不会收到通知。 (你可以在自己的代码中实现这样的通知 —— 这被称为**观察者模式** —— 但大多数语言没有将这种模式作为内置功能。)
-但是,在大多数编程语言中,您无法订阅可变变量中的更改 —— 您只能定期读取它。与电子表格不同,如果变量的值发生变化,变量的读者不会收到通知。 (您可以在自己的代码中实现这样的通知 —— 这被称为观察者模式 —— 但大多数语言没有将此模式作为内置功能。)
+ 数据库继承了这种可变数据的被动方法:如果你想知道数据库的内容是否发生了变化,通常你唯一的选择就是轮询(即定期重复你的查询)。 订阅变更只是刚刚开始出现的功能(参阅“[变更流的API支持](ch11.md#变更流的API支持)”)。
-数据库继承了这种可变数据的被动方法:如果你想知道数据库的内容是否发生了变化,通常你唯一的选择就是轮询(即定期重复你的查询)。 订阅更改只是刚刚开始出现的功能(请参阅第455页的“[更改流的API支持]()”)。
+#### 数据流:状态变化和应用代码间的相互作用
-#### 数据流:状态变化和应用程序代码之间的相互影响
+ 从数据流的角度思考应用,意味着重新协商应用代码和状态管理之间的关系。将数据库视为被应用程序操纵的被动变量,取而代之的是,而是更多地考虑状态,状态变更和处理它们的代码之间的相互作用与协作。应用代码通过在另一个地方触发状态变更来响应状态变更。
-从数据流的角度思考应用意味着重新谈判应用代码和状态管理之间的关系。我们不是将数据库视为被应用程序操纵的被动变量,而是更多地考虑状态,状态更改和处理它们的代码之间的相互作用和协作。应用程序代码通过在另一个地方触发状态更改来响应状态更改。
+ 我们在第451页的“[数据库和数据流]()”中看到了这一思路,我们讨论了将数据库的变更日志视为一种我们可以订阅的事件流。消息传递系统(如Actor)(请参阅第136页的“[消息传递数据流]()”)也具有响应事件的概念。早在20世纪80年代,**元组空间**模型探索表示分布式计算的过程,观察状态变化并对它们做出反应【38,39】。
-我们在第451页的“[数据库和数据流]()”中看到了这一思路,我们讨论将数据库更改的日志作为我们可以指定的事件流处理。消息传递系统(如角色)(请参阅第136页的“[消息传递数据流]()”)也具有响应事件的概念。早在20世纪80年代,元组空间模型探索表示分布式计算的过程,观察状态变化并对它们做出反应【38,39】。
+ 如前所述,当触发器由于数据变更而触发时,或者次级索引更新以反映索引表中的变更时,数据库内部发生着类似的情况。分拆数据库意味着将这个想法应用于在主数据库之外创建衍生数据集:缓存,全文搜索索引,机器学习或分析系统。我们可以为此使用流处理和消息传递系统。
-如前所述,当触发器由于数据更改而触发时,或者次级索引更新以反映索引表中的更改时,数据库内部会发生类似的情况。分解数据库意味着将此想法应用于在主数据库之外创建衍生数据集:缓存,全文搜索索引,机器学习或分析系统。我们可以为此使用流处理和消息传递系统。
+ 需要记住的重要一点是,维护衍生数据不同于执行异步任务。传统消息系统通常是为执行异步任务设计的(参阅“[与传统消息传递相比的日志](ch11.md#与传统消息传递相比的日志)”):
-需要记住的重要一点是,维护衍生数据与传统设计消息传递系统的异步作业执行不同(请参阅第448页上的“[与传统消息传递相比的日志]()”):
+* 在维护衍生数据时,状态变更的顺序通常很重要(如果多个视图是从事件日志衍生的,则需要按照相同的顺序处理事件,以便它们之间保持一致)。如第445页上的“[确认和重新传递]()”中所述,许多消息代理在重传未确认消息时没有此属性,双写也被排除在外(请参阅第454页上的“[保持系统同步]()”)。
-•在维护衍生数据时,状态更改的顺序通常很重要(如果多个视图是从事件日志衍生的,则需要按照相同的顺序处理事件,以便它们保持一致)。如第445页上的“[确认和重新传递]()”中所述,许多消息代理在重新传送未确认消息时没有此属性。双重写入也被排除(请参阅第454页上的“[保持系统同步]()”)。
-•容错是导出数据的关键:仅丢失单个消息会导致衍生数据集永远与其数据源不同步。消息传递和衍生状态更新都必须可靠。例如,许多角色系统默认在内存中维护角色状态和消息,所以如果运行角色的机器崩溃,他们就会丢失。
+* 容错是衍生数据的关键:仅丢失单个消息会导致衍生数据集永远与其数据源失去同步。消息传递和衍生状态更新都必须可靠。例如,许多Actor系统默认在内存中维护角色状态和消息,所以如果运行Actor的机器崩溃,状态和消息就会丢失。
-稳定的消息排序和容错消息处理是相当严格的要求,但与分布式事务相比,它们更便宜,操作更稳定。现代流处理器可以提供这些排序和可靠性保证,并允许应用程序代码作为流操作符运行。此应用程序代码可以执行数据库中内置的衍生函数通常不提供的任意处理。就像管道链接的Unix工具一样,流操作符可以组成数据流周围的大型系统。每个运算符将状态变化流作为输入,并产生其他状态变化流作为输出。
+
+稳定的消息排序和容错消息处理是相当严格的要求,但与分布式事务相比,它们更廉价,操作更稳定。现代流处理组件可以提供这些排序和可靠性保证,并允许应用代码作为流操作符运行。此应用程序代码可以执行数据库中内置的衍生函数通常不提供的任意处理。就像管道链接的Unix工具一样,流操作符可以组成数据流周围的大型系统。每个运算符将状态变化流作为输入,并产生其他状态变化流作为输出。
#### 流处理器和服务
-当前流行的应用程序开发风格涉及将功能分解为一组通过同步网络请求(如REST API)进行通信的服务(请参阅第121页的“通过服务实现数据流:REST和RPC”)。这种面向服务的体系结构优于单个单一应用程序的优势主要在于通过松散耦合的组织可伸缩性:不同的团队可以在不同的服务上工作,从而减少团队之间的协调工作(只要服务可以独立部署和更新)。
+ 当前流行的应用开发风格涉及将功能分解为一组通过同步网络请求(如REST API)进行通信的**服务(service)**(请参阅第121页的“[通过服务实现数据流:REST和RPC]()”)。这种面向服务的架构优于单一庞大应用的优势主要在于:通过松散耦合来提供组织上的可扩展性:不同的团队可以专职于不同的服务上,从而减少团队之间的协调工作(只要服务可以独立部署和更新)。
-将流操作符合并到数据流系统中与微服务方法有很多相似的特征【40】。但是,底层的通信机制是非常不同的:单向异步消息流而不是同步请求/响应交互。
+ 将流操作符合并到数据流系统中,与微服务方法有很多相似的特征【40】。但是,底层的通信机制是非常不同的:单向异步消息流,而不是同步请求/响应式交互。
-除了第136页上的“[消息传递数据流]()”中列出的优点(如更好的容错性),数据流系统还可以获得更好的性能。例如,假设客户正在购买以一种货币定价但以另一种货币支付的商品。为了执行货币转换,您需要知道当前的汇率。这个操作可以通过两种方式实现【40,41】:
+ 除了第136页上的“[消息传递数据流]()”中列出的优点(如更好的容错性),数据流系统还可以获得更好的性能。例如,假设客户正在购买以一种货币定价但以另一种货币支付的商品。为了执行货币换算,您需要知道当前的汇率。这个操作可以通过两种方式实现【40,41】:
-1. 在微服务方法中,处理购买的代码可能会查询汇率服务或数据库以获取特定货币的当前汇率。
-2. 在数据流方法中,处理采购的代码将提前订阅汇率更新流,并在当地数据库发生更改时将当前汇率记录下来。处理采购时,只需查询本地数据库即可。
+1. 在微服务方法中,处理购买的代码可能会查询汇率服务或数据库,以获取特定货币的当前汇率。
+2. 在数据流方法中,处理排序的代码将提前订阅汇率更新流,并在汇率发生变动时将当前汇率存储在本地数据库中。处理排序时,只需查询本地数据库即可。
-第二种方法已经将同步网络请求替换为对本地数据库进行查询的另一服务(即使在同一个进程中,该请求也可能在同一台机器上)[^ii]。数据流不仅方法更快,而且更稳健到另一项服务的失败。最快和最可靠的网络请求根本就没有网络请求!我们现在不使用RPC,而是在购买事件和汇率更新事件之间建立流联接(请参阅第473页的“[流表联接(流增强)]()”)。
-[^ii]: 在微服务方法中,您可以通过在处理购买的服务中本地缓存汇率来避免同步网络请求。 但是,为了使缓存保持新鲜,您需要定期轮询更新的汇率,或订阅更改流——这正是数据流方法中发生的情况。
-加入是时间相关的:如果购买事件在稍后的时间点被重新处理,汇率将会改变。如果要重建原始输出,则需要获取原始购买时的历史汇率。无论您是查询服务还是订阅汇率更新流,您都需要处理这种时间依赖性(请参阅第475页的“[连接的时间依赖性]()”)。
+ 第二种方法能将对另一服务的同步网络请求替换为对本地数据库的查询(可能在同一台机器甚至同一个进程中)[^ii]。数据流方法不仅更快,而且当其他服务失效时也更稳健。最快且最可靠的网络请求就是压根没有网络请求!我们现在不使用RPC,而是在购买事件和汇率更新事件之间建立流联接(请参阅第473页的“[流表联接(流增强)]()”)。
-订阅一系列更改,而不是在需要时查询当前状态,使我们更接近类似电子表格的计算模型:当某些数据发生更改时,依赖于此的所有衍生数据都可以快速更新。还有很多未解决的问题,例如围绕时间依赖连接等问题,但我认为围绕数据流想法构建应用程序是一个非常有希望的方向。
+[^ii]: 在微服务方法中,您可以通过在处理购买的服务中本地缓存汇率来避免同步网络请求。 但是,为了保鲜缓存,你需要定期轮询汇率以获取其更新,或订阅变更流 —— 这正是数据流方法中发生的情况。
+
+ 连接是时间相关的:如果购买事件在稍后的时间点被重新处理,汇率将会改变。如果要重建原始输出,则需要获取原始购买时的历史汇率。无论是查询服务还是订阅汇率更新流,你都需要处理这种时间依赖性(请参阅第475页的“[连接的时间依赖性]()”)。
+
+ 订阅变更流,而不是在需要时查询当前状态,使我们更接近类似电子表格的计算模型:当某些数据发生变更时,依赖于此的所有衍生数据都可以快速更新。还有很多未解决的问题,例如围绕时间依赖连接等问题,但我认为围绕数据流想法构建应用程序是一个非常有希望的方向。
### 观察衍生数据状态
-在抽象层面,上一节讨论的数据流系统为您提供了创建衍生数据集(例如搜索索引,物化视图和预测模型)并使其保持最新的过程。让我们称这个过程为写入路径:只要某些信息被写入系统,它可能会经历批处理和流处理的多个阶段,并且最终每个衍生数据集都会更新以合并写入的数据。[图12-1](img/fig12-1.png)显示了更新搜索索引的示例。
+ 在抽象层面,上一节讨论的数据流系统为您提供了创建衍生数据集(例如搜索索引,物化视图和预测模型)并使其保持最新的过程。让我们称这个过程为**写路径(write path)**:只要某些信息被写入系统,它可能会经历批处理和流处理的多个阶段,并且最终每个衍生数据集都会更新,以适配写入的数据。[图12-1](img/fig12-1.png)显示了更新搜索索引的示例。

-**图12-1 在搜索索引中,写入(文档更新)符合读取(查询)**
+**图12-1 在搜索索引中,写(文档更新)遇上读(查询)**
-但为什么你首先创建衍生数据集?很可能是因为你想在以后再次查询它。这是读取路径:在提供从衍生数据集中读取的用户请求时,可能会对结果执行一些更多处理,然后构建对用户的响应。
+ 但你为什么最开始要创建衍生数据集?很可能是因为你想在以后再次查询它。这是**读路径(read path)**:在提供从衍生数据集中读取的用户请求时,可能会对结果执行一些更多处理,然后构建对用户的响应。
-总而言之,写入路径和读取路径涵盖了数据的整个旅程,从收集数据的地步到使用数据(可能是由另一个人)。写入路径是预先计算的行程的一部分 —— 即,一旦数据进入,即刻完成,无论是否有人要求查看它。阅读路径是旅程中只有当有人要求时才会发生的部分。如果您熟悉函数式编程语言,则可能会注意到写入路径类似于急切的评估,读取路径类似于懒惰评估。
+ 总而言之,写路径和读路径涵盖了数据的整个旅程,从收集数据开始,到使用数据结束(可能是由另一个人)。写路径是预计算过程的一部分 —— 即,一旦数据进入,即刻完成,无论是否有人需要看它。读路径是这个过程中只有当有人请求时才会发生的部分。如果你熟悉函数式编程语言,则可能会注意到写路径类似于立即求值,读路径类似于惰性求值。
-如[图12-1](img/fig12-1.png)所示,衍生数据集是写入路径和读取路径相遇的地方。它代表了在写入时需要完成的工作量与在读取时需要完成的工作量之间的权衡。
+ 如[图12-1](img/fig12-1.png)所示,衍生数据集是写路径和读路径相遇的地方。它代表了在写入时需要完成的工作量与在读取时需要完成的工作量之间的权衡。
#### 物化视图和缓存
-全文搜索索引就是一个很好的例子:写路径更新索引,读路径在索引中搜索关键字。读写都需要做一些工作。写入需要更新文档中出现的所有术语的索引条目。阅读需要搜索查询中的每个单词,并应用布尔逻辑来查找包含查询中所有单词(AND运算符)的文档,或者每个单词(OR运算符)的任何同义词。
+ 全文搜索索引就是一个很好的例子:写路径更新索引,读路径在索引中搜索关键字。读写都需要做一些工作。写入需要更新文档中出现的所有术语的索引条目。读取需要搜索查询中的每个单词,并应用布尔逻辑来查找包含查询中所有单词(AND运算符)的文档,或者每个单词(OR运算符)的任何同义词。
-如果您没有索引,搜索查询将不得不扫描所有文档(如grep),如果您有大量文档,这将会非常昂贵。没有索引意味着写入路径上的工作量较少(没有要更新的索引),但是在读取路径上需要更多工作。
+ 如果您没有索引,搜索查询将不得不扫描所有文档(如grep),如果您有大量文档,这样做开销巨大。没有索引意味着写入路径上的工作量较少(没有要更新的索引),但是在读取路径上需要更多工作。
-[^iii]: 假设一个有限的语料库,那么非空洞的搜索结果的非空搜索结果是有限的。然而,它在语料库中的术语数量是指数级的,这仍然是一个坏消息。
+ 另一方面,你可以想象为所有可能的查询预先计算搜索结果。在这种情况下,读取路径上的工作量会减少:不需要布尔逻辑,只需查找查询结果并返回即可。但是,写路径会更加昂贵:可能要求的可能的搜索查询集合是无限的,因此预先计算所有可能的搜索结果将需要无限的时间和存储空间。那肯定没戏[^iii]。
-另一方面,您可以想象为所有可能的查询预先计算搜索结果。在这种情况下,读取路径上的工作量会减少:不需要布尔逻辑,只需查找查询结果并返回即可。但是,写入路径会更加昂贵:可能要求的可能的搜索查询集合是无限的,因此预先计算所有可能的搜索结果将需要无限的时间和存储空间。那不会很好[^iii]。
+[^iii]: 假设一个有限的语料库,那么返回非空搜索结果的搜索查询集合是有限的。然而,它是与语料库中的术语数量呈指数关系,这仍是一个坏消息。
-另一个选择是预先计算搜索结果,只对一组固定的最常见的查询进行计算,以便它们可以快速地服务而不必去索引。不寻常的查询仍然可以从索引提供。这通常会被称为常见查询缓存,尽管我们也可以称之为物化
+ 另一个选择是预先计算搜索结果,只对一组固定的最常见的查询进行计算,以便它们可以快速地服务而不必去索引。不寻常的查询仍然可以从索引提供。这通常被称作常见查询的**缓存**,尽管我们也可以称之为**物化视图**,因为当新文档出现时,需要更新这些文档,这些文档应该包含在其中一个常见查询的结果中。
-视图,因为当新文档出现时,需要更新这些文档,这些文档应该包含在其中一个常见查询的结果中。
+ 从这个例子中我们可以看到,索引不是写路径和读路径之间唯一可能的边界。常见搜索结果的缓存也是可能的,并且在少量文档上也可以使用没有索引的类似grep的扫描。如此看来,缓存,索引和物化视图的作用很简单:它们改变了读路径和写路径之间的边界。通过预先计算结果,它们允许我们在写路径上做更多的工作,以节省读取路径上的工作量。
-从这个例子中我们可以看到,索引不是写路径和读路径之间唯一可能的边界。常见搜索结果的缓存是可能的,并且在少量文档上也可以使用没有索引的类似grep的扫描。如此看来,缓存,索引和物化视图的作用很简单:它们改变了读取路径和写入路径之间的边界。通过预先计算结果,它们允许我们在写入路径上做更多的工作,以节省读取路径上的工作量。
-
-在写作路径上完成的工作和读取路径之间的界限实际上是本书开始处的Twitter示例的主题,在第11页的“描述负载”中。在该示例中,我们还看到了与普通用户相比,名人的写作路径和阅读路径可能会有所不同。在500页之后,我们已经到了整个圈子!
+ 在写路径上完成的工作和读路径之间的界限,实际上是本书开始处的Twitter例子的主题,在“[描述负载](ch1.md#描述负载)”中。在该示例中,我们还看到了与普通用户相比,名人的写路径和读路径可能会有所不同。在500页之后,我们已经走完一个大循环!
#### 有状态,可离线的客户端
-我发现写作和读取路径之间的边界很有趣,因为我们可以讨论移动这个边界并探讨实际意义上的这种转变意味着什么。我们来看看不同情况下的想法。
+ 我发现写和读路径之间的边界很有趣,因为我们可以讨论移动这个边界并探讨实际意义上的这种转变意味着什么。我们来看看不同情况下的想法。
-过去二十年来,Web应用程序的巨大流行使我们对应用程序开发有了一定的假设,这很容易被视为理所当然。特别是,客户机/服务器模型(客户机主要是无状态的,服务器拥有数据的权限)非常普遍,我们几乎忘记了其他任何东西都存在。但是,技术不断发展,我认为不时质疑现状非常重要。
+ 过去二十年来,Web应用程序的巨大流行使我们对应用程序开发有了一定的假设,这很容易被视为理所当然。特别是,客户机/服务器模型(客户端主要是无状态的,服务器拥有权威数的据)非常普遍,我们几乎忘记了其他任何东西都存在。但是,技术不断发展,我认为不时质疑现状非常重要。
-传统上,网络浏览器是无状态的客户端,只有在您连接到互联网时才能做有用的事情(只有您可以离线执行的唯一的事情是在您之前在线加载的页面上下滚动)。然而,最近的“单页面”JavaScript Web应用程序已经获得了很多有状态的功能,包括客户端用户界面交互和Web浏览器中的持久本地存储。移动应用程序可以类似地在设备上存储大量状态,并且不需要往返于大多数用户交互的服务器。
+ 传统上,网络浏览器是无状态的客户端,只有在您连接到互联网时才能做有用的事情(只有您可以离线执行的唯一的事情是在您之前在线加载的页面上下滚动)。然而,最近的“单页面”JavaScript Web应用程序已经获得了很多有状态的功能,包括客户端用户界面交互和Web浏览器中的持久本地存储。移动应用程序可以类似地在设备上存储大量状态,并且不需要往返于大多数用户交互的服务器。
-这些不断变化的功能引发了对离线优先应用程序的重新兴趣,这些应用程序尽可能地在同一设备上使用本地数据库,无需连接互联网,并且在网络连接时与后台远程服务器同步可用【42】。由于移动设备通常具有缓慢且不可靠的蜂窝互联网连接,因此,如果用户的用户界面不必等待同步网络请求,并且应用程序大多离线工作,则对用户来说是一大优势(请参阅“具有离线操作的客户端”第170页)。
+ 这些不断变化的功能引发了对**离线优先(offline-first)**应用程序的重新兴趣,这些应用程序尽可能地在同一设备上使用本地数据库,无需连接互联网,并且在网络连接时与后台远程服务器同步可用【42】。由于移动设备通常具有缓慢且不可靠的蜂窝互联网连接,因此,如果用户的用户界面不必等待同步网络请求,并且应用程序大多离线工作,则对用户来说是一大优势(请参阅“[具有离线操作的客户端](ch5.md#具有离线操作的客户端)”第170页)。
-当我们摆脱无国籍客户与中央数据库交谈的假设,并转向终端用户设备上维护的状态时,开启了一个全新的机会。特别是,我们可以将设备上的状态视为服务器上的状态缓存。屏幕上的像素是客户端应用程序中模型对象的物化视图;模型对象是远程数据中心的本地状态副本【27】。
+ 当我们摆脱无状态客户端与中央数据库交互的假设,并转向终端用户设备上维护的状态时,这就开启了新世界的大门。特别是,我们可以将设备上的状态视为**服务器状态的缓存**。屏幕上的像素是客户端应用程序中模型对象的物化视图;模型对象是远程数据中心的本地状态副本【27】。
-#### 将状态更改推送给客户端
+#### 将状态变更推送给客户端
-在典型的网页中,如果您在Web浏览器中加载页面,并且随后服务器上的数据发生更改,则浏览器在重新加载页面之前不会查找有关更改。浏览器只能在一个时间点读取数据,假设它是静态的 —— 它不会订阅来自服务器的更新。因此,设备上的状态是一个陈旧的缓存,除非您明确轮询更改,否则不会更新。 (像RSS这样的基于HTTP的订阅源订阅协议实际上只是一种基本的调查形式。)
+ 在典型的网页中,如果您在Web浏览器中加载页面,并且随后服务器上的数据发生变更,则浏览器在重新加载页面之前不会查找有关变更。浏览器只能在一个时间点读取数据,假设它是静态的 —— 它不会订阅来自服务器的更新。因此,设备上的状态是一个陈旧的缓存,除非你显式轮询变更,否则不会更新。 (像RSS这样的基于HTTP的订阅源订阅协议实际上只是一种基本的轮询形式。)
-更新的协议已经超越了HTTP的基本请求/响应模式:服务器发送的事件(EventSource API)和WebSockets提供了通信渠道,通过这些通信渠道,Web浏览器可以与服务器保持开放的TCP连接,服务器可以只要保持连接状态,就会主动将消息推送到浏览器。这为服务器提供了一个机会,主动通知最终用户客户端本地存储状态的任何变化,从而减少客户端状态的陈旧程度。
+ 最近的协议已经超越了HTTP的基本请求/响应模式:服务器发送的事件(EventSource API)和WebSockets提供了通信渠道,通过这些通信渠道,Web浏览器可以与服务器保持打开的TCP连接,服务器可以只要保持连接状态,就会主动将消息推送到浏览器。这为服务器提供了一个机会,主动通知最终用户客户端本地存储状态的任何变化,从而减少客户端状态的陈旧程度。
-就我们的写入路径和读取路径模型而言,主动将状态改变到客户端设备意味着将写入路径一直延伸到最终用户。当客户端首次初始化时,它仍然需要使用读取路径来获取其初始状态,但此后可能依赖于服务器发送的状态更改流。我们在流处理和消息传递方面讨论的想法并不局限于仅在数据中心运行:我们可以进一步采用这些想法,并将它们一直延伸到终端用户设备【43】。
+ 就我们的写路径和读路径模型而言,主动将状态变更推至到客户端设备,意味着将写路径一直延伸到终端用户。当客户端首次初始化时,它仍然需要使用读路径来获取其初始状态,但此后可能依赖于服务器发送的状态变更流。我们在流处理和消息传递方面讨论的想法并不局限于仅在数据中心运行:我们可以进一步采纳这些想法,并将它们一直延伸到终端用户设备【43】。
-这些设备有时会脱机,并且在此期间无法收到服务器状态更改的任何通知。但是我们已经解决了这个问题:在第449页的“消费者偏移量”中,我们讨论了基于日志的消息中介的使用者在失败或断开连接后可以重新连接,并确保它不会错过任何到达的消息它被断开。同样的技术适用于单个用户,每个设备都是小事件的小用户。
+ 这些设备有时会脱机,并且在此期间无法收到服务器状态变更的任何通知。但是我们已经解决了这个问题:在第449页的“[消费者偏移量](ch11.md#消费者偏移量)”中,我们讨论了基于日志的消息代理用户在失败或断开连接后可以重新连接,并确保它不会错过掉线期间任何到达的消息。同样的技术适用于单个用户,每个设备都是小事件流的小订阅者。
#### 端到端的事件流
-最近用于开发有状态客户端和用户界面的工具(如Elm语言【30】和Facebook的React,Flux和Redux工具链)已经通过订阅表示用户的事件流来管理内部客户端状态输入或来自服务器的响应,其结构与事件源相似(请参阅第457页的“[事件源]()”)。
+ 最近用于开发有状态客户端和用户界面的工具,例如如Elm语言【30】和Facebook的React,Flux和Redux工具链,已经通过订阅表示用户输入和服务器响应的事件流来管理客户端的内部状态,其结构与事件源相似(请参阅第457页的“[事件源]()”)。
-将这种编程模型扩展为允许服务器将状态改变事件推送到客户端事件管道中是非常自然的。因此,状态变化可以通过端到端的写入路径流动:从触发状态改变的一个设备上的交互,通过事件日志以及通过多个衍生的数据系统和流处理器,一直到用户界面在另一台设备上观察状态的人。这些状态变化可以以相当低的延迟传播——比如说,在一秒内结束。
+ 将这种编程模型扩展为允许服务器将状态改变事件推送到客户端事件管道中是非常自然的。因此,状态变化可以通过端到端的写路径流动:从触发状态改变的一个设备上的交互,通过事件日志以及通过多个衍生的数据系统和流处理器,一直到用户界面在另一台设备上观察状态的人。这些状态变化可以以相当低的延迟传播——比如说,在一秒内结束。
-一些应用程序(如即时消息传递和在线游戏)已经具有这种“实时”体系结构(从低延迟的交互意义上说,不是“响应时间保证”在本页中的含义)。但为什么我们不用这种方式构建所有的应用程序?
+ 一些应用程序(如即时消息传递和在线游戏)已经具有这种“实时”体系结构(从低延迟的交互意义上说,不是“响应时间保证”在本页中的含义)。但为什么我们不用这种方式构建所有的应用程序?
-挑战在于无状态客户端和请求/响应交互的假设在我们的数据库,库,框架和协议中非常深入。许多数据存储支持读取和写入操作,请求返回一个响应,但是少得多提供订阅更改的能力 —— 即随着时间的推移返回响应流的请求(请参阅“更改流的API支持” 。
+ 挑战在于无状态客户端和请求/响应交互的假设在我们的数据库,库,框架和协议中非常深入。许多数据存储支持读取和写入操作,请求返回一个响应,但是少得多提供订阅变更的能力 —— 即随着时间的推移返回响应流的请求(请参阅“变更流的API支持” 。
-为了将写入路径扩展到最终用户,我们需要从根本上重新思考我们构建这些系统的方式:从请求/响应交互转向发布/订阅数据流【27】。我认为更具响应性的用户界面和更好的离线支持的优势将使其值得付出努力。如果您正在设计数据系统,我希望您会记住订阅更改的选项,而不只是查询当前状态。
+ 为了将写入路径扩展到最终用户,我们需要从根本上重新思考我们构建这些系统的方式:从请求/响应交互转向发布/订阅数据流【27】。我认为更具响应性的用户界面和更好的离线支持的优势将使其值得付出努力。如果您正在设计数据系统,我希望您会记住订阅变更的选项,而不只是查询当前状态。
#### 读也是事件
-我们讨论过,当流处理器将衍生数据写入存储(数据库,缓存或索引)时,以及当用户请求查询该存储时,存储将充当写入路径和读取路径之间的边界。该商店允许对数据进行随机访问读取查询,否则这些查询将需要扫描整个事件日志。
+ 我们讨论过,当流处理器将衍生数据写入存储(数据库,缓存或索引)时,以及当用户请求查询该存储时,存储将充当写路径和读路径之间的边界。该商店允许对数据进行随机访问读取查询,否则这些查询将需要扫描整个事件日志。
-在很多情况下,数据存储与流式传输系统是分开的。但请记住,流处理器还需要维护状态以执行聚合和连接(请参阅第472页的“[流连接]()”)。这种状态通常隐藏在流处理器内部,但是一些框架允许它也被外部客户端查询【45】,将流处理器本身变成一种简单的数据库。
+ 在很多情况下,数据存储与流式传输系统是分开的。但请记住,流处理器还需要维护状态以执行聚合和连接(请参阅第472页的“[流连接]()”)。这种状态通常隐藏在流处理器内部,但是一些框架允许它也被外部客户端查询【45】,将流处理器本身变成一种简单的数据库。
-我想进一步考虑这个想法。正如到目前为止所讨论的那样,对商店的写入是通过事件日志进行的,而读取是瞬时网络请求,直接进入存储被查询数据的节点。这是一个合理的设计,但不是唯一可能的设计。也可以将读取请求表示为事件流,并通过流处理器发送读取事件和写入事件;处理器通过将读取结果发送到输出流来响应读取事件【46】。
+ 我想进一步考虑这个想法。正如到目前为止所讨论的那样,对商店的写入是通过事件日志进行的,而读取是瞬时网络请求,直接进入存储被查询数据的节点。这是一个合理的设计,但不是唯一可能的设计。也可以将读取请求表示为事件流,并通过流处理器发送读取事件和写入事件;处理器通过将读取结果发送到输出流来响应读取事件【46】。
-当写入和读取都被表示为事件,并且被路由到同一个流操作符以便处理时,我们实际上是在读查询流和数据库之间执行流表连接。读取事件需要发送到保存数据的数据库分区(请参阅第214页的“[请求路由]()”),就像批处理和流处理器在连接时需要在同一个键上共同输入一样(请参阅“[Reduce端连接和分组]()“)。
+ 当写入和读取都被表示为事件,并且被路由到同一个流操作符以便处理时,我们实际上是在读查询流和数据库之间执行流表连接。读取事件需要发送到保存数据的数据库分区(请参阅第214页的“[请求路由]()”),就像批处理和流处理器在连接时需要在同一个键上共同输入一样(请参阅“[Reduce端连接和分组]()“)。
-服务请求和正在执行的连接之间的这种对应关系是非常重要的【47】。一次性读取请求只是通过连接运算符传递请求,然后立即忘记它;订阅请求是与连接另一端的过去和未来事件的持续连接。
+ 服务请求和正在执行的连接之间的这种对应关系是非常重要的【47】。一次性读取请求只是通过连接运算符传递请求,然后立即忘记它;订阅请求是与连接另一端的过去和未来事件的持续连接。
-记录读取事件的日志可能对于追踪整个系统中的因果关系和数据来源也有好处:它可以让您在做出特定决策之前重建用户看到的内容。例如,在网上商店,向客户显示的预测出货日期和库存状态可能影响他们是否选择购买物品【4】。要分析此连接,您需要记录用户查询运输和库存状态的结果。
+ 记录读取事件的日志可能对于追踪整个系统中的因果关系和数据来源也有好处:它可以让您在做出特定决策之前重建用户看到的内容。例如,在网上商店,向客户显示的预测出货日期和库存状态可能影响他们是否选择购买物品【4】。要分析此连接,您需要记录用户查询运输和库存状态的结果。
-将读取事件写入持久存储器可以更好地跟踪因果关系(请参阅第493页的“[顺序事件以捕获因果关系]()”),但会产生额外的存储和I/O成本。优化这些系统以减少开销仍然是一个开放的研究问题【2】。但是,如果您已经为了操作目的而记录了读取请求,作为请求处理的副作用,将日志作为请求的来源并不是什么大的改变。
+ 将读取事件写入持久存储器可以更好地跟踪因果关系(请参阅第493页的“[顺序事件以捕获因果关系]()”),但会产生额外的存储和I/O成本。优化这些系统以减少开销仍然是一个开放的研究问题【2】。但是,如果您已经为了操作目的而记录了读取请求,作为请求处理的副作用,将日志作为请求的来源并不是什么大的改变。
#### 多分区数据处理
-对于只涉及单个分区的查询,通过流发送查询和收集响应流的努力可能是过度的。然而,这个想法打开了分布式执行复杂查询的可能性,这需要合并来自多个分区的数据,利用流处理器已经提供的消息路由,分区和加入的基础设施。
+ 对于只涉及单个分区的查询,通过流发送查询和收集响应流的努力可能是过度的。然而,这个想法打开了分布式执行复杂查询的可能性,这需要合并来自多个分区的数据,利用流处理器已经提供的消息路由,分区和加入的基础设施。
-Storm的分布式RPC功能支持这种使用模式(请参阅第468页的“[消息传递和RPC]()”)。例如,它已被用于计算在Twitter上看到过网址的人数 —— 即,每个人都推送了该网址的跟随者集合【48】。由于Twitter用户组是分区的,因此这种计算需要合并来自多个分区的结果
+ Storm的分布式RPC功能支持这种使用模式(请参阅第468页的“[消息传递和RPC]()”)。例如,它已被用于计算在Twitter上看到过网址的人数 —— 即,每个人都推送了该网址的跟随者集合【48】。由于Twitter用户组是分区的,因此这种计算需要合并来自多个分区的结果
-这种模式的另一个例子是欺诈预防:为了评估特定购买事件是否具有欺诈风险,您可以检查用户的IP地址,电子邮件地址,帐单地址,送货地址等的信誉分数。这些信誉数据库中的每一个都是自身分区的,因此为特定购买事件收集分数需要一系列具有不同分区数据集的联合【49】。
+ 这种模式的另一个例子是欺诈预防:为了评估特定购买事件是否具有欺诈风险,您可以检查用户的IP地址,电子邮件地址,帐单地址,送货地址等的信誉分数。这些信誉数据库中的每一个都是自身分区的,因此为特定购买事件收集分数需要一系列具有不同分区数据集的联合【49】。
-MPP数据库的内部查询执行图具有相似的特征(请参阅第417页的“[比较Hadoop与分布式数据库]()”)。如果您需要执行这种多分区连接,使用提供此功能的数据库可能比使用流处理器实现它更简单。但是,将查询视为流提供了一个选项,可以实现大规模应用程序,这些应用程序可以在传统的现成解决方案的限制下运行。
+ MPP数据库的内部查询执行图具有相似的特征(请参阅第417页的“[比较Hadoop与分布式数据库]()”)。如果您需要执行这种多分区连接,使用提供此功能的数据库可能比使用流处理器实现它更简单。但是,将查询视为流提供了一个选项,可以实现大规模应用程序,这些应用程序可以在传统的现成解决方案的限制下运行。
-## 目标是正确性
+## 将事情做正确
-对于只读取数据的无状态服务,出现问题时不会造成什么大问题:您可以修复该错误并重新启动服务,并且一切都恢复正常。像数据库这样的有状态的系统并不是那么简单:它们被设计成永远记住事物(或多或少),所以如果出现问题,效果也可能永远持续下去,这意味着它们需要更仔细的思考【50】。
+ 对于只读取数据的无状态服务,出现问题时不会造成什么大问题:您可以修复该错误并重新启动服务,并且一切都恢复正常。像数据库这样的有状态的系统并不是那么简单:它们被设计成永远记住事物(或多或少),所以如果出现问题,效果也可能永远持续下去,这意味着它们需要更仔细的思考【50】。
-我们希望构建可靠和正确的应用程序(即,即使面对各种故障,其语义也能很好地定义和理解的程序)。大约四十年来,原子性,隔离性和耐久性(第7章)的交易特性一直是构建正确应用的首选工具。但是,这些基础比看起来更弱:例如见证弱隔离级别的混合(请参见“[弱隔离级别]()”(第233页))。
+ 我们希望构建可靠和正确的应用程序(即,即使面对各种故障,其语义也能很好地定义和理解的程序)。大约四十年来,原子性,隔离性和耐久性(第7章)的交易特性一直是构建正确应用的首选工具。但是,这些基础比看起来更弱:例如见证弱隔离级别的混合(请参见“[弱隔离级别]()”(第233页))。
-在某些领域,事务被完全抛弃,并被提供更好性能和可伸缩性的模型取代,但是更复杂的语义(例如,请参阅第167页上的“[无主复制]()”)。一致性经常被讨论,但定义不明确(参见第224页的“[一致性]()”和[第9章](ch9.md))。有些人主张我们应该“为了更好的可用性而拥抱弱一致性”,而对实际上的实际意义缺乏清晰的认识。
+ 在某些领域,事务被完全抛弃,并被提供更好性能和可伸缩性的模型取代,但是更复杂的语义(例如,请参阅第167页上的“[无主复制]()”)。一致性经常被讨论,但定义不明确(参见第224页的“[一致性]()”和[第9章](ch9.md))。有些人主张我们应该“为了更好的可用性而拥抱弱一致性”,而对实际上的实际意义缺乏清晰的认识。
-对于如此重要的话题,我们的理解和我们的工程方法是惊人的片状。例如,确定在特定事务隔离级别或复制配置下运行特定应用程序是否安全是非常困难的【51,52】。通常简单的解决方案似乎在并发性低的情况下正常工作,并且没有错误,但是在要求更高的情况下会出现许多细微的错误。
+ 对于如此重要的话题,我们的理解和我们的工程方法是惊人的片状。例如,确定在特定事务隔离级别或复制配置下运行特定应用程序是否安全是非常困难的【51,52】。通常简单的解决方案似乎在并发性低的情况下正常工作,并且没有错误,但是在要求更高的情况下会出现许多细微的错误。
-例如,凯尔金斯伯里(Kyle Kingsbury)的杰普森(Jepsen)实验【53】强调了一些产品声称的安全保证与存在网络问题和崩溃时的实际行为之间的明显差异。即使像数据库这样的基础设施产品没有问题,应用程序代码仍然需要正确使用它们提供的功能,如果配置很难理解,这是很容易出错的(这是弱隔离级别,法定配置, 等等)。
+ 例如,凯尔金斯伯里(Kyle Kingsbury)的杰普森(Jepsen)实验【53】强调了一些产品声称的安全保证与存在网络问题和崩溃时的实际行为之间的明显差异。即使像数据库这样的基础设施产品没有问题,应用程序代码仍然需要正确使用它们提供的功能,如果配置很难理解,这是很容易出错的(这是弱隔离级别,法定配置, 等等)。
-如果您的应用程序可以容忍偶尔以不可预测的方式破坏或丢失数据,那么生活就会简单得多,您可能只需横过手指就能逃脱,希望获得最好的效果。另一方面,如果您需要更强的正确性保证,那么可序列化和原子提交就是建立的方法,但是它们是有代价的:它们通常只在单个数据中心(排除地理分布式体系结构)中工作,您可以实现的规模和容错性能。
+ 如果您的应用程序可以容忍偶尔以不可预测的方式破坏或丢失数据,那么生活就会简单得多,您可能只需要阿弥陀佛就能逃脱,希望获得最好的效果。另一方面,如果您需要更强的正确性保证,那么可序列化和原子提交就是建立的方法,但是它们是有代价的:它们通常只在单个数据中心(排除地理分布式体系结构)中工作,您可以实现的规模和容错性能。
-虽然传统的交易方式并没有消失,但我也相信,在使应用程序正确和灵活地处理错误方面,并不是最后一句话。在本节中,我将提出一些关于数据流架构中正确性的思考方法。
+ 虽然传统的交易方式并没有消失,但我也相信,在使应用程序正确和灵活地处理错误方面,并不是最后一句话。在本节中,我将提出一些关于数据流架构中正确性的思考方法。
### 数据库端到端的争论
-仅仅因为应用程序使用提供比较强的安全属性的数据系统(例如可序列化的事务),并不意味着应用程序可以保证没有数据丢失或损坏。例如,如果一个应用程序有一个错误导致它写入不正确的数据,或者从数据库中删除数据,那么可序列化的事务不会为你节省。
+ 仅仅因为应用程序使用提供比较强的安全属性的数据系统(例如可序列化的事务),并不意味着应用程序可以保证没有数据丢失或损坏。例如,如果一个应用程序有一个错误导致它写入不正确的数据,或者从数据库中删除数据,那么可序列化的事务不会为你节省。
-这个例子可能看起来很无聊,但值得认真对待:应用程序错误发生,人们犯错误。我在第459页的“[状态,流和不可变性]()”中使用了这个例子来支持不可变和只能追加的数据,因为如果删除错误代码的能力来破坏好的数据,更容易从这些错误中恢复数据。
+ 这个例子可能看起来很无聊,但值得认真对待:应用程序错误发生,人们犯错误。我在第459页的“[状态,流和不可变性]()”中使用了这个例子来支持不可变和只能追加的数据,因为如果删除错误代码的能力来破坏好的数据,更容易从这些错误中恢复数据。
-虽然不变性是有用的,但它本身并非万能的。让我们看看可能发生的数据损坏的一个更为简单的例子。
+ 虽然不变性是有用的,但它本身并非万能的。让我们看看可能发生的数据损坏的一个更为简单的例子。
#### 正好执行一次操作
-在第476页的“[容错]()”中,我们遇到了一种精确调用一次(或有效一次)语义的想法。如果在处理消息时出现问题,您可以放弃(丢弃消息 —— 即导致数据丢失)或再次尝试。如果再试一次,第一次就有成功的风险,但是你没有发现成功,所以这个消息最终被处理了两次。
+ 在第476页的“[容错]()”中,我们遇到了一种精确调用一次(或有效一次)语义的想法。如果在处理消息时出现问题,您可以放弃(丢弃消息 —— 即导致数据丢失)或再次尝试。如果再试一次,第一次就有成功的风险,但是你没有发现成功,所以这个消息最终被处理了两次。
-处理两次是数据损坏的一种形式:对于相同的服务向客户收费两次(计费太多)或增加计数器两次(夸大一些度量)是不可取的。在这种情况下,正好一次就意味着安排计算,使得最终效果与没有发生错误的情况相同,即使操作实际上由于某种错误而被重试。我们以前讨论过实现这一目标的几种方法。
+ 处理两次是数据损坏的一种形式:对于相同的服务向客户收费两次(计费太多)或增加计数器两次(夸大一些度量)是不可取的。在这种情况下,正好一次就意味着安排计算,使得最终效果与没有发生错误的情况相同,即使操作实际上由于某种错误而被重试。我们以前讨论过实现这一目标的几种方法。
-最有效的方法之一是使幂等操作(参见第478页的“[幂等性]()”);即确保它具有相同的效果,无论是执行一次还是多次。但是,采取一种不自然是幂等的操作并使其具有幂等性需要付出一定的努力和关注:您可能需要维护一些额外的元数据(例如更新了值的操作ID集合),并在从一个节点到另一个节点(请参阅第295页上的“[领导和锁定]()”)。
+ 最有效的方法之一是使幂等操作(参见第478页的“[幂等性]()”);即确保它具有相同的效果,无论是执行一次还是多次。但是,采取一种不自然是幂等的操作并使其具有幂等性需要付出一定的努力和关注:您可能需要维护一些额外的元数据(例如更新了值的操作ID集合),并在从一个节点到另一个节点(请参阅第295页上的“[领导和锁定]()”)。
#### 重复抑制
-除了流处理之外,还需要抑制重复的相同模式出现在许多其他位置。例如,TCP使用数据包上的序列号将它们按正确的顺序排列在收件人处,并确定网络上是否有数据包丢失或重复。任何丢失的数据包都会被重新传输,并且在将数据交给应用程序之前,TCP堆栈会删除任何重复数据包。
+ 除了流处理之外,还需要抑制重复的相同模式出现在许多其他位置。例如,TCP使用数据包上的序列号将它们按正确的顺序排列在收件人处,并确定网络上是否有数据包丢失或重复。任何丢失的数据包都会被重新传输,并且在将数据交给应用程序之前,TCP堆栈会删除任何重复数据包。
-但是,此重复抑制仅适用于单个TCP连接的上下文中。假设TCP连接是一个客户端与数据库的连接,并且它正在执行[例12-1]()中的事务。在许多数据库中,事务与客户端连接有关(如果客户端发送了多个查询,数据库就知道它们属于同一个事务,因为它们是在同一个TCP连接上发送的)。如果客户端在发送COMMIT之后但在从数据库服务器回听之前遇到网络中断和连接超时,则不知道事务是否已被提交或中止([图8-1](img/fig8-1.png))。
+ 但是,此重复抑制仅适用于单个TCP连接的上下文中。假设TCP连接是一个客户端与数据库的连接,并且它正在执行[例12-1]()中的事务。在许多数据库中,事务与客户端连接有关(如果客户端发送了多个查询,数据库就知道它们属于同一个事务,因为它们是在同一个TCP连接上发送的)。如果客户端在发送`COMMIT`之后但在从数据库服务器回听之前遇到网络中断和连接超时,则不知道事务是否已被提交或中止([图8-1](img/fig8-1.png))。
**例12-1 将资金从一个账户转移到另一个账户的非赦免**
@@ -465,16 +465,17 @@ BEGIN TRANSACTION;
COMMIT;
```
-客户端可以重新连接到数据库并重试事务,但现在它在TCP重复抑制的范围之外。由于[例12-1]()中的交易不是幂等的,可能会发生\$22而不是所需的\$11。因此,尽管[例12-1]()是一个交易原子性的标准例子,但它实际上并不正确,而真正的银行并不像这样工作【3】。
+ 客户端可以重新连接到数据库并重试事务,但现在它在TCP重复抑制的范围之外。由于[例12-1]()中的交易不是幂等的,可能会发生\$22而不是所需的\$11。因此,尽管[例12-1]()是一个交易原子性的标准例子,但它实际上并不正确,而真正的银行并不像这样工作【3】。
-两阶段提交(请参阅第354页上的“[原子提交和两阶段提交(2PC)]()”)协议会破坏TCP连接和事务之间的1:1映射,因为它们必须允许事务协调器在数据库之后重新连接到数据库一个网络故障,并告诉它是否提交或中止有疑问的交易。这足以确保交易只能执行一次吗?不幸的是,即使我们可以抑制数据库客户端和服务器之间的重复事务,我们仍然需要担心最终用户设备和应用程序服务器之间的网络。例如,如果最终用户客户端是Web浏览器,则可能使用HTTP POST请求向服务器提交指令。也许用户处于一个微弱的蜂窝数据连接,他们成功地发送POST,但是信号在他们能够从服务器接收响应之前变得太弱。
+ 两阶段提交(请参阅第354页上的“[原子提交和两阶段提交(2PC)]()”)协议会破坏TCP连接和事务之间的1:1映射,因为它们必须允许事务协调器在数据库之后重新连接到数据库一个网络故障,并告诉它是否提交或中止有疑问的交易。这足以确保交易只能执行一次吗?不幸的是,即使我们可以抑制数据库客户端和服务器之间的重复事务,我们仍然需要担心最终用户设备和应用程序服务器之间的网络。例如,如果最终用户客户端是Web浏览器,则可能使用HTTP POST请求向服务器提交指令。也许用户处于一个微弱的蜂窝数据连接,他们成功地发送POST,但是信号在他们能够从服务器接收响应之前变得太弱。
-在这种情况下,用户可能会显示错误消息,并且可能会手动重试。 Web浏览器警告说,“你确定要再次提交这个表单吗?” —— 用户说是,因为他们希望操作发生。 (Post / Redirect / Get模式【54】可以避免在正常操作中出现此警告消息,但如果POST请求超时,它将无济于事。)从Web服务器的角度来看,重试是一个单独的请求,并且从数据库的角度来看,这是一个单独的事务。通常的重复数据删除机制无济于事。
+ 在这种情况下,用户可能会显示错误消息,并且可能会手动重试。 Web浏览器警告说,“你确定要再次提交这个表单吗?” —— 用户说是,因为他们希望操作发生。 (Post/Redirect/Get模式【54】可以避免在正常操作中出现此警告消息,但如果POST请求超时,它将无济于事。)从Web服务器的角度来看,重试是一个单独的请求,并且从数据库的角度来看,这是一个单独的事务。通常的重复数据删除机制无济于事。
#### 操作标识符
-要通过几次网络通信使操作具有幂等性,仅仅依赖数据库提供的事务机制是不够的—— 您需要考虑请求的端到端流。
-例如,您可以为操作(例如UUID)生成唯一的标识符,并将其作为隐藏的表单字段包含在客户机应用程序中,或计算所有相关表单字段的散列以衍生操作ID 【3】。如果Web浏览器提交两次POST请求,这两个请求将具有相同的操作ID。然后,您可以将该操作ID传递到数据库,并检查您是否只使用给定的ID执行一个操作,如[例12-2]()中所示。
+ 要通过几次网络通信使操作具有幂等性,仅仅依赖数据库提供的事务机制是不够的—— 您需要考虑请求的端到端流。
+ 例如,您可以为操作(例如UUID)生成唯一的标识符,并将其作为隐藏的表单字段包含在客户机应用程序中,或计算所有相关表单字段的散列以衍生操作ID 【3】。如果Web浏览器提交两次POST请求,这两个请求将具有相同的操作ID。然后,您可以将该操作ID传递到数据库,并检查您是否只使用给定的ID执行一个操作,如[例12-2]()中所示。
+
**例12-2 使用唯一的ID来抑制重复的请求**
```sql
@@ -488,117 +489,115 @@ BEGIN TRANSACTION;
COMMIT;
```
-[例12-2]()依赖于`request_id`列上的唯一性约束。如果一个事务尝试插入一个已经存在的ID,那么`INSERT`失败,事务被中止,使其无法生效两次。即使在较弱的隔离级别下,关系数据库也能正确地维护唯一性约束(而在第248页上的“[写入偏差和幻读]()”中讨论过,应用程序级别的check-then-insert可能会在不可序列化的隔离下失败)。
+ [例12-2]()依赖于`request_id`列上的唯一性约束。如果一个事务尝试插入一个已经存在的ID,那么`INSERT`失败,事务被中止,使其无法生效两次。即使在较弱的隔离级别下,关系数据库也能正确地维护唯一性约束(而在第248页上的“[写入偏差和幻读]()”中讨论过,应用程序级别的check-then-insert可能会在不可序列化的隔离下失败)。
-除了抑制重复的请求之外,示[例12-2]()中的请求表充当事件日志的一种,暗示着事件源的方向(请参阅第457页的“[事件源]()”)。账户余额的更新事实上不必与插入事件相同的事务发生,因为它们是多余的,并且可以从下游消费者中的请求事件衍生出来 —— 只要该事件只处理一次,这可以再次使用请求ID来执行。
+ 除了抑制重复的请求之外,示[例12-2]()中的请求表充当事件日志的一种,暗示着事件源的方向(请参阅第457页的“[事件源]()”)。账户余额的更新事实上不必与插入事件相同的事务发生,因为它们是多余的,并且可以从下游消费者中的请求事件衍生出来 —— 只要该事件只处理一次,这可以再次使用请求ID来执行。
**端到端的论点**
-抑制重复交易的这种情况只是一个更普遍的原则的一个例子,这个原则被称为端对端的论点,它在1984年由Saltzer,Reed和Clark阐述【55】:
+ 抑制重复交易的这种情况只是一个更普遍的原则的一个例子,这个原则被称为端对端的论点,它在1984年由Saltzer,Reed和Clark阐述【55】:
-只有在通信系统端点的应用程序的知识和帮助下,所讨论的功能才能够完全正确地实现。因此,将这种被质疑的功能作为通信系统本身的功能是不可能的。 (有时,通信系统提供的功能的不完整版本可能有助于提高性能。)
+ 只有在通信系统端点的应用程序的知识和帮助下,所讨论的功能才能够完全正确地实现。因此,将这种被质疑的功能作为通信系统本身的功能是不可能的。 (有时,通信系统提供的功能的不完整版本可能有助于提高性能。)
-在我们的例子中,有问题的函数是重复抑制。我们看到TCP在TCP连接级别抑制重复的数据包,一些流处理器在消息处理级别提供了所谓的唯一的语义,但是这不足以防止用户提交重复请求一次。 TCP,数据库事务和流处理器本身并不能完全排除这些重复。解决这个问题需要一个端到端的解决方案:从最终用户客户端一直传递到数据库的事务标识符。
+ 在我们的例子中,有问题的函数是重复抑制。我们看到TCP在TCP连接级别抑制重复的数据包,一些流处理器在消息处理级别提供了所谓的唯一的语义,但是这不足以防止用户提交重复请求一次。 TCP,数据库事务和流处理器本身并不能完全排除这些重复。解决这个问题需要一个端到端的解决方案:从最终用户客户端一直传递到数据库的事务标识符。
-端到端参数也适用于检查数据的完整性:以太网,TCP和TLS中内置的校验和可以检测网络中数据包的损坏情况,但是它们无法检测到发送和接收软件中的错误网络连接的末端,或数据存储在磁盘上的损坏。如果您想要捕获所有可能的数据损坏源,则还需要端到端的校验和。
+ 端到端参数也适用于检查数据的完整性:以太网,TCP和TLS中内置的校验和可以检测网络中数据包的损坏情况,但是它们无法检测到发送和接收软件中的错误网络连接的末端,或数据存储在磁盘上的损坏。如果您想要捕获所有可能的数据损坏源,则还需要端到端的校验和。
-类似的说法适用于加密【55】:家庭WiFi网络上的密码可以防止人们窃听您的WiFi流量,但不会对互联网上其他地方的攻击者进行窥探;您的客户端和服务器之间的TLS / SSL可以得到保护对网络攻击者,而不是对服务器的妥协。只有端到端的加密和认证可以防止所有这些事情。
+ 类似的说法适用于加密【55】:家庭WiFi网络上的密码可以防止人们窃听您的WiFi流量,但不会对互联网上其他地方的攻击者进行窥探;您的客户端和服务器之间的TLS / SSL可以得到保护对网络攻击者,而不是对服务器的妥协。只有端到端的加密和认证可以防止所有这些事情。
-尽管低级功能(TCP复制抑制,以太网校验和,WiFi加密)无法单独提供所需的端到端功能,但它们仍然很有用,因为它们可以降低较高级别出现问题的可能性。例如,如果我们没有TCP将数据包放回正确的顺序,那么HTTP请求通常会被破坏。我们只需要记住,低级别的可靠性功能本身并不足以确保端到端的正确性。
+ 尽管低级功能(TCP复制抑制,以太网校验和,WiFi加密)无法单独提供所需的端到端功能,但它们仍然很有用,因为它们可以降低较高级别出现问题的可能性。例如,如果我们没有TCP将数据包放回正确的顺序,那么HTTP请求通常会被破坏。我们只需要记住,低级别的可靠性功能本身并不足以确保端到端的正确性。
#### 在数据系统中应用端到端思考
-这使我回到我的原始论文:仅仅因为应用程序使用提供比较强的安全属性的数据系统(如可序列化事务),并不意味着应用程序保证不会丢失数据或损坏。应用程序本身也需要采取端到端的措施,例如重复压制。
+ 这使我回到我的原始论文:仅仅因为应用程序使用提供比较强的安全属性的数据系统(如可序列化事务),并不意味着应用程序保证不会丢失数据或损坏。应用程序本身也需要采取端到端的措施,例如重复压制。
-这是一个耻辱,因为容错机制很难得到正确的。低级可靠性机制(如TCP中的可靠性机制)运行良好,因此其余较高级别的故障发生得相当少。将抽象中的高级容错机制封装起来非常好,以便应用程序代码不必担心它 —— 但是我担心我们还没有找到合适的抽象。
+ 这是一个耻辱,因为容错机制很难得到正确的。低级可靠性机制(如TCP中的可靠性机制)运行良好,因此其余较高级别的故障发生得相当少。将抽象中的高级容错机制封装起来非常好,以便应用程序代码不必担心它 —— 但是我担心我们还没有找到合适的抽象。
-长期以来,交易被认为是一个很好的抽象,我相信它们是有用的。正如第7章介绍中所讨论的那样,它们会涉及各种可能的问题(并发写入,约束违规,崩溃,网络中断,磁盘故障),并将其折叠为两种可能的结果:提交或中止。这是编程模型的一个巨大的简化,但我担心这是不够的。
-
-事务处理非常昂贵,尤其是涉及异构存储技术时(请参阅第364页的“[实践中的分布式事务]()”)。当我们拒绝使用分布式事务是因为它们太昂贵时,我们最终不得不在应用程序代码中重新实现容错机制。正如本书中大量的例子所显示的那样,关于并发性和部分失败的推理是困难且违反直觉的,所以我怀疑大多数应用程序级别的机制不能正常工作。结果是丢失或损坏的数据。
-
-出于这些原因,我认为值得探索的容错抽象方法能够容易地提供特定于应用程序的端到端正确性属性,而且还可以在大型分布式环境中保持良好的性能和良好的操作特性。
+ 长期以来,事务被认为是一个很好的抽象,我相信它们是有用的。正如[第7章](ch7.md)介绍中所讨论的那样,它们会涉及各种可能的问题(并发写入,约束违规,崩溃,网络中断,磁盘故障),并将其折叠为两种可能的结果:提交或中止。这是编程模型的一个巨大的简化,但我担心这是不够的。
+ 事务处理非常昂贵,尤其是涉及异构存储技术时(请参阅第364页的“[实践中的分布式事务]()”)。当我们拒绝使用分布式事务是因为它们太昂贵时,我们最终不得不在应用程序代码中重新实现容错机制。正如本书中大量的例子所显示的那样,关于并发性和部分失败的推理是困难且违反直觉的,所以我怀疑大多数应用程序级别的机制不能正常工作。结果是丢失或损坏的数据。
+ 出于这些原因,我认为值得探索的容错抽象方法能够容易地提供特定于应用程序的端到端正确性属性,而且还可以在大型分布式环境中保持良好的性能和良好的操作特性。
### 强制实施约束
-让我们考虑非捆绑数据库的想法背景下的正确性(“[剥离数据库]()”,第499页)。我们看到,使用从客户端一直传递到记录写入的数据库的请求标识可以实现端到端的重复压缩。其他类型的限制呢?
+ 让我们考虑分拆数据库的想法背景下的正确性(“[剥离数据库]()”,第499页)。我们看到,使用从客户端一直传递到记录写入的数据库的请求标识可以实现端到端的重复压缩。其他类型的限制呢?
-特别是让我们关注唯一性约束 —— 例如我们在[例12-2]()中所依赖的约束。在第330页的“[约束和唯一性保证]()”中,我们看到了几个其他需要强制实施唯一性的应用程序功能示例:用户名或电子邮件地址必须唯一标识用户,文件存储服务不能包含多个文件同名,两个人不能在航班或剧院预订同一个座位。
+ 特别是让我们关注唯一性约束 —— 例如我们在[例12-2]()中所依赖的约束。在第330页的“[约束和唯一性保证]()”中,我们看到了几个其他需要强制实施唯一性的应用程序功能示例:用户名或电子邮件地址必须唯一标识用户,文件存储服务不能包含多个文件同名,两个人不能在航班或剧院预订同一个座位。
-其他类型的约束非常相似:例如,确保帐户余额永远不会变为负数,您不会出售比仓库中的库存更多的物料,或者会议室没有重复的预订。执行唯一性的技术通常也可以用于这些约束。
+ 其他类型的约束非常相似:例如,确保帐户余额永远不会变为负数,您不会出售比仓库中的库存更多的物料,或者会议室没有重复的预订。执行唯一性的技术通常也可以用于这些约束。
#### 唯一性约束需要达成共识
-在[第9章](ch9.md)中我们看到,在分布式环境中,强制执行唯一性约束需要达成共识:如果存在多个具有相同值的并发请求,则系统需要决定哪个冲突操作被接受,并拒绝其他违规操作的约束。
+ 在[第9章](ch9.md)中我们看到,在分布式环境中,强制执行唯一性约束需要达成共识:如果存在多个具有相同值的并发请求,则系统需要决定哪个冲突操作被接受,并拒绝其他违规操作的约束。
-达成这一共识的最常见方式是将单个节点作为领导者,并将其负责制定所有决策。只要您不介意通过单个节点发送所有请求(即使客户端位于世界的另一端),并且只要该节点没有失败,就可以正常工作。如果您需要容忍领导者失败,那么您又回到了共识问题(请参阅第367页上的“[单领导表示和共识]()”)。
+ 达成这一共识的最常见方式是将单个节点作为领导者,并将其负责制定所有决策。只要您不介意通过单个节点发送所有请求(即使客户端位于世界的另一端),并且只要该节点没有失败,就可以正常工作。如果您需要容忍领导者失败,那么您又回到了共识问题(请参阅第367页上的“[单领导表示和共识]()”)。
-唯一性检查可以根据需要唯一的值进行划分。例如,如果需要通过请求标识确保唯一性(如[例12-2]()所示),则可以确保具有相同请求标识的所有请求都路由到同一分区(请参阅[第6章](ch6.md))。如果您需要用户名是唯一的,您可以通过用户名的哈希分区。
+ 唯一性检查可以根据需要唯一的值进行划分。例如,如果需要通过请求标识确保唯一性(如[例12-2]()所示),则可以确保具有相同请求标识的所有请求都路由到同一分区(请参阅[第6章](ch6.md))。如果您需要用户名是唯一的,您可以通过用户名的哈希分区。
-但是,排除了异步多主复制,因为它可能会导致不同的主设备同时接受冲突的写操作,因此这些值不再是唯一的(请参阅第295页的“[实现可线性化系统]()”)。如果你想立即拒绝任何违反约束的写入,同步协调是不可避免的【56】。
+ 但是,排除了异步多主复制,因为它可能会导致不同的主设备同时接受冲突的写操作,因此这些值不再是唯一的(请参阅第295页的“[实现可线性化系统]()”)。如果你想立即拒绝任何违反约束的写入,同步协调是不可避免的【56】。
#### 基于日志的消息传递的唯一性
-该日志确保所有消费者以相同的顺序查看消息 —— 这种保证在形式上被称为全部命令广播并且等同于共识(参见第346页上的“[全序广播]()”)。在使用基于日志的消息传递的非捆绑数据库方法中,我们可以使用非常类似的方法来执行唯一性约束。
+ 该日志确保所有消费者以相同的顺序查看消息 —— 这种保证在形式上被称为全部命令广播并且等同于共识(参见第346页上的“[全序广播]()”)。在使用基于日志的消息传递的分拆数据库方法中,我们可以使用非常类似的方法来执行唯一性约束。
-流处理器在单个线程上依次占用日志分区中的所有消息(请参见第448页的“[与传统消息传递相比的日志]()”)。因此,如果日志根据需要唯一的值进行分区,则流处理器可以明确并确定性地确定几个冲突操作中的哪一个先到达。例如,在多个用户尝试声明相同用户名的情况下【57】:
+ 流处理器在单个线程上依次占用日志分区中的所有消息(请参见第448页的“[与传统消息传递相比的日志]()”)。因此,如果日志根据需要唯一的值进行分区,则流处理器可以明确并确定性地确定几个冲突操作中的哪一个先到达。例如,在多个用户尝试声明相同用户名的情况下【57】:
1. 对用户名的每个请求都被编码为一条消息,并附加到由用户名散列确定的分区。
2. 流处理器使用本地数据库连续读取日志中的请求,以跟踪使用哪些用户名。对于每个可用的用户名请求,它都会记录该名称并将成功消息发送到输出流。对于每个已经被使用的用户名请求,它都会向输出流发送拒绝消息。
3. 请求用户名的客户端观察输出流并等待与其请求相对应的成功或拒绝消息。
+
该算法基本上与第363页上的“[使用全序广播实现线性化存储]()”中的算法相同。它可以通过增加分区数容易地扩展为较大的请求吞吐量,因为可以独立处理每个分区。
-该方法不仅适用于唯一性约束,而且适用于许多其他类型的约束。其基本原理是任何可能冲突的写入都会路由到相同的分区并按顺序处理。正如第174页上的“什么是冲突?”和第246页上的“[写入偏差和幻读]()”中所述,冲突的定义可能取决于应用程序,但流处理器可以使用任意逻辑来验证请求。这个想法与Bayou在20世纪90年代开创的方法类似【58】。
+ 该方法不仅适用于唯一性约束,而且适用于许多其他类型的约束。其基本原理是任何可能冲突的写入都会路由到相同的分区并按顺序处理。正如第174页上的“什么是冲突?”和第246页上的“[写入偏差和幻读]()”中所述,冲突的定义可能取决于应用程序,但流处理器可以使用任意逻辑来验证请求。这个想法与Bayou在20世纪90年代开创的方法类似【58】。
#### 多分区请求处理
-当涉及多个分区时,确保操作以原子方式执行,同时满足约束条件变得更有趣。在示[例12-2]()中,可能有三个分区:一个包含请求ID,一个包含收款人账户,另一个包含付款人账户。没有理由把这三件事放在同一个分区,因为它们都是相互独立的。
+ 当涉及多个分区时,确保操作以原子方式执行,同时满足约束条件变得更有趣。在示[例12-2]()中,可能有三个分区:一个包含请求ID,一个包含收款人账户,另一个包含付款人账户。没有理由把这三件事放在同一个分区,因为它们都是相互独立的。
-在数据库的传统方法中,执行此事务需要跨所有三个分区进行原子提交,这实质上会将它强制为与任何这些分区上的所有其他事务的总顺序。由于现在存在跨分区协调,不能再独立处理不同的分区,因此吞吐量可能会受到影响。
+ 在数据库的传统方法中,执行此事务需要跨所有三个分区进行原子提交,这实质上会将它强制为与任何这些分区上的所有其他事务的总顺序。由于现在存在跨分区协调,不能再独立处理不同的分区,因此吞吐量可能会受到影响。
但是,事实证明,使用分区日志可以实现同等的正确性,并且不需要原子提交:
1. 从账户A向账户B转账的请求由客户端提供唯一的请求ID,并根据请求ID附加到日志分区。
2. 流处理器读取请求的日志。对于每个请求消息,它发出两条消息以输出流:付款人账户A(由A分配)的借方指令和收款人账户B(由B分区)的信贷指令。原始的请求ID包含在那些发出的消息中。
-3. 其他处理器使用信用卡和借记指令流,通过请求ID进行扣除,并将更改应用于账户余额。
+3. 其他处理器使用信用卡和借记指令流,通过请求ID进行扣除,并将变更应用于账户余额。
步骤1和步骤2是必要的,因为如果客户直接发送信用和借记指令,则需要在这两个分区之间进行原子提交以确保两者都不发生。为了避免分布式事务的需要,我们首先将请求永久记录为单条消息,然后从第一条消息中获取信用和借记指令。单对象写入在几乎所有数据系统中都是原子性的(请参阅“[单对象写入]()”第213页),因此请求既可以出现在日志中,也可以不出现,而不需要多分区原子com-麻省理工学院。
-如果步骤2中的流处理器崩溃,则从上一个检查点恢复处理。这样做时,它不会跳过任何请求消息,但可能会多次处理请求并产生重复的信用和借记指令。但是,由于它是确定性的,因此它只会再次生成相同的指令,并且步骤3中的处理器可以使用端到端请求ID轻松地对它们进行重复数据删除。
-
-如果您想确保付款人帐户不会因此次转账而透支,您可以额外使用流处理器(分区)使用多个不同分区的阶段的想法与我们所讨论的类似“多分区数据处理”一节第514页(另请参阅“[并发控制]()”一节第462页)。
-
+ 如果步骤2中的流处理器崩溃,则从上一个检查点恢复处理。这样做时,它不会跳过任何请求消息,但可能会多次处理请求并产生重复的信用和借记指令。但是,由于它是确定性的,因此它只会再次生成相同的指令,并且步骤3中的处理器可以使用端到端请求ID轻松地对它们进行重复数据删除。
+ 如果您想确保付款人帐户不会因此次转账而透支,您可以额外使用流处理器(分区)使用多个不同分区的阶段的想法与我们所讨论的类似“多分区数据处理”一节第514页(另请参阅“[并发控制]()”一节第462页)。
### 及时性与完整性
-事务的一个方便属性是它们通常是可线性化的(请参阅“[可用性]()”),也就是说,一个作者等待事务提交,之后其所有读者立即可以看到它的写入。
+ 事务的一个方便属性是它们通常是可线性化的(请参阅“[可用性]()”),也就是说,一个写入者等待事务提交,之后其所有读者立即可以看到它的写入。
-在跨流处理器的多个阶段拆分操作时情况并非如此:日志的使用者在设计上是异步的,因此发送者不会等到其消息已经被消费者处理。但是,客户端可能会等待消息出现在输出流上。这是我们在检查是否满足唯一性约束时在“基于日志的消息传递中的唯一性”一节中所做的操作。
+ 在跨流处理器的多个阶段拆分操作时情况并非如此:日志的使用者在设计上是异步的,因此发送者不会等到其消息已经被消费者处理。但是,客户端可能会等待消息出现在输出流上。这是我们在检查是否满足唯一性约束时在“基于日志的消息传递中的唯一性”一节中所做的操作。
-在这个例子中,唯一性检查的正确性不取决于消息的发送者是否等待结果。等待仅具有同步通知发送者唯一性检查是否成功的目的,但是该通知可以与处理消息的效果分离。
+ 在这个例子中,唯一性检查的正确性不取决于消息的发送者是否等待结果。等待仅具有同步通知发送者唯一性检查是否成功的目的,但是该通知可以与处理消息的效果分离。
更一般地说,我认为术语一致性这个术语将两个不同的需求分开考虑:
***及时性***
-及时性意味着确保用户观察系统处于最新状态。我们之前看到,如果用户从数据的陈旧副本中读取数据,他们可能会以不一致的状态观察数据(请参阅第161页上的“[复制延迟问题]()”)。但是,这种不一致是暂时的,最终只能通过等待和再次尝试来解决。
+ 及时性意味着确保用户观察系统处于最新状态。我们之前看到,如果用户从数据的陈旧副本中读取数据,他们可能会以不一致的状态观察数据(请参阅第161页上的“[复制延迟问题]()”)。但是,这种不一致是暂时的,最终只能通过等待和再次尝试来解决。
-CAP定理(参见第359页的“[线性一致性的代价]()”)使用线性一致的意义上的一致性,这是实现及时性的强有力的方法。像写后读一致性这样的时效性较弱的属性(请参阅第162页的“[读己之写]()”)也很有用。
+ CAP定理(参见第359页的“[线性一致性的代价]()”)使用线性一致的意义上的一致性,这是实现及时性的强有力的方法。像写后读一致性这样的时效性较弱的属性(请参阅第162页的“[读己之写]()”)也很有用。
***完整性***
-诚信意味着没有腐败;即没有数据丢失,并且没有矛盾或错误的数据。尤其是,如果将某些衍生数据集作为某些基础数据的视图进行维护(请参阅“[从事件日志导出当前状态]()”(第458页)),衍生必须正确。例如,数据库索引必须正确地反映数据库的内容 —— 缺少某些记录的索引不是很有用。如果完整性受到侵犯,这种不一致是永久性的:在大多数情况下,等待并再次尝试不会修复数据库损坏。相反,需要明确的检查和修理。在ACID事务的上下文中(参见第223页上的“[ACID的含义]()”),一致性通常被理解为某种特定于应用程序的完整性概念。原子性和耐久性是保持完整性的重要工具。
-口号形式:违反及时性是“最终一致性”,而违反诚信则是“永久不一致”。
+ 完整性意味着没有腐败;即没有数据丢失,并且没有矛盾或错误的数据。尤其是,如果将某些衍生数据集作为某些基础数据的视图进行维护(请参阅“[从事件日志导出当前状态]()”(第458页)),衍生必须正确。例如,数据库索引必须正确地反映数据库的内容 —— 缺少某些记录的索引不是很有用。如果完整性受到侵犯,这种不一致是永久性的:在大多数情况下,等待并再次尝试不会修复数据库损坏。相反,需要明确的检查和修理。在ACID事务的上下文中(参见第223页上的“[ACID的含义]()”),一致性通常被理解为某种特定于应用程序的完整性概念。原子性和耐久性是保持完整性的重要工具。
-我要断言,在大多数应用中,完整性比时间要重要得多。违反时效可能令人讨厌和混淆,但是对正直的侵犯可能是灾难性的。
+ 口号形式:违反及时性是“最终一致性”,而违反诚信则是“永久不一致”。
-例如,在您的信用卡对账单上,如果您在过去24小时内完成的交易尚未出现,这并不奇怪 —— 这些系统有一定的滞后是正常的。我们知道银行协调和异步结算交易,并且这里的及时性并不重要【3】。但是,如果报表余额不等于交易总和加上先前的报表余额(数额错误),或者交易是向您收取但未支付给商家的话,那将是非常糟糕的(消失的金钱)。这样的问题会违反系统的完整性。
+ 我要断言,在大多数应用中,完整性比时间要重要得多。违反时效可能令人讨厌和混淆,但是对正直的侵犯可能是灾难性的。
+
+ 例如,在您的信用卡对账单上,如果您在过去24小时内完成的交易尚未出现,这并不奇怪 —— 这些系统有一定的滞后是正常的。我们知道银行协调和异步结算交易,并且这里的及时性并不重要【3】。但是,如果报表余额不等于交易总和加上先前的报表余额(数额错误),或者交易是向您收取但未支付给商家的话,那将是非常糟糕的(消失的金钱)。这样的问题会违反系统的完整性。
#### 数据流系统的正确性
-ACID事务通常既提供时间性(例如线性化)又提供完整性(例如原子提交)保证。因此,如果从ACID交易的角度来看应用程序的正确性,那么时间性与完整性的区别就相当不重要了。
+ ACID事务通常既提供时间性(例如线性化)又提供完整性(例如原子提交)保证。因此,如果从ACID交易的角度来看应用程序的正确性,那么时间性与完整性的区别就相当不重要了。
-另一方面,我们在本章中讨论的基于事件的数据流系统的一个有趣特性是它们将时间性和完整性分开。在异步处理事件流时,不能保证及时性,除非在返回之前明确地构建等待消息到达的消费者。但是,完整性实际上是流式传输系统的核心。
+ 另一方面,我们在本章中讨论的基于事件的数据流系统的一个有趣特性是它们将时间性和完整性分开。在异步处理事件流时,不能保证及时性,除非在返回之前明确地构建等待消息到达的消费者。但是,完整性实际上是流式传输系统的核心。
一次或一次有效的语义(请参阅“[故障容错]()”一节第437页)是一种保持完整性的机制。如果事件丢失,或者事件发生两次,数据系统的完整性可能被侵犯。因此,容错消息传递和重复抑制(例如,幂等操作)对于在面对故障时保持数据系统的完整性是重要的。
@@ -606,7 +605,7 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
性能和运行稳健性。我们通过以下机制的结合实现了这一完整性:
-* 将写入操作的内容表示为单个消息,可以轻松地以原子方式编写 —— 这种方法非常适合事件采购(请参阅第457页的“事件采购”)。
+* 将写入操作的内容表示为单个消息,可以轻松地以原子方式编写 —— 这种方法非常适合事件排序(请参阅第457页的“事件排序”)。
* 使用确定性描述函数从该单个消息中获取所有其他状态更新,这与存储过程类似(请参见第252页的“[真的串行执行]()”和第479页的“[作为衍生函数的应用程序代码]()”)
* 通过所有这些级别的处理传递客户端生成的请求ID,启用端到端重复抑制和幂等性
* 使消息不可变并允许衍生数据不时重新处理,这使得从错误中恢复变得更加容易(请参阅“[不可变事件的优点]()”第367页)
@@ -615,7 +614,7 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
#### 松散的解释约束
-如前所述,执行唯一性约束需要达成共识,通常通过在单个节点中汇集特定分区中的所有事件来实现。如果我们想要传统的唯一性约束形式,并且流处理无法避免,这种限制是不可避免的。
+ 如前所述,执行唯一性约束需要达成共识,通常通过在单个节点中汇集特定分区中的所有事件来实现。如果我们想要传统的唯一性约束形式,并且流处理无法避免,这种限制是不可避免的。
然而,另一个要认识到的是,许多真正的应用程序实际上可以摆脱唯一性较弱的概念:
@@ -624,13 +623,15 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
* 同样地,许多航空公司预计飞行员会错过飞机,许多旅馆超额预订客房,预计部分客人将取消预订。在这些情况下,出于商业原因故意违反了“每个座位一人”的约束,并且处理补偿过程(退款,升级,在邻近酒店提供免费房间)以处理需求超过供应的情况。即使没有超额预订,为了应对由于恶劣天气而被取消的航班或者罢工的员工,这些问题的恢复仅仅是商业活动的正常组成部分,就需要道歉和赔偿流程。
* 如果有人收取比他们账户中更多的钱,银行可以向他们收取透支费用,并要求他们偿还欠款。通过限制每天的提款总额,银行的风险是有限的。
-在许多商业环境中,临时违反约束并稍后通过道歉修复它实际上是可以接受的。道歉的成本(金钱或报酬)各不相同,但通常很低:您无法取消发送电子邮件,但可以发送后续电子邮件并进行更正。如果您不小心向信用卡收取了两次费用,您可以退还其中一项费用,而您的费用仅仅是处理费用,并且可以处理客户投诉。一旦自动提款机支付了钱,就不能直接将其退回,尽管原则上如果账户透支并且客户不支付,原则上可以派遣收债员收回款项。
-道歉的成本是否可以接受是一个商业决策。如果可以接受的话,在写入数据之前检查所有约束的传统模型是不必要的限制,并且不需要线性化约束。乐观地继续写作,并在事实之后检查约束,这可能是一个合理的选择。您仍然可以确保验证发生在做恢复成本高昂的事情之前,但这并不意味着在您编写数据之前您必须先进行验证。
-这些应用程序确实需要完整性:您不会希望失去预订,或者由于信用和借方不匹配而导致资金消失。但是他们并不需要及时执行约束:如果您销售的物品多于仓库中的物品,则可以在事后道歉后修补问题。这样做与我们在第171页上的“处理写入冲突”中讨论的冲突解决方法类似。
+ 在许多商业环境中,临时违反约束并稍后通过道歉修复它实际上是可以接受的。道歉的成本(金钱或报酬)各不相同,但通常很低:您无法取消发送电子邮件,但可以发送后续电子邮件并进行更正。如果您不小心向信用卡收取了两次费用,您可以退还其中一项费用,而您的费用仅仅是处理费用,并且可以处理客户投诉。一旦自动提款机支付了钱,就不能直接将其退回,尽管原则上如果账户透支并且客户不支付,原则上可以派遣收债员收回款项。
-#### 协调避免数据系统
+ 道歉的成本是否可以接受是一个商业决策。如果可以接受的话,在写入数据之前检查所有约束的传统模型是不必要的限制,并且不需要线性化约束。乐观地继续写作,并在事实之后检查约束,这可能是一个合理的选择。您仍然可以确保验证发生在做恢复成本高昂的事情之前,但这并不意味着在您编写数据之前您必须先进行验证。
+
+ 这些应用程序确实需要完整性:您不会希望失去预订,或者由于信用和借方不匹配而导致资金消失。但是他们并不需要及时执行约束:如果您销售的物品多于仓库中的物品,则可以在事后道歉后修补问题。这样做与我们在第171页上的“处理写入冲突”中讨论的冲突解决方法类似。
+
+#### 无协调数据系统
我们现在做了两个有趣的观察:
@@ -639,81 +640,81 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
总之,这些观察意味着数据流系统可以为许多应用程序提供数据管理服务,而不需要协调,同时仍然提供强大的完整性保证。这种避免协调的数据系统具有很大的吸引力:它们可以比需要执行同步协调的系统获得更好的性能和容错能力【56】。
-例如,这种系统可以在多引导器配置中跨多个数据中心进行分布式操作,在区域之间异步复制。任何一个数据中心都可以继续独立运行,因为不需要同步跨区域协调。这样一个系统的时效性保证会很弱 —— 如果不引入协调,它就不可能线性化,但它仍然可以提供强有力的完整性保证。
+ 例如,这种系统可以在多主配置中跨多个数据中心进行分布式操作,在区域之间异步复制。任何一个数据中心都可以继续独立运行,因为不需要同步跨区域协调。这样一个系统的时效性保证会很弱 —— 如果不引入协调,它就不可能线性化,但它仍然可以提供强有力的完整性保证。
-在这种情况下,序列化事务作为维护衍生状态的一部分仍然是有用的,但它们可以在小范围内运行,在那里它们工作得很好【8】。异构分布式事务(如XA事务)(请参阅“[实践中的分布式事务]()”(第367页))不是必需的。同步协调仍然可以在需要的地方引入(例如,在无法恢复的操作之前执行严格的限制),但是如果只有一个小的协议,则不需要任何东西来支付协调费用应用程序的一部分需要它【43】。
+ 在这种情况下,序列化事务作为维护衍生状态的一部分仍然是有用的,但它们可以在小范围内运行,在那里它们工作得很好【8】。异构分布式事务(如XA事务)(请参阅“[实践中的分布式事务]()”(第367页))不是必需的。同步协调仍然可以在需要的地方引入(例如,在无法恢复的操作之前执行严格的限制),但是如果只有一个小的协议,则不需要任何东西来支付协调费用应用程序的一部分需要它【43】。
-查看协调和约束的另一种方法是:它们减少了由于不一致而必须做出的道歉数量,但也可能会降低系统的性能和可用性,从而可能会增加必须制定的道歉数量中断。您不能将道歉数量减少到零,但您可以根据自己的需求寻找最佳平衡点 —— 这是既不存在太多不一致性又不存在太多可用性问题的最佳选择。
+ 查看协调和约束的另一种方法是:它们减少了由于不一致而必须做出的道歉数量,但也可能会降低系统的性能和可用性,从而可能会增加必须制定的道歉数量中断。您不能将道歉数量减少到零,但您可以根据自己的需求寻找最佳平衡点 —— 这是既不存在太多不一致性又不存在太多可用性问题的最佳选择。
### 信任,但需要验证
-我们所有关于正确性,完整性和容错性的讨论都假设某些事情可能会出错,但其他事情则不会。我们将这些假设称为我们的系统模型(请参阅“[将系统模型映射到现实世界]()”一节第309页):例如,我们应该假设进程可能会崩溃,机器可能突然断电,网络可能会任意延迟或丢弃消息。但是我们也可以假设写入磁盘的数据在`fsync`之后不会丢失,内存中的数据没有损坏,并且CPU的乘法指令总是返回正确的结果。
+ 我们所有关于正确性,完整性和容错性的讨论都假设某些事情可能会出错,但其他事情则不会。我们将这些假设称为我们的系统模型(请参阅“[将系统模型映射到现实世界]()”一节第309页):例如,我们应该假设进程可能会崩溃,机器可能突然断电,网络可能会任意延迟或丢弃消息。但是我们也可以假设写入磁盘的数据在`fsync`之后不会丢失,内存中的数据没有损坏,并且CPU的乘法指令总是返回正确的结果。
-这些假设是非常合理的,因为大多数时候它们都是真实的,如果我们不得不经常担心计算机出错,那么很难完成任何事情。传统上,系统模型采用二元方法处理故障:我们假设有些事情会发生,而其他事情永远不会发生。实际上,这更像是一个概率问题:有些事情更可能,其他事情不太可能。问题是违反我们的假设是否经常发生,以至于我们可能在实践中遇到它们。
+ 这些假设是非常合理的,因为大多数时候它们都是真实的,如果我们不得不经常担心计算机出错,那么很难完成任何事情。传统上,系统模型采用二元方法处理故障:我们假设有些事情会发生,而其他事情永远不会发生。实际上,这更像是一个概率问题:有些事情更可能,其他事情不太可能。问题是违反我们的假设是否经常发生,以至于我们可能在实践中遇到它们。
-我们已经看到,数据可能会在磁盘未触及时破损(请参阅第227页的“[复制和耐久性]()”),并且网络上的数据损坏有时可能会妨碍TCP校验和(请参阅第293页的“[虚弱形式的说谎]()” )。也许这是我们应该更加关注的事情?
+ 我们已经看到,数据可能会在磁盘未触及时破损(请参阅第227页的“[复制和耐久性]()”),并且网络上的数据损坏有时可能会妨碍TCP校验和(请参阅第293页的“[虚弱形式的说谎]()” )。也许这是我们应该更加关注的事情?
-我过去曾经使用过的一个应用程序收集了来自客户端的崩溃报告,我们收到的一些报告只能通过在这些设备的内存中随机位翻转来解释。这看起来不太可能,但是如果你有足够的设备来运行你的软件,那么即使发生的事情也不会发生。除了由于硬件故障或辐射导致的随机存储器损坏之外,某些病态存储器访问模式甚至可以在没有故障的存储器中翻转位【62】 —— 可用于破坏操作系统中安全机制的效应【63】技术被称为rowhammer)。一旦你仔细观察,硬件并不是完美的抽象。
+ 我过去曾经使用过的一个应用程序收集了来自客户端的崩溃报告,我们收到的一些报告只能通过在这些设备的内存中随机位翻转来解释。这看起来不太可能,但是如果你有足够的设备来运行你的软件,那么即使发生的事情也不会发生。除了由于硬件故障或辐射导致的随机存储器损坏之外,某些病态存储器访问模式甚至可以在没有故障的存储器中翻转位【62】 —— 可用于破坏操作系统中安全机制的效应【63】技术被称为rowhammer)。一旦你仔细观察,硬件并不是完美的抽象。
-要清楚的是,随机位翻转在现代硬件上仍然非常罕见【64】。我只想指出,他们并没有超越可能性领域,所以他们值得关注。
+ 要清楚的是,随机位翻转在现代硬件上仍然非常罕见【64】。我只想指出,他们并没有超越可能性领域,所以他们值得关注。
#### 在软件缺陷面前保持完整性
-除了这些硬件问题之外,总是存在软件错误的风险,这些错误不会被较低级别的网络,内存或文件系统校验和所捕获。即使广泛使用的数据库软件也存在一些问题:即使MySQL和PostgreSQL是健壮且充分的,但我个人发现MySQL的例子未能正确维护唯一性约束【65】和PostgreSQL的序列化隔离级别,表现出写入偏斜异常【66】他们认为这些数据库已经被许多人进行了多年的战斗测试。在不太成熟的软件中,情况可能会更糟糕。
+ 除了这些硬件问题之外,总是存在软件错误的风险,这些错误不会被较低级别的网络,内存或文件系统校验和所捕获。即使广泛使用的数据库软件也存在一些问题:即使MySQL和PostgreSQL是健壮且充分的,但我个人发现MySQL的例子未能正确维护唯一性约束【65】和PostgreSQL的序列化隔离级别,表现出写入偏斜异常【66】他们认为这些数据库已经被许多人进行了多年的战斗测试。在不太成熟的软件中,情况可能会更糟糕。
-尽管在仔细设计,测试和审查方面做出了相当大的努力,但错误仍在蔓延。虽然它们很少,并且最终被发现并修复,但仍然存在一段时期,这些错误可能会破坏数据。
+ 尽管在仔细设计,测试和审查方面做出了相当大的努力,但错误仍在蔓延。虽然它们很少,并且最终被发现并修复,但仍然存在一段时期,这些错误可能会破坏数据。
-当涉及到应用程序代码时,我们不得不承担更多的错误,因为大多数应用程序在数据库代码所做的评审和测试的数量上没有接近的地方。许多应用程序甚至没有正确使用数据库提供的用于保存完整性的功能,例如外键或唯一性约束【36】。
+ 当涉及到应用程序代码时,我们不得不承担更多的错误,因为大多数应用程序在数据库代码所做的评审和测试的数量上没有接近的地方。许多应用程序甚至没有正确使用数据库提供的用于保存完整性的功能,例如外键或唯一性约束【36】。
-ACID意义上的一致性(请参阅第224页的“[一致性]()”)基于数据库以一致状态启动并且事务将其从一个一致状态转换为另一个一致状态的想法。因此,我们期望数据库始终处于一致状态。但是,如果您认为该交易没有错误,则这个概念才有意义。如果应用程序错误地使用数据库以某种方式,例如,不安全地使用弱隔离级别,数据库的完整性无法得到保证。
+ ACID意义上的一致性(请参阅第224页的“[一致性]()”)基于数据库以一致状态启动并且事务将其从一个一致状态转换为另一个一致状态的想法。因此,我们期望数据库始终处于一致状态。但是,如果您认为该交易没有错误,则这个概念才有意义。如果应用程序错误地使用数据库以某种方式,例如,不安全地使用弱隔离级别,数据库的完整性无法得到保证。
#### 不要盲目信任他们的承诺
-由于硬件和软件并不总是符合我们希望的理想,所以数据腐败似乎迟早是不可避免的。因此,我们至少应该有办法查明数据是否已经损坏,以便我们能够修复它并尝试追查错误的来源。检查数据的完整性称为审计。
+ 由于硬件和软件并不总是符合我们希望的理想,所以数据腐败似乎迟早是不可避免的。因此,我们至少应该有办法查明数据是否已经损坏,以便我们能够修复它并尝试追查错误的来源。检查数据的完整性称为审计。
-如“不可变事件的优点”一节中所述,审计不仅适用于财务应用程序。然而,可审计性在财务中非常重要,因为每个人都知道错误发生了,而且我们都认识到需要能够检测和解决问题。
+ 如“不可变事件的优点”一节中所述,审计不仅适用于财务应用程序。然而,可审计性在财务中非常重要,因为每个人都知道错误发生了,而且我们都认识到需要能够检测和解决问题。
-成熟的系统同样倾向于考虑不太可能的事情出错的可能性,并管理这种风险。例如,HDFS和Amazon S3等大规模存储系统不完全信任磁盘:它们运行后台进程,这些后台进程持续读回文件,将其与其他副本进行比较,并将文件从一个磁盘移动到另一个磁盘,以便减轻沉默腐败的风险【67】。
+ 成熟的系统同样倾向于考虑不太可能的事情出错的可能性,并管理这种风险。例如,HDFS和Amazon S3等大规模存储系统不完全信任磁盘:它们运行后台进程,这些后台进程持续读回文件,将其与其他副本进行比较,并将文件从一个磁盘移动到另一个磁盘,以便减轻沉默腐败的风险【67】。
-如果你想确保你的数据仍然存在,你必须真正阅读并检查。大多数时候它仍然会在那里,但如果不是这样,你真的很想早日找到答案。通过同样的说法,尝试从备份中不时恢复是非常重要的,否则只有当它太迟而您已经丢失数据时才会发现备份被破坏。不要盲目地相信它是全部工作。
+ 如果你想确保你的数据仍然存在,你必须真正阅读并检查。大多数时候它仍然会在那里,但如果不是这样,你真的很想早日找到答案。通过同样的说法,尝试从备份中不时恢复是非常重要的,否则只有当它太迟而您已经丢失数据时才会发现备份被破坏。不要盲目地相信它是全部工作。
#### 验证的文化
-像HDFS和S3这样的系统仍然必须假定磁盘大部分时间都能正常工作 —— 这是一个合理的假设,但假设它们始终正常工作并不相同。然而,目前还没有多少系统有这种“信任但是验证”的方式来持续审计自己。许多人认为正确性保证是绝对的,并且没有为罕见数据损坏的可能性做出规定。我希望将来我们会看到更多的自我验证或自我审计系统,不断检查自己的诚信,而不是依赖盲目的信任【68】。
+ 像HDFS和S3这样的系统仍然必须假定磁盘大部分时间都能正常工作 —— 这是一个合理的假设,但假设它们始终正常工作并不相同。然而,目前还没有多少系统有这种“信任但是验证”的方式来持续审计自己。许多人认为正确性保证是绝对的,并且没有为罕见数据损坏的可能性做出规定。我希望将来我们会看到更多的自我验证或自我审计系统,不断检查自己的诚信,而不是依赖盲目的信任【68】。
-我担心ACID数据库的文化导致我们在盲目信任技术(如交易机制)的基础上开发应用程序,而忽视了这种过程中的任何可审计性。由于我们所信任的技术在大多数情况下工作得很好,审计机制并不值得投资。
+ 我担心ACID数据库的文化导致我们在盲目信任技术(如交易机制)的基础上开发应用程序,而忽视了这种过程中的任何可审计性。由于我们所信任的技术在大多数情况下工作得很好,审计机制并不值得投资。
-但随之而来的是数据库的格局发生了变化:在NoSQL的旗帜下,一致性的保证变得越来越少,成熟的存储技术越来越被广泛的使用。但是,由于审计机制还没有形成,我们继续在盲目信任的基础上建立应用程序,即使这种方法现在变得更加危险。让我们思考一下关于可审计性的设计。
+ 但随之而来的是数据库的格局发生了变化:在NoSQL的旗帜下,一致性的保证变得越来越少,成熟的存储技术越来越被广泛的使用。但是,由于审计机制还没有形成,我们继续在盲目信任的基础上建立应用程序,即使这种方法现在变得更加危险。让我们思考一下关于可审计性的设计。
#### 设计可审计性
-如果一个事务在一个数据库中改变了多个对象,事实上很难说事实是什么意思。即使您捕获事务日志(请参阅第454页上的“[更改数据捕获]()”),各种表中的插入,更新和删除操作并不一定清楚表明为何执行这些突变。决定这些突变的应用逻辑的调用是暂时的,不能被复制。
+ 如果一个事务在一个数据库中改变了多个对象,事实上很难说事实是什么意思。即使您捕获事务日志(请参阅第454页上的“[变更数据捕获]()”),各种表中的插入,更新和删除操作并不一定清楚表明为何执行这些突变。决定这些突变的应用逻辑的调用是暂时的,不能被复制。
-相比之下,基于事件的系统可以提供更好的可审计性。在事件源方法中,系统的用户输入被表示为一个单一的不可变事件,并且任何结果状态更新都来自该事件。衍生可以做出确定性和可重复性,以便通过相同版本的衍生代码运行相同的事件日志将导致相同的状态更新。
+ 相比之下,基于事件的系统可以提供更好的可审计性。在事件源方法中,系统的用户输入被表示为一个单一的不可变事件,并且任何结果状态更新都来自该事件。衍生可以做出确定性和可重复性,以便通过相同版本的衍生代码运行相同的事件日志将导致相同的状态更新。
-清楚地看到数据流(请参阅第419页上的“[批处理输出的原理]()”)可以使数据的来源更加清晰,从而使完整性检查更加可行。对于事件日志,我们可以使用散列来检查事件存储没有被破坏。对于任何衍生状态,我们可以重新运行从事件日志中衍生它的批处理和流处理器,以检查是否获得相同的结果,或者甚至并行运行冗余衍生。
+ 清楚地看到数据流(请参阅第419页上的“[批处理输出的原理]()”)可以使数据的来源更加清晰,从而使完整性检查更加可行。对于事件日志,我们可以使用散列来检查事件存储没有被破坏。对于任何衍生状态,我们可以重新运行从事件日志中衍生它的批处理和流处理器,以检查是否获得相同的结果,或者甚至并行运行冗余衍生。
-确定性和定义良好的数据流也使调试和跟踪系统的执行变得容易,以确定它为什么做了某些事情【4,69】。如果出现意想不到的事情,那么具有诊断能力来重现导致意外事件的确切环境 —— 一种时间行程调试功能是非常有价值的。
+ 确定性和定义良好的数据流也使调试和跟踪系统的执行变得容易,以确定它为什么做了某些事情【4,69】。如果出现意想不到的事情,那么具有诊断能力来重现导致意外事件的确切环境 —— 一种时间行程调试功能是非常有价值的。
#### 再次是端到端的论点
-如果我们不能完全相信系统的每个组件都不会发生腐败 —— 每一个硬件都是无错的,并且每一个软件都没有缺陷 —— 那么我们至少必须定期检查数据的完整性。如果我们不检查,我们就不会发现腐败,直到它太晚了,并且已经造成了一些下游损害,在这一点上追踪这个问题将变得更加困难和昂贵。
+ 如果我们不能完全相信系统的每个组件都不会发生腐败 —— 每一个硬件都是无错的,并且每一个软件都没有缺陷 —— 那么我们至少必须定期检查数据的完整性。如果我们不检查,我们就不会发现腐败,直到它太晚了,并且已经造成了一些下游损害,在这一点上追踪这个问题将变得更加困难和昂贵。
-检查数据系统的完整性最好是以端到端的方式完成(请参阅“[数据库的端到端争论]()”,51页):我们可以在完整性检查中包含的系统越多,那里的机会就越少在这个过程的某个阶段,腐败是被忽视的。如果我们可以检查整个衍生数据管道是端到端正确的,那么沿着路径的任何磁盘,网络,服务和算法都隐含在检查中。
+ 检查数据系统的完整性最好是以端到端的方式完成(请参阅“[数据库的端到端争论]()”,51页):我们可以在完整性检查中包含的系统越多,那里的机会就越少在这个过程的某个阶段,腐败是被忽视的。如果我们可以检查整个衍生数据管道是端到端正确的,那么沿着路径的任何磁盘,网络,服务和算法都隐含在检查中。
-持续的端到端完整性检查可以提高您对系统正确性的信心,从而使您的移动速度更快【70】。与自动化测试一样,审计增加了发现错误的可能性,从而降低了系统更改或新存储技术造成损害的风险。如果您不害怕进行更改,您可以更好地开发应用程序以满足不断变化的需求。
+ 持续的端到端完整性检查可以提高您对系统正确性的信心,从而使您的移动速度更快【70】。与自动化测试一样,审计增加了发现错误的可能性,从而降低了系统变更或新存储技术造成损害的风险。如果您不害怕进行变更,您可以更好地开发应用程序以满足不断变化的需求。
#### 审计数据系统的工具
-目前,数据系统并不多,这使可审计性成为高层关注的重点。一些应用程序实现自己的审计机制,例如将所有更改记录到单独的审计表中,但是保证审计日志的完整性,并且数据库状态仍然很困难。事务日志可以通过定期使用硬件安全模块对事务日志进行签名来进行防篡改,但这并不能保证正确的事务首先进入日志。
+ 目前,数据系统并不多,这使可审计性成为高层关注的重点。一些应用程序实现自己的审计机制,例如将所有变更记录到单独的审计表中,但是保证审计日志的完整性,并且数据库状态仍然很困难。事务日志可以通过定期使用硬件安全模块对事务日志进行签名来进行防篡改,但这并不能保证正确的事务首先进入日志。
-使用加密工具来证明系统的完整性是十分有趣的,这种方式对于广泛的硬件和软件问题以及甚至是潜在的恶意行为都很有效。加密货币,区块链和分布式账本技术,如比特币,以太坊,波纹,恒星等等【71,72,73】已经出现在这个领域。
+ 使用加密工具来证明系统的完整性是十分有趣的,这种方式对于广泛的硬件和软件问题以及甚至是潜在的恶意行为都很有效。加密货币,区块链和分布式账本技术,如比特币,以太坊,波纹,恒星等等【71,72,73】已经出现在这个领域。
-我没有资格评论这些技术作为商定合同的货币或机制的优点。但是,从数据系统的角度来看,它们包含了一些有趣的想法。实质上,它们是分布式数据库,具有数据模型和事务机制,不同副本可以由互不信任的组织托管。复制品不断检查彼此的完整性,并使用共识协议来约定应执行的交易。
+ 我没有资格评论这些技术作为商定合同的货币或机制的优点。但是,从数据系统的角度来看,它们包含了一些有趣的想法。实质上,它们是分布式数据库,具有数据模型和事务机制,不同副本可以由互不信任的组织托管。复制品不断检查彼此的完整性,并使用共识协议来约定应执行的交易。
我对这些技术的拜占庭容错方面有些怀疑(参见第304页的“[拜占庭故障]()”),而且我发现工作证明(比如比特币挖掘)技术非常浪费。比特币的交易吞吐量相当低,尽管出于政治和经济原因而不是技术交易。但是,完整性检查方面很有趣。
-我可以想象完整性检查和审计算法(如证书透明度和分布式分类账算法)在一般数据系统中得到越来越广泛的应用。 一些工作将需要使它们具有同样的可扩展性,因为没有加密审计的系统,并且尽可能降低性能损失。 但我认为这是一个值得关注的领域。
+ 我可以想象完整性检查和审计算法(如证书透明度和分布式分类账算法)在一般数据系统中得到越来越广泛的应用。 一些工作将需要使它们具有同样的可扩展性,因为没有加密审计的系统,并且尽可能降低性能损失。 但我认为这是一个值得关注的领域。
@@ -721,130 +722,130 @@ ACID意义上的一致性(请参阅第224页的“[一致性]()”)基于数
## 做正确的事情
-在本书的最后部分,我想退后一步。在本书中,我们考察了各种不同的数据系统体系结构,评估了它们的优缺点,并探讨了构建可靠,可扩展和可维护应用程序的技术。但是,我们忽略了现在我想填补的讨论中一个重要的基本部分。
+ 在本书的最后部分,我想退后一步。在本书中,我们考察了各种不同的数据系统体系结构,评估了它们的优缺点,并探讨了构建可靠,可扩展和可维护应用程序的技术。但是,我们忽略了现在我想填补的讨论中一个重要的基本部分。
-每个系统都是为了一个目的而建造的我们采取的每一项行动都有既定的意义,也有无意义的后果。其目的可能与赚钱一样简单,但世界的后果可能会远远超出最初的目的。我们这些建立这些系统的工程师有责任仔细考虑这些后果并有意识地决定我们希望生活在哪一种世界。
+ 每个系统都是为了一个目的而建造的我们采取的每一项行动都有既定的意义,也有无意义的后果。其目的可能与赚钱一样简单,但世界的后果可能会远远超出最初的目的。我们这些建立这些系统的工程师有责任仔细考虑这些后果并有意识地决定我们希望生活在哪一种世界。
-我们将数据作为一个抽象的东西来讨论,但请记住,许多数据集都是关于人的:他们的行为,他们的兴趣,他们的身份。我们必须以人性和尊重来对待这些数据。用户也是人类,人的尊严是至高无上的。
+ 我们将数据作为一个抽象的东西来讨论,但请记住,许多数据集都是关于人的:他们的行为,他们的兴趣,他们的身份。我们必须以人性和尊重来对待这些数据。用户也是人类,人的尊严是至高无上的。
-软件开发日益涉及重要的道德选择。有一些指导原则可以帮助软件工程师解决这些问题,例如ACM的软件工程道德规范和专业实践【77】,但很少在实践中讨论,应用和实施。因此,工程师和产品经理有时会对隐私和产品潜在的负面后果采取非常傲慢的态度【78,79,80】。
+ 软件开发日益涉及重要的道德选择。有一些指导原则可以帮助软件工程师解决这些问题,例如ACM的软件工程道德规范和专业实践【77】,但很少在实践中讨论,应用和实施。因此,工程师和产品经理有时会对隐私和产品潜在的负面后果采取非常傲慢的态度【78,79,80】。
-技术本身并不好或不好,重要的是如何使用它以及它如何影响人。对于像搜索引擎这样的软件系统来说,就像使用像武器这样的武器非常相似。我认为,软件工程师仅仅专注于技术并忽视其后果是不够的:伦理责任也是我们的责任。有关道德的推理是困难的,但忽视这一点太重要了。
+ 技术本身并不好或不好,重要的是如何使用它以及它如何影响人。对于像搜索引擎这样的软件系统来说,就像使用像武器这样的武器非常相似。我认为,软件工程师仅仅专注于技术并忽视其后果是不够的:伦理责任也是我们的责任。有关道德的推理是困难的,但忽视这一点太重要了。
### 预测分析
-例如,预测分析是“大数据”炒作的主要部分。使用数据分析预测天气或疾病传播是一回事【81】;另一个问题是要预测一个罪犯是否可能再犯罪,一个贷款申请人是否有可能违约,或者一个保险客户是否有可能做出昂贵的索赔。后者直接影响到个人的生活。当然,支付网络希望防止欺诈交易,银行希望避免不良贷款,航空公司希望避免劫持,公司希望避免激怒无效或不可靠的人。从他们的角度来看,错过商业机会的成本很低,但不良贷款或有问题的员工的成本要高得多,因此组织希望保持谨慎是很自然的。如果有疑问,他们最好不要说。
+ 例如,预测分析是“大数据”炒作的主要部分。使用数据分析预测天气或疾病传播是一回事【81】;另一个问题是要预测一个罪犯是否可能再犯罪,一个贷款申请人是否有可能违约,或者一个保险客户是否有可能做出昂贵的索赔。后者直接影响到个人的生活。当然,支付网络希望防止欺诈交易,银行希望避免不良贷款,航空公司希望避免劫持,公司希望避免激怒无效或不可靠的人。从他们的角度来看,错过商业机会的成本很低,但不良贷款或有问题的员工的成本要高得多,因此组织希望保持谨慎是很自然的。如果有疑问,他们最好不要说。
-然而,随着算法决策变得越来越普遍,某人(准确地或错误地)被某种算法标记为有风险,可能会遭受大量“不”决定。系统地排除工作,航空旅行,保险,物业租赁,金融服务和社会其他关键方面是个人自由的巨大制约因素,因此被称为“算法监狱”【82】。在尊重人权的国家,刑事司法系统在被证明有罪之前就认定无罪。另一方面,自动化系统可以有系统地,任意地排除一个人参与社会活动,而没有任何内疚的证据,而且几乎没有上诉的机会。
+ 然而,随着算法决策变得越来越普遍,某人(准确地或错误地)被某种算法标记为有风险,可能会遭受大量“不”决定。系统地排除工作,航空旅行,保险,物业租赁,金融服务和社会其他关键方面是个人自由的巨大制约因素,因此被称为“算法监狱”【82】。在尊重人权的国家,刑事司法系统在被证明有罪之前就认定无罪。另一方面,自动化系统可以有系统地,任意地排除一个人参与社会活动,而没有任何内疚的证据,而且几乎没有上诉的机会。
#### 偏见和歧视
-算法做出的决定不一定比人类做出的更好或更差。每个人都可能有偏见,即使他们积极地试图抵消它们,歧视性做法也可能在文化上被制度化。人们希望根据数据做出决定,而不是通过人们的主观和现场评估来更加公平,给那些在传统系统中经常被忽视的人更好的机会【83】。
+ 算法做出的决定不一定比人类做出的更好或更差。每个人都可能有偏见,即使他们积极地试图抵消它们,歧视性做法也可能在文化上被制度化。人们希望根据数据做出决定,而不是通过人们的主观和现场评估来更加公平,给那些在传统系统中经常被忽视的人更好的机会【83】。
-当我们开发预测分析系统时,我们不仅仅是通过使用软件来指定什么时候说是或否的规则来自动化人的决定;我们甚至将规则本身从数据中推断出来。但是,这些系统学到的模式是不透明的:即使数据中存在一些相关性,我们也可能不知道为什么。如果在算法输入中存在系统偏差,系统很可能会在输出中学习和放大偏差【84】。
+ 当我们开发预测分析系统时,我们不仅仅是通过使用软件来指定什么时候说是或否的规则来自动化人的决定;我们甚至将规则本身从数据中推断出来。但是,这些系统学到的模式是不透明的:即使数据中存在一些相关性,我们也可能不知道为什么。如果在算法输入中存在系统偏差,系统很可能会在输出中学习和放大偏差【84】。
-在许多国家,反歧视法律禁止根据种族,年龄,性别,性别,残疾或信仰等受保护特征对待不同的人。一个人的数据的其他特征可能会被分析,但是如果他们与受保护的特征相关会发生什么?例如,在种族隔离的邻居中,一个人的邮政编码,甚至是他们的IP地址,都是一个强烈的种族预测。这样说,相信一种算法可以以某种方式将偏倚的数据作为输入并产生公平和公正的输出【85】,这似乎是荒谬的。然而,这种观点似乎常常被数据驱动型决策制定的支持者所暗示,这种态度被讽刺为“机器学习就像洗钱对于偏见”【86】。
+ 在许多国家,反歧视法律禁止根据种族,年龄,性别,性别,残疾或信仰等受保护特征对待不同的人。一个人的数据的其他特征可能会被分析,但是如果他们与受保护的特征相关会发生什么?例如,在种族隔离的邻居中,一个人的邮政编码,甚至是他们的IP地址,都是一个强烈的种族预测。这样说,相信一种算法可以以某种方式将偏倚的数据作为输入并产生公平和公正的输出【85】,这似乎是荒谬的。然而,这种观点似乎常常被数据驱动型决策制定的支持者所暗示,这种态度被讽刺为“机器学习就像洗钱对于偏见”【86】。
-预测分析系统只是从过去推断出来的;如果过去是歧视性的,他们就会把这种歧视归为法律。如果我们希望未来比过去更好,那么就需要道德想象力,而这只有人类才能提供【87】。数据和模型应该是我们的工具,而不是我们的主人。
+ 预测分析系统只是从过去推断出来的;如果过去是歧视性的,他们就会把这种歧视归为法律。如果我们希望未来比过去更好,那么就需要道德想象力,而这只有人类才能提供【87】。数据和模型应该是我们的工具,而不是我们的主人。
#### 责任和问责
-自动决策开启了责任和问责的问题【87】。如果一个人犯了错误,他们可以被追究责任,受决定影响的人可以上诉。算法也会犯错误,但是如果他们出错,谁会犯错误【88】?当一辆自驾车引发事故时,谁负责?如果自动信用评分算法系统地区分特定种族或宗教的人,是否有任何追索权?如果机器学习系统的决定受到司法审查,您能向法官解释算法是如何做出决定的吗?
+ 自动决策开启了责任和问责的问题【87】。如果一个人犯了错误,他们可以被追究责任,受决定影响的人可以上诉。算法也会犯错误,但是如果他们出错,谁会犯错误【88】?当一辆自驾车引发事故时,谁负责?如果自动信用评分算法系统地区分特定种族或宗教的人,是否有任何追索权?如果机器学习系统的决定受到司法审查,您能向法官解释算法是如何做出决定的吗?
-信用评级机构是收集数据来做出人们决策的一个老例子。不良信用评分会使生活变得困难,但至少一个信用评分通常是基于一个人的实际借款历史的相关事实,并且记录中的任何错误都可以得到纠正(尽管机构通常不会这么容易)。然而,基于机器学习的评分算法通常使用更广泛的输入范围,并且更加不透明,使得难以理解特定决策是如何发生的以及是否有人正在以不公平或歧视的方式被对待【89】。
+ 信用评级机构是收集数据来做出人们决策的一个老例子。不良信用评分会使生活变得困难,但至少一个信用评分通常是基于一个人的实际借款历史的相关事实,并且记录中的任何错误都可以得到纠正(尽管机构通常不会这么容易)。然而,基于机器学习的评分算法通常使用更广泛的输入范围,并且更加不透明,使得难以理解特定决策是如何发生的以及是否有人正在以不公平或歧视的方式被对待【89】。
-信用评分总结了“你过去的表现如何?”,而预测分析通常是基于“谁与你类似,以及过去人们喜欢你的行为如何?”。与他人行为相似的绘图意味着刻板印象人们,例如根据他们居住的地方(一个关于种族和社会经济阶层的密切代理人)。那些放错人的人呢?而且,如果由于错误的数据而做出的决定是不正确的,则追索权几乎是不可能的【87】。
+ 信用评分总结了“你过去的表现如何?”,而预测分析通常是基于“谁与你类似,以及过去人们喜欢你的行为如何?”。与他人行为相似的绘图意味着刻板印象人们,例如根据他们居住的地方(一个关于种族和社会经济阶层的密切代理人)。那些放错人的人呢?而且,如果由于错误的数据而做出的决定是不正确的,则追索权几乎是不可能的【87】。
-很多数据本质上是统计的,这意味着即使概率分布总体上是正确的,个别情况也可能是错的。例如,如果贵国的平均寿命是80岁,那么这并不意味着在80岁生日时就会死亡。从平均分布和概率分布来看,你不能说很多关于某个特定人的生活年龄。同样,预测系统的输出是概率性的,在个别情况下可能是错误的。
+ 很多数据本质上是统计的,这意味着即使概率分布总体上是正确的,个别情况也可能是错的。例如,如果贵国的平均寿命是80岁,那么这并不意味着在80岁生日时就会死亡。从平均分布和概率分布来看,你不能说很多关于某个特定人的生活年龄。同样,预测系统的输出是概率性的,在个别情况下可能是错误的。
-盲目相信数据至高无上的决策不仅是妄想,它是非常危险的。随着数据驱动的决策变得越来越普遍,我们需要弄清楚如何使算法负责任和透明,如何避免加强现有的偏见,以及如何在不可避免的错误时加以修复。
+ 盲目相信数据至高无上的决策不仅是妄想,它是非常危险的。随着数据驱动的决策变得越来越普遍,我们需要弄清楚如何使算法负责任和透明,如何避免加强现有的偏见,以及如何在不可避免的错误时加以修复。
-我们还需要弄清楚如何防止数据被用来伤害人,并且实现其正向潜力。例如,分析可以揭示人们生活的财务和社会特征。一方面,这种权力可以用来把援助和支持集中在帮助那些最需要援助的人身上。另一方面,它有时被掠夺性企业用于识别弱势群体并向高风险产品销售,如高成本贷款和无价值的大学学位【87,90】。
+ 我们还需要弄清楚如何防止数据被用来伤害人,并且实现其正向潜力。例如,分析可以揭示人们生活的财务和社会特征。一方面,这种权力可以用来把援助和支持集中在帮助那些最需要援助的人身上。另一方面,它有时被掠夺性企业用于识别弱势群体并向高风险产品销售,如高成本贷款和无价值的大学学位【87,90】。
#### 反馈回路
-即使预测性应用程序对人们的影响较小,比如推荐系统,也存在难以解决的问题。当服务善于预测用户想要看到什么内容时,他们最终可能只会向人们展示他们已经同意的观点,导致产生陈腔滥调,错误信息和极化可能滋生的回声室。我们已经看到社交媒体呼应室对竞选活动的影响【91】。
+ 即使预测性应用程序对人们的影响较小,比如推荐系统,也存在难以解决的问题。当服务善于预测用户想要看到什么内容时,他们最终可能只会向人们展示他们已经同意的观点,导致产生陈腔滥调,错误信息和极化可能滋生的回声室。我们已经看到社交媒体呼应室对竞选活动的影响【91】。
-当预测分析影响人们的生活时,特别是由于自我强化反馈循环而出现有害问题。例如,考虑雇主使用信用评分来评估潜在员工的情况。你可能是一个信誉好的好员工,但是由于你不能控制的不幸,你会突然发现自己陷入财务困境。由于您错过了账单付款,您的信用评分会受到影响,您将不太可能找到工作。失业使你陷入贫困,这进一步恶化了你的分数,使其更难找到工作【87】。由于有毒的假设,这是一个下降的螺旋,隐藏在数学严谨和数据伪装的背后。
+ 当预测分析影响人们的生活时,特别是由于自我强化反馈循环而出现有害问题。例如,考虑雇主使用信用评分来评估潜在员工的情况。你可能是一个信誉好的好员工,但是由于你不能控制的不幸,你会突然发现自己陷入财务困境。由于您错过了账单付款,您的信用评分会受到影响,您将不太可能找到工作。失业使你陷入贫困,这进一步恶化了你的分数,使其更难找到工作【87】。由于有毒的假设,这是一个下降的螺旋,隐藏在数学严谨和数据伪装的背后。
-我们不能总是预测这种反馈循环何时发生。然而,通过考虑整个系统(不仅仅是计算机化的部分,而且还有与之互动的人),可以预测许多后果—— 一种称为系统思维的方法【92】。我们可以尝试理解数据分析系统如何响应不同的行为,结构或特性。该系统是否加强和扩大了人们之间现有的差异(例如,使富人更穷或更穷),还是试图打击不公正?即使有最好的意图,我们也必须小心意想不到的后果。
+ 我们不能总是预测这种反馈循环何时发生。然而,通过考虑整个系统(不仅仅是计算机化的部分,而且还有与之互动的人),可以预测许多后果—— 一种称为系统思维的方法【92】。我们可以尝试理解数据分析系统如何响应不同的行为,结构或特性。该系统是否加强和扩大了人们之间现有的差异(例如,使富人更穷或更穷),还是试图打击不公正?即使有最好的意图,我们也必须小心意想不到的后果。
#### 隐私和跟踪
-除了预测分析的问题 —— 即使用数据来做出关于人的自动决策 —— 数据收集本身也存在道德问题。收集数据的组织与正在收集数据的人之间有什么关系?
+ 除了预测分析的问题 —— 即使用数据来做出关于人的自动决策 —— 数据收集本身也存在道德问题。收集数据的组织与正在收集数据的人之间有什么关系?
-当系统仅存储用户明确输入的数据时,系统会以特定方式存储和处理数据,系统正在为用户执行服务:用户就是客户。但是,当用户的活动被跟踪并记录为他们正在做的其他事情的副作用时,这种关系就不那么清晰了。该服务不再仅仅是用户告诉它做的事情,而是服务于它自己的利益,这可能与用户的兴趣相冲突。
+ 当系统仅存储用户明确输入的数据时,系统会以特定方式存储和处理数据,系统正在为用户执行服务:用户就是客户。但是,当用户的活动被跟踪并记录为他们正在做的其他事情的副作用时,这种关系就不那么清晰了。该服务不再仅仅是用户告诉它做的事情,而是服务于它自己的利益,这可能与用户的兴趣相冲突。
-跟踪行为数据对于许多在线服务的面向用户的功能变得越来越重要:跟踪点击哪些搜索结果有助于提高搜索结果的排名;推荐“喜欢X的人也喜欢Y”,可以帮助用户发现有趣而有用的东西; A / B测试和用户流量分析可以帮助指出如何改进用户界面。这些功能需要一定量的用户行为跟踪,用户可以从中受益。
+ 跟踪行为数据对于许多在线服务的面向用户的功能变得越来越重要:跟踪点击哪些搜索结果有助于提高搜索结果的排名;推荐“喜欢X的人也喜欢Y”,可以帮助用户发现有趣而有用的东西; A / B测试和用户流量分析可以帮助指出如何改进用户界面。这些功能需要一定量的用户行为跟踪,用户可以从中受益。
-但是,根据公司的商业模式,追踪往往不止于此。如果服务是通过广告获得资金支持的,那么广告客户就是真正的客户,而用户的利益则位居第二。跟踪数据变得更加详细,分析变得更深入,数据保留很长时间,以便为营销目的建立每个人的详细资料。
+ 但是,根据公司的商业模式,追踪往往不止于此。如果服务是通过广告获得资金支持的,那么广告客户就是真正的客户,而用户的利益则位居第二。跟踪数据变得更加详细,分析变得更深入,数据保留很长时间,以便为营销目的建立每个人的详细资料。
-现在,公司和收集数据的用户之间的关系开始变得非常不同。用户可获得免费服务,并尽可能使用户接受该服务。对用户的追踪主要不是服务于该个人,而是服务于该服务的广告商的需求。我认为这种关系可以用一个更具罪犯内涵的词来恰当地描述:监视。
+ 现在,公司和收集数据的用户之间的关系开始变得非常不同。用户可获得免费服务,并尽可能使用户接受该服务。对用户的追踪主要不是服务于该个人,而是服务于该服务的广告商的需求。我认为这种关系可以用一个更具罪犯内涵的词来恰当地描述:监视。
#### 监控
-作为一个思想实验,尝试用监视来替换单词数据,并观察常用短语是否听起来如此好【93】。这样如何:“在我们的监控驱动的组织中,我们收集实时监控流并将它们存储在我们的监控仓库中。我们的监控科学家使用高级分析和监测处理来获得新的见解。“
+ 作为一个思想实验,尝试用监视来替换单词数据,并观察常用短语是否听起来如此好【93】。这样如何:“在我们的监控驱动的组织中,我们收集实时监控流并将它们存储在我们的监控仓库中。我们的监控科学家使用高级分析和监测处理来获得新的见解。“
-这个思想实验对于本书“设计监控 —— 密集型应用程序”来说是异乎寻常的争论,但我认为需要强调的话来强调这一点。在我们制作软件“吃世界”的尝试中【94】,我们已经建立了世界上迄今为止所见过的最伟大的大众监视基础设施。我们正朝着物联网迈进,我们正在迅速接近这样一个世界:每个有人居住的空间至少包含一个互联网连接的麦克风,以智能手机,智能电视,语音控制助理设备,婴儿监视器甚至儿童玩具使用基于云的语音识别。这些设备中的很多都有可怕的安全记录【95】。
+ 这个思想实验对于本书“设计监控 —— 密集型应用程序”来说是异乎寻常的争论,但我认为需要强调的话来强调这一点。在我们制作软件“吃世界”的尝试中【94】,我们已经建立了世界上迄今为止所见过的最伟大的大众监视基础设施。我们正朝着物联网迈进,我们正在迅速接近这样一个世界:每个有人居住的空间至少包含一个互联网连接的麦克风,以智能手机,智能电视,语音控制助理设备,婴儿监视器甚至儿童玩具使用基于云的语音识别。这些设备中的很多都有可怕的安全记录【95】。
-即使是极权主义和专制政权也只能梦想在每个房间放置一个麦克风,并强迫每个人不断地携带能够追踪其位置和动作的设备。然而,我们显然自愿地,甚至热心地投身于这个全面监视的世界。不同之处在于数据是由公司而不是由政府机构收集的【96】。
+ 即使是极权主义和专制政权也只能梦想在每个房间放置一个麦克风,并强迫每个人不断地携带能够追踪其位置和动作的设备。然而,我们显然自愿地,甚至热心地投身于这个全面监视的世界。不同之处在于数据是由公司而不是由政府机构收集的【96】。
-并不是所有的数据收集都必须符合监督的要求,但检查它们可以帮助我们理解我们与数据收集者的关系。为什么我们似乎很乐意接受企业的监督?也许你觉得你没有隐瞒 —— 换句话说,你完全符合现有的权力结构,你不是被边缘化的少数派,你不必害怕迫害【97】。不是每个人都如此幸运。也许这是因为目的似乎是良性的 —— 这不是强制性的,也不是强制性的,而只是更好的建议和更个性化的营销。但是,结合上一节中对预测分析的讨论,这种区分似乎不太清晰。
+ 并不是所有的数据收集都必须符合监督的要求,但检查它们可以帮助我们理解我们与数据收集者的关系。为什么我们似乎很乐意接受企业的监督?也许你觉得你没有隐瞒 —— 换句话说,你完全符合现有的权力结构,你不是被边缘化的少数派,你不必害怕迫害【97】。不是每个人都如此幸运。也许这是因为目的似乎是良性的 —— 这不是强制性的,也不是强制性的,而只是更好的建议和更个性化的营销。但是,结合上一节中对预测分析的讨论,这种区分似乎不太清晰。
-我们已经看到与汽车追踪设备挂钩的汽车保险费以及取决于佩戴健身追踪设备的人的健康保险范围。当监视被用来确定在生活的重要方面如保险或就业等方面的东西时,它开始变得不那么温和。此外,数据分析可以揭示出令人惊讶的侵入性事物:例如,智能手表或健身追踪器中的运动传感器可用于以相当好的准确度计算出您正在输入的内容(例如密码)【98】。而分析算法只会变得更好。
+ 我们已经看到与汽车追踪设备挂钩的汽车保险费以及取决于佩戴健身追踪设备的人的健康保险范围。当监视被用来确定在生活的重要方面如保险或就业等方面的东西时,它开始变得不那么温和。此外,数据分析可以揭示出令人惊讶的侵入性事物:例如,智能手表或健身追踪器中的运动传感器可用于以相当好的准确度计算出您正在输入的内容(例如密码)【98】。而分析算法只会变得更好。
#### 同意和选择的自由
-我们可能会断言用户自愿选择使用跟踪其活动的服务,并且他们已同意服务条款和隐私政策,因此他们同意收集数据。我们甚至可以声称,用户正在接受有价值的服务,以换取所提供的数据,并且为了提供服务,跟踪是必要的。毫无疑问,社交网络,搜索引擎以及其他各种免费的在线服务对于用户来说都是有价值的,但是这个说法存在问题。
+ 我们可能会断言用户自愿选择使用跟踪其活动的服务,并且他们已同意服务条款和隐私政策,因此他们同意收集数据。我们甚至可以声称,用户正在接受有价值的服务,以换取所提供的数据,并且为了提供服务,跟踪是必要的。毫无疑问,社交网络,搜索引擎以及其他各种免费的在线服务对于用户来说都是有价值的,但是这个说法存在问题。
-用户几乎不知道他们提供给我们的数据库的数据,或者它们如何保留和处理 —— 而大多数隐私政策的作用更多的是模糊而不是照亮。如果不了解他们的数据会发生什么,用户无法给出任何有意义的同意。通常,来自一个用户的数据还说明关于不是该服务的用户并且没有同意任何条款的其他人的事情。我们在本书的这一部分讨论的衍生数据集(来自整个用户群的数据可能与行为跟踪和外部数据源相结合)恰恰是用户无法获得任何有意义理解的数据种类。
-而且,数据是通过单向过程从用户中提取出来的,而不是真正的互惠关系,而不是公平的价值交换。没有对话,用户无法选择他们提供的数据量以及他们收到的服务回报:服务和用户之间的关系是非常不对称和片面的。这些条款是由服务设置,而不是由用户【99】。
+ 用户几乎不知道他们提供给我们的数据库的数据,或者它们如何保留和处理 —— 而大多数隐私政策的作用更多的是模糊而不是照亮。如果不了解他们的数据会发生什么,用户无法给出任何有意义的同意。通常,来自一个用户的数据还说明关于不是该服务的用户并且没有同意任何条款的其他人的事情。我们在本书的这一部分讨论的衍生数据集(来自整个用户群的数据可能与行为跟踪和外部数据源相结合)恰恰是用户无法获得任何有意义理解的数据种类。
+ 而且,数据是通过单向过程从用户中提取出来的,而不是真正的互惠关系,而不是公平的价值交换。没有对话,用户无法选择他们提供的数据量以及他们收到的服务回报:服务和用户之间的关系是非常不对称和片面的。这些条款是由服务设置,而不是由用户【99】。
-对于不同意监视的用户,唯一真正的选择就是不使用服务。但是这个选择也不是免费的:如果一项服务如此受欢迎以至于“被大多数人认为是基本社会参与的必要条件”【99】,那么指望人们选择退出这项服务是不合理的 —— 使用它事实上是强制性的。例如,在大多数西方社会群体中,携带智能手机,使用Facebook进行社交以及使用Google查找信息已成为常态。特别是当一项服务具有网络效应时,人们选择不使用它会产生社会成本。
+ 对于不同意监视的用户,唯一真正的选择就是不使用服务。但是这个选择也不是免费的:如果一项服务如此受欢迎以至于“被大多数人认为是基本社会参与的必要条件”【99】,那么指望人们选择退出这项服务是不合理的 —— 使用它事实上是强制性的。例如,在大多数西方社会群体中,携带智能手机,使用Facebook进行社交以及使用Google查找信息已成为常态。特别是当一项服务具有网络效应时,人们选择不使用它会产生社会成本。
-由于跟踪用户而拒绝使用服务,这只是少数拥有足够的时间和知识来了解其隐私政策的人员的一种选择,并且有可能错过社会参与或专业人士如果他们参与了服务,可能会出现机会。对于处境不太好的人来说,没有任何意义上的自由选择:监督变得不可避免。
+ 由于跟踪用户而拒绝使用服务,这只是少数拥有足够的时间和知识来了解其隐私政策的人员的一种选择,并且有可能错过社会参与或专业人士如果他们参与了服务,可能会出现机会。对于处境不太好的人来说,没有任何意义上的自由选择:监督变得不可避免。
#### 数据的隐私和使用
-有时候,人们声称“隐私已经死了”,理由是有些用户愿意把各种有关他们生活的事情发布到社交媒体上,有时是通常的,有时是个人的。但是,这种说法是错误的,并且依赖于对隐私一词的错误理解。
+ 有时候,人们声称“隐私已经死了”,理由是有些用户愿意把各种有关他们生活的事情发布到社交媒体上,有时是通常的,有时是个人的。但是,这种说法是错误的,并且依赖于对隐私一词的错误理解。
-拥有隐私并不意味着保密一切;它意味着要有自由选择要向谁揭示哪些事情,要公开什么,以及要保密什么。隐私权是一项决定权:它可以让每个人决定他们希望在每个场合保密和透明度之间的区域【99】。这是一个人的自由和自主的重要方面。
+ 拥有隐私并不意味着保密一切;它意味着要有自由选择要向谁揭示哪些事情,要公开什么,以及要保密什么。隐私权是一项决定权:它可以让每个人决定他们希望在每个场合保密和透明度之间的区域【99】。这是一个人的自由和自主的重要方面。
-当通过监控基础设施从人身上提取数据时,隐私权不一定被侵蚀,而是转移到数据收集器。获取数据的公司本质上是说“相信我们用你的数据做正确的事情”,这意味着决定要透露什么和保密的权利是从个人转移到公司的。
+ 当通过监控基础设施从人身上提取数据时,隐私权不一定被侵蚀,而是转移到数据收集器。获取数据的公司本质上是说“相信我们用你的数据做正确的事情”,这意味着决定要透露什么和保密的权利是从个人转移到公司的。
-这些公司反过来选择保留这个监视秘密的大部分结果,因为揭示它会被认为是令人毛骨悚然的,并且会损害他们的商业模式(这比其他公司更依赖于对人的了解)。关于用户的亲密信息只是间接显示的,例如以广告针对特定人群(如那些患有特定疾病的人群)的工具的形式。
+ 这些公司反过来选择保留这个监视秘密的大部分结果,因为揭示它会被认为是令人毛骨悚然的,并且会损害他们的商业模式(这比其他公司更依赖于对人的了解)。关于用户的亲密信息只是间接显示的,例如以广告针对特定人群(如那些患有特定疾病的人群)的工具的形式。
-即使特定用户无法从特定广告定位的人群中个人身份识别,他们已经失去了一些关于披露一些隐私信息的机构,例如他们是否患有某种疾病。决定根据个人喜好向谁透露什么的不是用户,而是公司以最大化利润为目标行使隐私权。
+ 即使特定用户无法从特定广告定位的人群中个人身份识别,他们已经失去了一些关于披露一些隐私信息的机构,例如他们是否患有某种疾病。决定根据个人喜好向谁透露什么的不是用户,而是公司以最大化利润为目标行使隐私权。
-许多公司都有一个不被视为令人毛骨悚然的目标 —— 避免了他们的数据收集的实际侵入性问题,而是专注于管理用户感知。即使这些看法经常被糟糕的管理:例如,事实可能是事实上的正确,但如果它触发痛苦的回忆,用户可能不希望被提醒它【100】。对于任何类型的数据,我们都应该期望在某种程度上出现错误,不可取或不适当的可能性,我们需要建立处理这些失败的机制。无论是“不可取的”还是“不适当的”,当然都是由人的判断决定的;除非我们明确地规划它们尊重人类的需求,否则算法会忽略这些概念。作为这些系统的工程师,我们必须谦虚,接受和规划这些失败。
+ 许多公司都有一个不被视为令人毛骨悚然的目标 —— 避免了他们的数据收集的实际侵入性问题,而是专注于管理用户感知。即使这些看法经常被糟糕的管理:例如,事实可能是事实上的正确,但如果它触发痛苦的回忆,用户可能不希望被提醒它【100】。对于任何类型的数据,我们都应该期望在某种程度上出现错误,不可取或不适当的可能性,我们需要建立处理这些失败的机制。无论是“不可取的”还是“不适当的”,当然都是由人的判断决定的;除非我们明确地规划它们尊重人类的需求,否则算法会忽略这些概念。作为这些系统的工程师,我们必须谦虚,接受和规划这些失败。
-允许在线服务的用户控制其他用户可以看到的数据的哪些方面的隐私设置是将一些控制交还给用户的起点。但是,无论设置如何,服务本身仍然可以不受限制地访问数据,并且可以以隐私策略允许的任何方式自由使用它。即使服务承诺不会将数据出售给第三方,它通常会授予自己不受限制的权利,以在内部处理和分析数据,而且往往比用户明显看到的要多得多。
+ 允许在线服务的用户控制其他用户可以看到的数据的哪些方面的隐私设置是将一些控制交还给用户的起点。但是,无论设置如何,服务本身仍然可以不受限制地访问数据,并且可以以隐私策略允许的任何方式自由使用它。即使服务承诺不会将数据出售给第三方,它通常会授予自己不受限制的权利,以在内部处理和分析数据,而且往往比用户明显看到的要多得多。
-这种从个人到公司的大规模隐私权转移历史上是史无前例的【99】。监控一直存在,但它过去是昂贵和手动的,不可扩展和自动化。信托关系一直存在,例如患者与其医生之间,或被告与其律师之间 —— 但在这些情况下,数据的使用严格受到道德,法律和监管限制的约束。互联网服务使得在没有有意义的同意的情况下收集大量敏感信息变得容易得多,并且在没有用户理解他们的私人数据正在发生的情况下大规模使用它。
+ 这种从个人到公司的大规模隐私权转移历史上是史无前例的【99】。监控一直存在,但它过去是昂贵和手动的,不可扩展和自动化。信托关系一直存在,例如患者与其医生之间,或被告与其律师之间 —— 但在这些情况下,数据的使用严格受到道德,法律和监管限制的约束。互联网服务使得在没有有意义的同意的情况下收集大量敏感信息变得容易得多,并且在没有用户理解他们的私人数据正在发生的情况下大规模使用它。
#### 数据作为资产和权力
-由于行为数据是用户与服务交互的副产品,因此有时称为“数据耗尽” —— 表明数据是毫无价值的浪费材料。从这个角度来看,行为和预测分析可以被看作是一种从数据中提取价值的回收形式,否则这些数据会被抛弃。
+ 由于行为数据是用户与服务交互的副产品,因此有时称为“数据耗尽” —— 表明数据是毫无价值的浪费材料。从这个角度来看,行为和预测分析可以被看作是一种从数据中提取价值的回收形式,否则这些数据会被抛弃。
-更准确的说是反过来看:从经济的角度来看,如果有针对性的广告是为服务付费的话,那么关于人的行为数据就是服务的核心资产。在这种情况下,用户与之交互的应用程序仅仅是一种诱骗用户将更多的个人信息提供给监控基础设施的手段【99】。数据提取机器人讥讽地发现,在线服务中常常表现出令人愉快的人类创造力和社交关系。
+ 更准确的说是反过来看:从经济的角度来看,如果有针对性的广告是为服务付费的话,那么关于人的行为数据就是服务的核心资产。在这种情况下,用户与之交互的应用程序仅仅是一种诱骗用户将更多的个人信息提供给监控基础设施的手段【99】。数据提取机器人讥讽地发现,在线服务中常常表现出令人愉快的人类创造力和社交关系。
-个人数据是宝贵资产的说法得到了数据中介的支持,这个数据中介是一个隐蔽的行业,它主要是为了市场目的而采购,收集,分析,推断和转售侵入性个人数据【90】。初创公司通过他们的用户数量,通过“眼球”,即通过他们的监视能力来估价。
+ 个人数据是宝贵资产的说法得到了数据中介的支持,这个数据中介是一个隐蔽的行业,它主要是为了市场目的而排序,收集,分析,推断和转售侵入性个人数据【90】。初创公司通过他们的用户数量,通过“眼球”,即通过他们的监视能力来估价。
-因为数据很有价值,所以很多人都想要它。当然,公司需要它 —— 这就是为什么他们收集它的原因。但政府也想获得它:通过秘密交易,胁迫,法律强制或者只是偷窃【101】。当公司破产时,收集到的个人资料就是被出售的资产之一。而且,数据难以确保,因此违规事件经常令人不安地发生【102】。
+ 因为数据很有价值,所以很多人都想要它。当然,公司需要它 —— 这就是为什么他们收集它的原因。但政府也想获得它:通过秘密交易,胁迫,法律强制或者只是偷窃【101】。当公司破产时,收集到的个人资料就是被出售的资产之一。而且,数据难以确保,因此违规事件经常令人不安地发生【102】。
-这些观察已经导致批评者说数据不仅仅是一种资产,而是一种“有毒资产”【101】,或者至少是“有害物质”【103】。即使我们认为我们有能力防止滥用数据,但是每当我们收集数据时,我们都需要平衡这些好处和落入他们手中的风险:计算机系统可能会被犯罪分子或敌对的外国情报服务,数据可能会被内部人士泄露,公司可能会落入不合法的管理层之中,而这些管理层不会分享我们的价值观,或者这个国家可能会被毫无疑问迫使我们交出数据的政权所接管。
+ 这些观察已经导致批评者说数据不仅仅是一种资产,而是一种“有毒资产”【101】,或者至少是“有害物质”【103】。即使我们认为我们有能力防止滥用数据,但是每当我们收集数据时,我们都需要平衡这些好处和落入他们手中的风险:计算机系统可能会被犯罪分子或敌对的外国情报服务,数据可能会被内部人士泄露,公司可能会落入不合法的管理层之中,而这些管理层不会分享我们的价值观,或者这个国家可能会被毫无疑问迫使我们交出数据的政权所接管。
-正如古老的格言所言,“知识就是力量”。此外,“在避免审查自己的同时审视他人是最重要的权力形式之一”【105】。这就是为什么极权政府希望监督:这让他们有能力控制人口。尽管今天的科技公司并没有公开地寻求政治权力,但是他们积累的数据和知识却给他们带来了很多权力,其中大部分是在公共监督之外偷偷摸摸的【106】。
+ 正如古老的格言所言,“知识就是力量”。此外,“在避免审查自己的同时审视他人是最重要的权力形式之一”【105】。这就是为什么极权政府希望监督:这让他们有能力控制人口。尽管今天的科技公司并没有公开地寻求政治权力,但是他们积累的数据和知识却给他们带来了很多权力,其中大部分是在公共监督之外偷偷摸摸的【106】。
#### 记住工业革命
-数据是信息时代的决定性特征。互联网,数据存储,处理和软件驱动的自动化正在对全球经济和人类社会产生重大影响。随着我们的日常生活和社会组织在过去的十年中发生了变化,并且在未来的十年中可能会继续发生根本性的变化,所以我们就会想到与工业革命的比较【87,96】。
+ 数据是信息时代的决定性特征。互联网,数据存储,处理和软件驱动的自动化正在对全球经济和人类社会产生重大影响。随着我们的日常生活和社会组织在过去的十年中发生了变化,并且在未来的十年中可能会继续发生根本性的变化,所以我们就会想到与工业革命的比较【87,96】。
-工业革命是通过重大的技术和农业进步来实现的,它带来了持续的经济增长,长期的生活水平显着提高。然而,它也带来了一些重大问题:空气污染(由于烟雾和化学过程)以及水(工业和人类的废物)造成的污染是可怕的。工厂老板生活在辉煌中,而城市工人经常住在非常贫困的住房里,并且在恶劣的条件下长时间工作。童工是常见的,包括在矿场工作的危险和低薪。
+ 工业革命是通过重大的技术和农业进步来实现的,它带来了持续的经济增长,长期的生活水平显着提高。然而,它也带来了一些重大问题:空气污染(由于烟雾和化学过程)以及水(工业和人类的废物)造成的污染是可怕的。工厂老板生活在辉煌中,而城市工人经常住在非常贫困的住房里,并且在恶劣的条件下长时间工作。童工是常见的,包括在矿场工作的危险和低薪。
-花了很长时间才制定了保护措施,例如环境保护条例,工作场所安全协议,禁止使用童工和食品卫生检查。毫无疑问,生产成本增加了,因为事实再也不能把废物倒入河流,销售污染的食物或者剥削工人。但是,整个社会都从中受益匪浅,我们中很少人会想要回到这些规定之前的时间【87】。
+ 花了很长时间才制定了保护措施,例如环境保护条例,工作场所安全协议,禁止使用童工和食品卫生检查。毫无疑问,生产成本增加了,因为事实再也不能把废物倒入河流,销售污染的食物或者剥削工人。但是,整个社会都从中受益匪浅,我们中很少人会想要回到这些规定之前的时间【87】。
-就像工业革命有一个黑暗的一面需要管理一样,我们对信息时代的转变也有我们需要面对和解决的重大问题。我相信收集和使用数据是其中的一个问题。用布鲁斯·施奈尔(Bruce Schneier)【96】的话来说:
+ 就像工业革命有一个黑暗的一面需要管理一样,我们对信息时代的转变也有我们需要面对和解决的重大问题。我相信收集和使用数据是其中的一个问题。用布鲁斯·施奈尔(Bruce Schneier)【96】的话来说:
> 数据是信息时代的污染问题,保护隐私是环境挑战。几乎所有的电脑都能生成信息。它停留在周围,溃烂。我们如何处理它 —— 我们如何控制它以及如何处理它 —— 对我们信息经济的健康至关重要。正如我们今天回顾工业时代的早期十年,并想知道我们的祖先在匆忙建立一个工业世界的过程中如何忽略污染,我们的孙辈在信息时代的前几十年将回顾我们,就我们如何应对数据收集和滥用的挑战来判断我们。
>
@@ -852,453 +853,277 @@ ACID意义上的一致性(请参阅第224页的“[一致性]()”)基于数
#### 立法和自律
-数据保护法可能有助于维护个人的权利。例如,1995年的“欧洲数据保护指令”规定,个人数据必须“为特定的,明确的和合法的目的收集,而不是以与这些目的不相符的方式进一步处理”,并且数据必须“充分,与收集目的相关并不过分“【107】。
+ 数据保护法可能有助于维护个人的权利。例如,1995年的“欧洲数据保护指令”规定,个人数据必须“为特定的,明确的和合法的目的收集,而不是以与这些目的不相符的方式进一步处理”,并且数据必须“充分,与收集目的相关并不过分“【107】。
-但是,这个立法在今天的互联网环境下是否有效还是有疑问的【108】。这些规则直接违背了大数据的理念,即最大限度地收集数据,将其与其他数据集结合起来进行试验和探索,以便产生新的见解。探索意味着将数据用于未预期的目的,这与用户同意的“特定和明确”目的相反(如果我们可以有意义地谈论同意的话)【109】。目前正在制定更新的规定【89】。
+ 但是,这个立法在今天的互联网环境下是否有效还是有疑问的【108】。这些规则直接违背了大数据的理念,即最大限度地收集数据,将其与其他数据集结合起来进行试验和探索,以便产生新的见解。探索意味着将数据用于未预期的目的,这与用户同意的“特定和明确”目的相反(如果我们可以有意义地谈论同意的话)【109】。目前正在制定更新的规定【89】。
-收集大量有关人员数据的公司反对监管,认为这是创新的负担和阻碍。在某种程度上,这种反对是有道理的。例如,分享医疗数据时,隐私存在明显风险,但也有潜在机会:如果数据分析能够帮助我们实现更好的诊断或找到更好的治疗方法,可以预防多少人死亡【110】?过度监管可能会阻止这种突破。这种潜在机会与风险之间难以平衡【105】。
+ 收集大量有关人员数据的公司反对监管,认为这是创新的负担和阻碍。在某种程度上,这种反对是有道理的。例如,分享医疗数据时,隐私存在明显风险,但也有潜在机会:如果数据分析能够帮助我们实现更好的诊断或找到更好的治疗方法,可以预防多少人死亡【110】?过度监管可能会阻止这种突破。这种潜在机会与风险之间难以平衡【105】。
-从根本上说,我认为我们需要在个人数据方面进行科技行业的文化转变。我们应该停止将用户作为度量标准进行优化,并记住他们是值得尊重,尊严和代理的人。我们应该自我调节我们的数据收集和处理实践,以建立和维持依赖我们软件的人们的信任【111】。我们应该自己去教育最终用户如何使用他们的数据,而不是让他们处于黑暗中。
+ 从根本上说,我认为我们需要在个人数据方面进行科技行业的文化转变。我们应该停止将用户作为度量标准进行优化,并记住他们是值得尊重,尊严和代理的人。我们应该自我调节我们的数据收集和处理实践,以建立和维持依赖我们软件的人们的信任【111】。我们应该自己去教育最终用户如何使用他们的数据,而不是让他们处于黑暗中。
-我们应该允许每个人保持他们的隐私 —— 即他们控制自己的数据 —— 而不是通过监视来窃取他们的控制权。我们控制数据的个人权利就像是一个国家公园的自然环境:如果我们没有明确的保护和关心,它将被破坏。这将是公地的悲剧,我们都会因此而变得更糟。无所不在的监视不是不可避免的,我们仍然能够阻止它。
+ 我们应该允许每个人保持他们的隐私 —— 即他们控制自己的数据 —— 而不是通过监视来窃取他们的控制权。我们控制数据的个人权利就像是一个国家公园的自然环境:如果我们没有明确的保护和关心,它将被破坏。这将是公地的悲剧,我们都会因此而变得更糟。无所不在的监视不是不可避免的,我们仍然能够阻止它。
-我们究竟能做到这一点是一个悬而未决的问题。首先,我们不应该永久保留数据,但一旦不再需要就立即清除数据【111,112】。清除数据与不变性的想法背道而驰(请参阅第463页的“不变性的限制”),但可以解决该问题。我所看到的一种很有前途的方法是通过加密协议来实施访问控制,而不仅仅是通过策略【113,114】。总的来说,文化和态度的变化是必要的。
+ 我们究竟能做到这一点是一个悬而未决的问题。首先,我们不应该永久保留数据,但一旦不再需要就立即清除数据【111,112】。清除数据与不变性的想法背道而驰(请参阅第463页的“不变性的限制”),但可以解决该问题。我所看到的一种很有前途的方法是通过加密协议来实施访问控制,而不仅仅是通过策略【113,114】。总的来说,文化和态度的变化是必要的。
## 本章小结
-在本章中,我们讨论了设计数据系统的新方法,并且包括了我对未来的个人意见和猜测。我们从观察开始,即没有一种工具可以有效地服务于所有可能的用例,因此应用程序必须编写几个不同的软件才能实现其目标。我们讨论了如何使用批处理和事件流来解决这个数据集成问题,以便让数据变化在不同系统之间流动。
+ 在本章中,我们讨论了设计数据系统的新方法,并且包括了我对未来的个人意见和猜测。我们从观察开始,即没有一种工具可以有效地服务于所有可能的用例,因此应用程序必须编写几个不同的软件才能实现其目标。我们讨论了如何使用批处理和事件流来解决这个数据集成问题,以便让数据变化在不同系统之间流动。
-在这种方法中,某些系统被指定为记录系统,而其他数据则通过转换从中得出。通过这种方式,我们可以维护索引,物化视图,机器学习模型,统计摘要等等。通过使这些衍生和转换异步和松散耦合,防止一个区域中的问题扩散到系统的不相关部分,从而增加整个系统的稳健性和容错性。
+ 在这种方法中,某些系统被指定为记录系统,而其他数据则通过转换从中得出。通过这种方式,我们可以维护索引,物化视图,机器学习模型,统计摘要等等。通过使这些衍生和转换异步和松散耦合,防止一个区域中的问题扩散到系统的不相关部分,从而增加整个系统的稳健性和容错性。
-将数据流表示为从一个数据集到另一个数据集的转换也有助于演化应用程序:如果您想更改其中一个处理步骤,例如更改索引或缓存的结构,则可以在整个输入数据集上重新运行新的转换代码以便重新输出。同样,如果出现问题,您可以修复代码并重新处理数据以便恢复。
+ 将数据流表示为从一个数据集到另一个数据集的转换也有助于演化应用程序:如果您想变更其中一个处理步骤,例如变更索引或缓存的结构,则可以在整个输入数据集上重新运行新的转换代码以便重新输出。同样,如果出现问题,您可以修复代码并重新处理数据以便恢复。
-这些过程与内部数据库已经完成的过程非常相似,因此我们重新构思了数据流应用程序的概念,将数据库的组件分开,并通过组合这些松散耦合的组件来构建应用程序。
+ 这些过程与内部数据库已经完成的过程非常相似,因此我们重新构思了数据流应用程序的概念,将数据库的组件分开,并通过组合这些松散耦合的组件来构建应用程序。
-衍生状态可以通过观察底层数据的变化来更新。此外,下游消费者可以进一步观察衍生状态本身。我们甚至可以将此数据流一直传送到显示数据的最终用户设备,从而构建可动态更新以反映数据更改并继续脱机工作的用户界面。
-接下来,我们讨论了如何确保所有这些处理在出现故障时保持正确。我们看到强大的完整性保证可以通过异步事件处理可视化地实现,通过使用端到端操作标识符使操作幂等并异步检查约束。客户可以等到检查通过,或者不用等待就行,但是可能会有违反约束的道歉风险。这种方法比使用分布式事务的传统方法更具可扩展性和可靠性,并且适合于实践中有多少业务流程工作。
+ 衍生状态可以通过观察底层数据的变化来更新。此外,下游消费者可以进一步观察衍生状态本身。我们甚至可以将此数据流一直传送到显示数据的最终用户设备,从而构建可动态更新以反映数据变更并继续脱机工作的用户界面。
+ 接下来,我们讨论了如何确保所有这些处理在出现故障时保持正确。我们看到强大的完整性保证可以通过异步事件处理可视化地实现,通过使用端到端操作标识符使操作幂等并异步检查约束。客户可以等到检查通过,或者不用等待就行,但是可能会有违反约束的道歉风险。这种方法比使用分布式事务的传统方法更具可扩展性和可靠性,并且适合于实践中有多少业务流程工作。
-通过构建围绕数据流的应用程序并同步检查约束条件,我们可以避免大多数协调,并创建维护完整性但仍能很好地运行的系统,即使在地理分布的情况下和出现故障时也是如此。然后,我们谈了一些关于使用审计来验证数据的完整性并检测腐败的问题。
+ 通过构建围绕数据流的应用程序并同步检查约束条件,我们可以避免大多数协调,并创建维护完整性但仍能很好地运行的系统,即使在地理分布的情况下和出现故障时也是如此。然后,我们谈了一些关于使用审计来验证数据的完整性并检测腐败的问题。
-最后,我们退后一步,审查了构建数据密集型应用程序的一些道德问题。我们看到,虽然数据可以用来做好事,但它也可能造成重大损害:作出严重影响人们生活并难以申诉的决定的正当理由,导致歧视和剥削,规范监督以及揭露私密信息。我们也冒着数据泄露的风险,并且我们可能会发现善意使用数据会产生意想不到的后果。
+ 最后,我们退后一步,审查了构建数据密集型应用程序的一些道德问题。我们看到,虽然数据可以用来做好事,但它也可能造成重大损害:作出严重影响人们生活并难以申诉的决定的正当理由,导致歧视和剥削,规范监督以及揭露私密信息。我们也冒着数据泄露的风险,并且我们可能会发现善意使用数据会产生意想不到的后果。
-由于软件和数据对世界产生如此巨大的影响,我们的工程师们必须记住,我们有责任为我们想要生活的这个世界努力工作:一个对待人性与尊重的世界。我希望我们能够一起为实现这一目标而努力。
+ 由于软件和数据对世界产生如此巨大的影响,我们的工程师们必须记住,我们有责任为我们想要生活的这个世界努力工作:一个对待人性与尊重的世界。我希望我们能够一起为实现这一目标而努力。
## 参考文献
-1. Rachid Belaid:
- “[Postgres Full-Text Search is Good Enough!](http://rachbelaid.com/postgres-full-text-search-is-good-enough/),” *rachbelaid.com*, July 13, 2015.
+1. Rachid Belaid: “[Postgres Full-Text Search is Good Enough!](http://rachbelaid.com/postgres-full-text-search-is-good-enough/),” *rachbelaid.com*, July 13, 2015.
-1. Philippe Ajoux, Nathan Bronson, Sanjeev Kumar, et al.:
- “[Challenges to Adopting Stronger Consistency at Scale](https://www.usenix.org/system/files/conference/hotos15/hotos15-paper-ajoux.pdf),” at *15th USENIX Workshop on Hot Topics in Operating Systems* (HotOS), May 2015.
+1. Philippe Ajoux, Nathan Bronson, Sanjeev Kumar, et al.: “[Challenges to Adopting Stronger Consistency at Scale](https://www.usenix.org/system/files/conference/hotos15/hotos15-paper-ajoux.pdf),” at *15th USENIX Workshop on Hot Topics in Operating Systems* (HotOS), May 2015.
-1. Pat Helland and Dave Campbell:
- “[Building on Quicksand](https://database.cs.wisc.edu/cidr/cidr2009/Paper_133.pdf),” at
- *4th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2009.
+1. Pat Helland and Dave Campbell: “[Building on Quicksand](https://database.cs.wisc.edu/cidr/cidr2009/Paper_133.pdf),” at *4th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2009.
-1. Jessica Kerr:
- “[Provenance and Causality in Distributed Systems](http://blog.jessitron.com/2016/09/provenance-and-causality-in-distributed.html),” *blog.jessitron.com*, September 25, 2016.
+1. Jessica Kerr: “[Provenance and Causality in Distributed Systems](http://blog.jessitron.com/2016/09/provenance-and-causality-in-distributed.html),” *blog.jessitron.com*, September 25, 2016.
-1. Kostas Tzoumas:
- “[Batch Is a Special Case of Streaming](http://data-artisans.com/batch-is-a-special-case-of-streaming/),” *data-artisans.com*, September 15, 2015.
+1. Kostas Tzoumas: “[Batch Is a Special Case of Streaming](http://data-artisans.com/batch-is-a-special-case-of-streaming/),” *data-artisans.com*, September 15, 2015.
-1. Shinji Kim and Robert Blafford:
- “[Stream Windowing Performance Analysis: Concord and Spark Streaming](http://concord.io/posts/windowing_performance_analysis_w_spark_streaming),” *concord.io*, July 6, 2016.
+1. Shinji Kim and Robert Blafford: “[Stream Windowing Performance Analysis: Concord and Spark Streaming](http://concord.io/posts/windowing_performance_analysis_w_spark_streaming),” *concord.io*, July 6, 2016.
-1. Jay Kreps:
- “[The Log: What Every Software Engineer Should Know About Real-Time Data's Unifying Abstraction](http://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying),”
- *engineering.linkedin.com*, December 16, 2013.
+1. Jay Kreps: “[The Log: What Every Software Engineer Should Know About Real-Time Data's Unifying Abstraction](http://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying),” *engineering.linkedin.com*, December 16, 2013.
-1. Pat Helland:
- “[Life Beyond Distributed Transactions: An Apostate’s Opinion](http://www-db.cs.wisc.edu/cidr/cidr2007/papers/cidr07p15.pdf),” at *3rd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2007.
+1. Pat Helland: “[Life Beyond Distributed Transactions: An Apostate’s Opinion](http://www-db.cs.wisc.edu/cidr/cidr2007/papers/cidr07p15.pdf),” at *3rd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2007.
1. “[Great Western Railway (1835–1948)](https://www.networkrail.co.uk/VirtualArchive/great-western/),” Network Rail Virtual Archive, *networkrail.co.uk*.
-1. Jacqueline Xu:
- “[Online Migrations at Scale](https://stripe.com/blog/online-migrations),”
- *stripe.com*, February 2, 2017.
+1. Jacqueline Xu: “[Online Migrations at Scale](https://stripe.com/blog/online-migrations),” *stripe.com*, February 2, 2017.
-1. Molly Bartlett Dishman and Martin Fowler:
- “[Agile Architecture](http://conferences.oreilly.com/software-architecture/sa2015/public/schedule/detail/40388),” at *O'Reilly Software Architecture Conference*, March 2015.
+1. Molly Bartlett Dishman and Martin Fowler: “[Agile Architecture](http://conferences.oreilly.com/software-architecture/sa2015/public/schedule/detail/40388),” at *O'Reilly Software Architecture Conference*, March 2015.
-1. Nathan Marz and James Warren:
- *Big Data: Principles and Best Practices of Scalable Real-Time Data Systems*. Manning, 2015. ISBN: 978-1-617-29034-3
+1. Nathan Marz and James Warren: *Big Data: Principles and Best Practices of Scalable Real-Time Data Systems*. Manning, 2015. ISBN: 978-1-617-29034-3
-1. Oscar Boykin, Sam Ritchie, Ian O'Connell, and
- Jimmy Lin: “[Summingbird: A Framework for Integrating Batch and Online MapReduce Computations](http://www.vldb.org/pvldb/vol7/p1441-boykin.pdf),” at *40th International Conference on Very Large Data Bases* (VLDB), September 2014.
+1. Oscar Boykin, Sam Ritchie, Ian O'Connell, and Jimmy Lin: “[Summingbird: A Framework for Integrating Batch and Online MapReduce Computations](http://www.vldb.org/pvldb/vol7/p1441-boykin.pdf),” at *40th International Conference on Very Large Data Bases* (VLDB), September 2014.
-1. Jay Kreps:
- “[Questioning the Lambda Architecture](https://www.oreilly.com/ideas/questioning-the-lambda-architecture),” *oreilly.com*, July 2, 2014.
+1. Jay Kreps: “[Questioning the Lambda Architecture](https://www.oreilly.com/ideas/questioning-the-lambda-architecture),” *oreilly.com*, July 2, 2014.
-1. Raul Castro Fernandez, Peter Pietzuch,
- Jay Kreps, et al.: “[Liquid: Unifying Nearline and Offline Big Data Integration](http://www.cidrdb.org/cidr2015/Papers/CIDR15_Paper25u.pdf),” at *7th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2015.
+1. Raul Castro Fernandez, Peter Pietzuch, Jay Kreps, et al.: “[Liquid: Unifying Nearline and Offline Big Data Integration](http://www.cidrdb.org/cidr2015/Papers/CIDR15_Paper25u.pdf),” at *7th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2015.
-1. Dennis M. Ritchie and Ken Thompson:
- “[The UNIX Time-Sharing System](http://www.cs.virginia.edu/~zaher/classes/CS656/p365-ritchie.pdf),” *Communications of the ACM*, volume 17, number 7, pages 365–375, July 1974.
- [doi:10.1145/361011.361061](http://dx.doi.org/10.1145/361011.361061)
+1. Dennis M. Ritchie and Ken Thompson: “[The UNIX Time-Sharing System](http://www.cs.virginia.edu/~zaher/classes/CS656/p365-ritchie.pdf),” *Communications of the ACM*, volume 17, number 7, pages 365–375, July 1974. [doi:10.1145/361011.361061](http://dx.doi.org/10.1145/361011.361061)
-1. Eric A. Brewer and Joseph M. Hellerstein:
- “[CS262a: Advanced Topics in Computer Systems](http://people.eecs.berkeley.edu/~brewer/cs262/systemr.html),” lecture notes, University of California, Berkeley, *cs.berkeley.edu*,
- August 2011.
+1. Eric A. Brewer and Joseph M. Hellerstein: “[CS262a: Advanced Topics in Computer Systems](http://people.eecs.berkeley.edu/~brewer/cs262/systemr.html),” lecture notes, University of California, Berkeley, *cs.berkeley.edu*, August 2011.
-1. Michael Stonebraker:
- “[The Case for Polystores](http://wp.sigmod.org/?p=1629),” *wp.sigmod.org*,
- July 13, 2015.
+1. Michael Stonebraker: “[The Case for Polystores](http://wp.sigmod.org/?p=1629),” *wp.sigmod.org*, July 13, 2015.
-1. Jennie Duggan,
- Aaron J. Elmore, Michael Stonebraker, et al.:
- “[The BigDAWG Polystore System](http://dspace.mit.edu/openaccess-disseminate/1721.1/100936),” *ACM SIGMOD Record*, volume 44, number 2, pages 11–16, June 2015.
- [doi:10.1145/2814710.2814713](http://dx.doi.org/10.1145/2814710.2814713)
+1. Jennie Duggan, Aaron J. Elmore, Michael Stonebraker, et al.: “[The BigDAWG Polystore System](http://dspace.mit.edu/openaccess-disseminate/1721.1/100936),” *ACM SIGMOD Record*, volume 44, number 2, pages 11–16, June 2015. [doi:10.1145/2814710.2814713](http://dx.doi.org/10.1145/2814710.2814713)
-1. Patrycja Dybka:
- “[Foreign Data Wrappers for PostgreSQL](http://www.vertabelo.com/blog/technical-articles/foreign-data-wrappers-for-postgresql),” *vertabelo.com*, March 24, 2015.
+1. Patrycja Dybka: “[Foreign Data Wrappers for PostgreSQL](http://www.vertabelo.com/blog/technical-articles/foreign-data-wrappers-for-postgresql),” *vertabelo.com*, March 24, 2015.
-1. David B. Lomet, Alan Fekete, Gerhard Weikum, and Mike Zwilling:
- “[Unbundling Transaction Services in the Cloud](https://www.microsoft.com/en-us/research/publication/unbundling-transaction-services-in-the-cloud/),” at *4th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2009.
+1. David B. Lomet, Alan Fekete, Gerhard Weikum, and Mike Zwilling: “[Unbundling Transaction Services in the Cloud](https://www.microsoft.com/en-us/research/publication/unbundling-transaction-services-in-the-cloud/),” at *4th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2009.
-1. Martin Kleppmann and Jay Kreps:
- “[Kafka, Samza and the Unix Philosophy of Distributed Data](http://martin.kleppmann.com/papers/kafka-debull15.pdf),” *IEEE Data Engineering Bulletin*, volume 38, number 4, pages 4–14,
- December 2015.
+1. Martin Kleppmann and Jay Kreps: “[Kafka, Samza and the Unix Philosophy of Distributed Data](http://martin.kleppmann.com/papers/kafka-debull15.pdf),” *IEEE Data Engineering Bulletin*, volume 38, number 4, pages 4–14, December 2015.
-1. John Hugg:
- “[Winning Now and in the Future: Where VoltDB Shines](https://voltdb.com/blog/winning-now-and-future-where-voltdb-shines),” *voltdb.com*, March 23, 2016.
+1. John Hugg: “[Winning Now and in the Future: Where VoltDB Shines](https://voltdb.com/blog/winning-now-and-future-where-voltdb-shines),” *voltdb.com*, March 23, 2016.
-1. Frank McSherry, Derek G. Murray, Rebecca Isaacs, and Michael Isard:
- “[Differential Dataflow](http://cidrdb.org/cidr2013/Papers/CIDR13_Paper111.pdf),”
+1. Frank McSherry, Derek G. Murray, Rebecca Isaacs, and Michael Isard: “[Differential Dataflow](http://cidrdb.org/cidr2013/Papers/CIDR13_Paper111.pdf),”
at *6th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2013.
-1. Derek G Murray, Frank McSherry, Rebecca Isaacs, et al.:
- “[Naiad: A Timely Dataflow System](http://research.microsoft.com/pubs/201100/naiad_sosp2013.pdf),”
+1. Derek G Murray, Frank McSherry, Rebecca Isaacs, et al.: “[Naiad: A Timely Dataflow System](http://research.microsoft.com/pubs/201100/naiad_sosp2013.pdf),”
at *24th ACM Symposium on Operating Systems Principles* (SOSP), pages 439–455, November 2013.
[doi:10.1145/2517349.2522738](http://dx.doi.org/10.1145/2517349.2522738)
-1. Gwen Shapira:
- “[We have a bunch of customers who are implementing ‘database inside-out’ concept and they all ask ‘is anyone else doing it? are we crazy?’](https://twitter.com/gwenshap/status/758800071110430720)” *twitter.com*, July 28, 2016.
+1. Gwen Shapira: “[We have a bunch of customers who are implementing ‘database inside-out’ concept and they all ask ‘is anyone else doing it? are we crazy?’](https://twitter.com/gwenshap/status/758800071110430720)” *twitter.com*, July 28, 2016.
-1. Martin Kleppmann:
- “[Turning the Database Inside-out with Apache Samza,](http://martin.kleppmann.com/2015/03/04/turning-the-database-inside-out.html)” at *Strange Loop*, September 2014.
+1. Martin Kleppmann: “[Turning the Database Inside-out with Apache Samza,](http://martin.kleppmann.com/2015/03/04/turning-the-database-inside-out.html)” at *Strange Loop*, September 2014.
-1. Peter Van Roy and Seif Haridi:
- *Concepts, Techniques, and Models of Computer Programming*. MIT Press, 2004.
- ISBN: 978-0-262-22069-9
+1. Peter Van Roy and Seif Haridi: *Concepts, Techniques, and Models of Computer Programming*. MIT Press, 2004. ISBN: 978-0-262-22069-9
1. “[Juttle Documentation](http://juttle.github.io/juttle/),” *juttle.github.io*, 2016.
-1. Evan Czaplicki and Stephen Chong:
- “[Asynchronous Functional Reactive Programming for GUIs](http://people.seas.harvard.edu/~chong/pubs/pldi13-elm.pdf),” at *34th ACM SIGPLAN Conference on Programming Language
- Design and Implementation* (PLDI), June 2013.
- [doi:10.1145/2491956.2462161](http://dx.doi.org/10.1145/2491956.2462161)
+1. Evan Czaplicki and Stephen Chong: “[Asynchronous Functional Reactive Programming for GUIs](http://people.seas.harvard.edu/~chong/pubs/pldi13-elm.pdf),” at *34th ACM SIGPLAN Conference on Programming Language Design and Implementation* (PLDI), June 2013. [doi:10.1145/2491956.2462161](http://dx.doi.org/10.1145/2491956.2462161)
-1. Engineer Bainomugisha, Andoni Lombide Carreton,
- Tom van Cutsem, Stijn Mostinckx, and Wolfgang de Meuter:
- “[A Survey on Reactive Programming](http://soft.vub.ac.be/Publications/2012/vub-soft-tr-12-13.pdf),” *ACM Computing Surveys*, volume 45, number 4, pages 1–34, August 2013.
- [doi:10.1145/2501654.2501666](http://dx.doi.org/10.1145/2501654.2501666)
+1. Engineer Bainomugisha, Andoni Lombide Carreton, Tom van Cutsem, Stijn Mostinckx, and Wolfgang de Meuter: “[A Survey on Reactive Programming](http://soft.vub.ac.be/Publications/2012/vub-soft-tr-12-13.pdf),” *ACM Computing Surveys*, volume 45, number 4, pages 1–34, August 2013. [doi:10.1145/2501654.2501666](http://dx.doi.org/10.1145/2501654.2501666)
-1. Peter
- Alvaro, Neil Conway, Joseph M. Hellerstein, and William R. Marczak:
- “[Consistency Analysis in Bloom: A CALM and Collected Approach](http://www.eecs.berkeley.edu/~palvaro/cidr11.pdf),” at *5th Biennial Conference on Innovative Data Systems Research*
+1. Peter Alvaro, Neil Conway, Joseph M. Hellerstein, and William R. Marczak: “[Consistency Analysis in Bloom: A CALM and Collected Approach](http://www.eecs.berkeley.edu/~palvaro/cidr11.pdf),” at *5th Biennial Conference on Innovative Data Systems Research*
(CIDR), January 2011.
-1. Felienne Hermans:
- “[Spreadsheets Are Code](https://vimeo.com/145492419),” at *Code Mesh*, November
- 2015.
+1. Felienne Hermans: “[Spreadsheets Are Code](https://vimeo.com/145492419),” at *Code Mesh*, November 2015.
-1. Dan Bricklin and Bob
- Frankston: “[VisiCalc: Information from Its Creators](http://danbricklin.com/visicalc.htm),” *danbricklin.com*.
+1. Dan Bricklin and Bob Frankston: “[VisiCalc: Information from Its Creators](http://danbricklin.com/visicalc.htm),” *danbricklin.com*.
-1. D. Sculley, Gary Holt, Daniel Golovin, et al.:
- “[Machine Learning: The High-Interest Credit Card of Technical Debt](http://research.google.com/pubs/pub43146.html),” at *NIPS Workshop on Software Engineering for Machine Learning*
- (SE4ML), December 2014.
+1. D. Sculley, Gary Holt, Daniel Golovin, et al.: “[Machine Learning: The High-Interest Credit Card of Technical Debt](http://research.google.com/pubs/pub43146.html),” at *NIPS Workshop on Software Engineering for Machine Learning* (SE4ML), December 2014.
-1. Peter Bailis, Alan Fekete, Michael J Franklin,
- et al.: “[Feral Concurrency Control: An Empirical Investigation of Modern Application Integrity](http://www.bailis.org/papers/feral-sigmod2015.pdf),” at *ACM International Conference on
- Management of Data* (SIGMOD), June 2015.
- [doi:10.1145/2723372.2737784](http://dx.doi.org/10.1145/2723372.2737784)
+1. Peter Bailis, Alan Fekete, Michael J Franklin, et al.: “[Feral Concurrency Control: An Empirical Investigation of Modern Application Integrity](http://www.bailis.org/papers/feral-sigmod2015.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2015. [doi:10.1145/2723372.2737784](http://dx.doi.org/10.1145/2723372.2737784)
-1. Guy Steele:
- “[Re: Need for Macros (Was Re: Icon)](https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg01134.html),” email to *ll1-discuss* mailing list, *people.csail.mit.edu*, December 24,
- 2001.
+1. Guy Steele: “[Re: Need for Macros (Was Re: Icon)](https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg01134.html),” email to *ll1-discuss* mailing list, *people.csail.mit.edu*, December 24, 2001.
-1. David Gelernter:
- “[Generative Communication in Linda](http://cseweb.ucsd.edu/groups/csag/html/teaching/cse291s03/Readings/p80-gelernter.pdf),” *ACM Transactions on Programming Languages and Systems*
- (TOPLAS), volume 7, number 1, pages 80–112, January 1985.
- [doi:10.1145/2363.2433](http://dx.doi.org/10.1145/2363.2433)
+1. David Gelernter: “[Generative Communication in Linda](http://cseweb.ucsd.edu/groups/csag/html/teaching/cse291s03/Readings/p80-gelernter.pdf),” *ACM Transactions on Programming Languages and Systems* (TOPLAS), volume 7, number 1, pages 80–112, January 1985. [doi:10.1145/2363.2433](http://dx.doi.org/10.1145/2363.2433)
-1. Patrick Th. Eugster, Pascal A. Felber,
- Rachid Guerraoui, and Anne-Marie Kermarrec:
- “[The Many Faces of Publish/Subscribe](http://www.cs.ru.nl/~pieter/oss/manyfaces.pdf),”
- *ACM Computing Surveys*, volume 35, number 2, pages 114–131, June 2003.
- [doi:10.1145/857076.857078](http://dx.doi.org/10.1145/857076.857078)
+1. Patrick Th. Eugster, Pascal A. Felber, Rachid Guerraoui, and Anne-Marie Kermarrec: “[The Many Faces of Publish/Subscribe](http://www.cs.ru.nl/~pieter/oss/manyfaces.pdf),” *ACM Computing Surveys*, volume 35, number 2, pages 114–131, June 2003. [doi:10.1145/857076.857078](http://dx.doi.org/10.1145/857076.857078)
-1. Ben Stopford:
- “[Microservices in a Streaming World](https://www.infoq.com/presentations/microservices-streaming),” at *QCon London*, March 2016.
+1. Ben Stopford: “[Microservices in a Streaming World](https://www.infoq.com/presentations/microservices-streaming),” at *QCon London*, March 2016.
-1. Christian Posta:
- “[Why Microservices Should Be Event Driven: Autonomy vs Authority](http://blog.christianposta.com/microservices/why-microservices-should-be-event-driven-autonomy-vs-authority/),” *blog.christianposta.com*, May 27, 2016.
+1. Christian Posta: “[Why Microservices Should Be Event Driven: Autonomy vs Authority](http://blog.christianposta.com/microservices/why-microservices-should-be-event-driven-autonomy-vs-authority/),” *blog.christianposta.com*, May 27, 2016.
-1. Alex Feyerke:
- “[Say Hello to Offline First](http://hood.ie/blog/say-hello-to-offline-first.html),”
- *hood.ie*, November 5, 2013.
+1. Alex Feyerke: “[Say Hello to Offline First](http://hood.ie/blog/say-hello-to-offline-first.html),” *hood.ie*, November 5, 2013.
-1. Sebastian Burckhardt, Daan Leijen, Jonathan
- Protzenko, and Manuel Fähndrich:
- “[Global Sequence Protocol: A Robust Abstraction for Replicated Shared State](http://drops.dagstuhl.de/opus/volltexte/2015/5238/),” at *29th European Conference on Object-Oriented Programming* (ECOOP), July 2015.
- [doi:10.4230/LIPIcs.ECOOP.2015.568](http://dx.doi.org/10.4230/LIPIcs.ECOOP.2015.568)
+1. Sebastian Burckhardt, Daan Leijen, Jonathan Protzenko, and Manuel Fähndrich: “[Global Sequence Protocol: A Robust Abstraction for Replicated Shared State](http://drops.dagstuhl.de/opus/volltexte/2015/5238/),” at *29th European Conference on Object-Oriented Programming* (ECOOP), July 2015. [doi:10.4230/LIPIcs.ECOOP.2015.568](http://dx.doi.org/10.4230/LIPIcs.ECOOP.2015.568)
-1. Mark Soper:
- “[Clearing Up React Data Management Confusion with Flux, Redux, and Relay](https://medium.com/@marksoper/clearing-up-react-data-management-confusion-with-flux-redux-and-relay-aad504e63cae),” *medium.com*, December 3, 2015.
+1. Mark Soper: “[Clearing Up React Data Management Confusion with Flux, Redux, and Relay](https://medium.com/@marksoper/clearing-up-react-data-management-confusion-with-flux-redux-and-relay-aad504e63cae),” *medium.com*, December 3, 2015.
-1. Eno Thereska, Damian Guy, Michael Noll, and Neha Narkhede:
- “[Unifying Stream Processing and Interactive Queries in Apache Kafka](http://www.confluent.io/blog/unifying-stream-processing-and-interactive-queries-in-apache-kafka/),” *confluent.io*, October 26, 2016.
+1. Eno Thereska, Damian Guy, Michael Noll, and Neha Narkhede: “[Unifying Stream Processing and Interactive Queries in Apache Kafka](http://www.confluent.io/blog/unifying-stream-processing-and-interactive-queries-in-apache-kafka/),” *confluent.io*, October 26, 2016.
-1. Frank McSherry:
- “[Dataflow as Database](https://github.com/frankmcsherry/blog/blob/master/posts/2016-07-17.md),” *github.com*, July 17, 2016.
+1. Frank McSherry: “[Dataflow as Database](https://github.com/frankmcsherry/blog/blob/master/posts/2016-07-17.md),” *github.com*, July 17, 2016.
-1. Peter Alvaro:
- “[I See What You Mean](https://www.youtube.com/watch?v=R2Aa4PivG0g),” at *Strange Loop*, September 2015.
+1. Peter Alvaro: “[I See What You Mean](https://www.youtube.com/watch?v=R2Aa4PivG0g),” at *Strange Loop*, September 2015.
-1. Nathan Marz:
- “[Trident: A High-Level Abstraction for Realtime Computation](https://blog.twitter.com/2012/trident-a-high-level-abstraction-for-realtime-computation),” *blog.twitter.com*, August 2, 2012.
+1. Nathan Marz: “[Trident: A High-Level Abstraction for Realtime Computation](https://blog.twitter.com/2012/trident-a-high-level-abstraction-for-realtime-computation),” *blog.twitter.com*, August 2, 2012.
-1. Edi Bice:
- “[Low Latency Web Scale Fraud Prevention with Apache Samza, Kafka and Friends](http://www.slideshare.net/edibice/extremely-low-latency-web-scale-fraud-prevention-with-apache-samza-kafka-and-friends),” at *Merchant Risk
- Council MRC Vegas Conference*, March 2016.
+1. Edi Bice: “[Low Latency Web Scale Fraud Prevention with Apache Samza, Kafka and Friends](http://www.slideshare.net/edibice/extremely-low-latency-web-scale-fraud-prevention-with-apache-samza-kafka-and-friends),” at *Merchant Risk Council MRC Vegas Conference*, March 2016.
-1. Charity Majors:
- “[The Accidental DBA](https://charity.wtf/2016/10/02/the-accidental-dba/),” *charity.wtf*,
- October 2, 2016.
+1. Charity Majors: “[The Accidental DBA](https://charity.wtf/2016/10/02/the-accidental-dba/),” *charity.wtf*, October 2, 2016.
-1. Arthur J. Bernstein, Philip M. Lewis, and Shiyong Lu:
- “[Semantic Conditions for Correctness at Different Isolation Levels](http://db.cs.berkeley.edu/cs286/papers/isolation-icde2000.pdf),” at *16th International Conference on Data Engineering* (ICDE), February 2000.
- [doi:10.1109/ICDE.2000.839387](http://dx.doi.org/10.1109/ICDE.2000.839387)
+1. Arthur J. Bernstein, Philip M. Lewis, and Shiyong Lu: “[Semantic Conditions for Correctness at Different Isolation Levels](http://db.cs.berkeley.edu/cs286/papers/isolation-icde2000.pdf),” at *16th International Conference on Data Engineering* (ICDE), February 2000. [doi:10.1109/ICDE.2000.839387](http://dx.doi.org/10.1109/ICDE.2000.839387)
-1. Sudhir Jorwekar, Alan Fekete, Krithi Ramamritham, and
- S. Sudarshan: “[Automating the Detection of Snapshot Isolation Anomalies](http://www.vldb.org/conf/2007/papers/industrial/p1263-jorwekar.pdf),” at *33rd International Conference on Very Large Data Bases* (VLDB), September 2007.
+1. Sudhir Jorwekar, Alan Fekete, Krithi Ramamritham, and S. Sudarshan: “[Automating the Detection of Snapshot Isolation Anomalies](http://www.vldb.org/conf/2007/papers/industrial/p1263-jorwekar.pdf),” at *33rd International Conference on Very Large Data Bases* (VLDB), September 2007.
-1. Kyle Kingsbury:
- [Jepsen blog post series](https://aphyr.com/tags/jepsen), *aphyr.com*, 2013–2016.
+1. Kyle Kingsbury: [Jepsen blog post series](https://aphyr.com/tags/jepsen), *aphyr.com*, 2013–2016.
-1. Michael Jouravlev:
- “[Redirect After Post](http://www.theserverside.com/news/1365146/Redirect-After-Post),”
- *theserverside.com*, August 1, 2004.
+1. Michael Jouravlev: “[Redirect After Post](http://www.theserverside.com/news/1365146/Redirect-After-Post),” *theserverside.com*, August 1, 2004.
-1. Jerome H. Saltzer, David P. Reed, and
- David D. Clark: “[End-to-End Arguments in System Design](http://www.ece.drexel.edu/courses/ECE-C631-501/SalRee1984.pdf),” *ACM Transactions on Computer Systems*, volume 2, number 4,
- pages 277–288, November 1984.
- [doi:10.1145/357401.357402](http://dx.doi.org/10.1145/357401.357402)
+1. Jerome H. Saltzer, David P. Reed, and David D. Clark: “[End-to-End Arguments in System Design](http://www.ece.drexel.edu/courses/ECE-C631-501/SalRee1984.pdf),” *ACM Transactions on Computer Systems*, volume 2, number 4, pages 277–288, November 1984. [doi:10.1145/357401.357402](http://dx.doi.org/10.1145/357401.357402)
-1. Peter Bailis, Alan Fekete, Michael J. Franklin, et al.:
- “[Coordination-Avoiding Database Systems](http://arxiv.org/pdf/1402.2237.pdf),”
+1. Peter Bailis, Alan Fekete, Michael J. Franklin, et al.: “[Coordination-Avoiding Database Systems](http://arxiv.org/pdf/1402.2237.pdf),”
*Proceedings of the VLDB Endowment*, volume 8, number 3, pages 185–196, November 2014.
-1. Alex Yarmula:
- “[Strong Consistency in Manhattan](https://blog.twitter.com/2016/strong-consistency-in-manhattan),” *blog.twitter.com*, March 17, 2016.
+1. Alex Yarmula: “[Strong Consistency in Manhattan](https://blog.twitter.com/2016/strong-consistency-in-manhattan),” *blog.twitter.com*, March 17, 2016.
-1. Douglas B Terry, Marvin M Theimer, Karin Petersen, et al.:
- “[Managing Update Conflicts in Bayou, a Weakly Connected Replicated Storage System](http://css.csail.mit.edu/6.824/2014/papers/bayou-conflicts.pdf),” at *15th ACM Symposium on Operating
- Systems Principles* (SOSP), pages 172–182, December 1995.
- [doi:10.1145/224056.224070](http://dx.doi.org/10.1145/224056.224070)
+1. Douglas B Terry, Marvin M Theimer, Karin Petersen, et al.: “[Managing Update Conflicts in Bayou, a Weakly Connected Replicated Storage System](http://css.csail.mit.edu/6.824/2014/papers/bayou-conflicts.pdf),” at *15th ACM Symposium on Operating Systems Principles* (SOSP), pages 172–182, December 1995. [doi:10.1145/224056.224070](http://dx.doi.org/10.1145/224056.224070)
-1. Jim Gray:
- “[The Transaction Concept: Virtues and Limitations](http://research.microsoft.com/en-us/um/people/gray/papers/theTransactionConcept.pdf),” at *7th International Conference on
- Very Large Data Bases* (VLDB), September 1981.
+1. Jim Gray: “[The Transaction Concept: Virtues and Limitations](http://research.microsoft.com/en-us/um/people/gray/papers/theTransactionConcept.pdf),” at *7th International Conference on Very Large Data Bases* (VLDB), September 1981.
-1. Hector Garcia-Molina and Kenneth Salem:
- “[Sagas](http://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf),” at
- *ACM International Conference on Management of Data* (SIGMOD), May 1987.
- [doi:10.1145/38713.38742](http://dx.doi.org/10.1145/38713.38742)
+1. Hector Garcia-Molina and Kenneth Salem: “[Sagas](http://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), May 1987. [doi:10.1145/38713.38742](http://dx.doi.org/10.1145/38713.38742)
-1. Pat Helland:
- “[Memories, Guesses, and Apologies](http://blogs.msdn.com/b/pathelland/archive/2007/05/15/memories-guesses-and-apologies.aspx),” *blogs.msdn.com*, May 15, 2007.
+1. Pat Helland: “[Memories, Guesses, and Apologies](http://blogs.msdn.com/b/pathelland/archive/2007/05/15/memories-guesses-and-apologies.aspx),” *blogs.msdn.com*, May 15, 2007.
-1. Yoongu Kim, Ross Daly, Jeremie Kim, et al.:
- “[Flipping Bits in Memory Without Accessing Them: An Experimental Study of DRAM Disturbance Errors](https://users.ece.cmu.edu/~yoonguk/papers/kim-isca14.pdf),” at *41st Annual International Symposium on Computer Architecture* (ISCA), June 2014.
- [doi:10.1145/2678373.2665726](http://dx.doi.org/10.1145/2678373.2665726)
+1. Yoongu Kim, Ross Daly, Jeremie Kim, et al.: “[Flipping Bits in Memory Without Accessing Them: An Experimental Study of DRAM Disturbance Errors](https://users.ece.cmu.edu/~yoonguk/papers/kim-isca14.pdf),” at *41st Annual International Symposium on Computer Architecture* (ISCA), June 2014. [doi:10.1145/2678373.2665726](http://dx.doi.org/10.1145/2678373.2665726)
-1. Mark Seaborn and Thomas Dullien:
- “[Exploiting the DRAM Rowhammer Bug to Gain Kernel Privileges](https://googleprojectzero.blogspot.co.uk/2015/03/exploiting-dram-rowhammer-bug-to-gain.html),” *googleprojectzero.blogspot.co.uk*, March 9,
- 2015.
+1. Mark Seaborn and Thomas Dullien: “[Exploiting the DRAM Rowhammer Bug to Gain Kernel Privileges](https://googleprojectzero.blogspot.co.uk/2015/03/exploiting-dram-rowhammer-bug-to-gain.html),” *googleprojectzero.blogspot.co.uk*, March 9, 2015.
-1. Jim N. Gray and Catharine van Ingen:
- “[Empirical Measurements of Disk Failure Rates and Error Rates](https://www.microsoft.com/en-us/research/publication/empirical-measurements-of-disk-failure-rates-and-error-rates/),” Microsoft Research, MSR-TR-2005-166,
- December 2005.
+1. Jim N. Gray and Catharine van Ingen: “[Empirical Measurements of Disk Failure Rates and Error Rates](https://www.microsoft.com/en-us/research/publication/empirical-measurements-of-disk-failure-rates-and-error-rates/),” Microsoft Research, MSR-TR-2005-166, December 2005.
-1. Annamalai Gurusami and Daniel Price:
- “[Bug #73170: Duplicates in Unique Secondary Index Because of Fix of Bug#68021](http://bugs.mysql.com/bug.php?id=73170),” *bugs.mysql.com*, July 2014.
+1. Annamalai Gurusami and Daniel Price: “[Bug #73170: Duplicates in Unique Secondary Index Because of Fix of Bug#68021](http://bugs.mysql.com/bug.php?id=73170),” *bugs.mysql.com*, July 2014.
-1. Gary Fredericks:
- “[Postgres Serializability Bug](https://github.com/gfredericks/pg-serializability-bug),”
- *github.com*, September 2015.
+1. Gary Fredericks: “[Postgres Serializability Bug](https://github.com/gfredericks/pg-serializability-bug),” *github.com*, September 2015.
-1. Xiao Chen:
- “[HDFS DataNode Scanners and Disk Checker Explained](http://blog.cloudera.com/blog/2016/12/hdfs-datanode-scanners-and-disk-checker-explained/),” *blog.cloudera.com*, December 20,
+1. Xiao Chen: “[HDFS DataNode Scanners and Disk Checker Explained](http://blog.cloudera.com/blog/2016/12/hdfs-datanode-scanners-and-disk-checker-explained/),” *blog.cloudera.com*, December 20,
2016.
-1. Jay Kreps:
- “[Getting Real About Distributed System Reliability](http://blog.empathybox.com/post/19574936361/getting-real-about-distributed-system-reliability),” *blog.empathybox.com*, March 19, 2012.
+1. Jay Kreps: “[Getting Real About Distributed System Reliability](http://blog.empathybox.com/post/19574936361/getting-real-about-distributed-system-reliability),” *blog.empathybox.com*, March 19, 2012.
-1. Martin Fowler:
- “[The LMAX Architecture](http://martinfowler.com/articles/lmax.html),”
- *martinfowler.com*, July 12, 2011.
+1. Martin Fowler: “[The LMAX Architecture](http://martinfowler.com/articles/lmax.html),” *martinfowler.com*, July 12, 2011.
-1. Sam Stokes:
- “[Move Fast with Confidence](http://blog.samstokes.co.uk/blog/2016/07/11/move-fast-with-confidence/),” *blog.samstokes.co.uk*, July 11, 2016.
+1. Sam Stokes: “[Move Fast with Confidence](http://blog.samstokes.co.uk/blog/2016/07/11/move-fast-with-confidence/),” *blog.samstokes.co.uk*, July 11, 2016.
1. “[Sawtooth Lake Documentation](http://intelledger.github.io/introduction.html),” Intel Corporation, *intelledger.github.io*, 2016.
-1. Richard Gendal Brown:
- “[Introducing R3 Corda™: A Distributed Ledger Designed for Financial Services](https://gendal.me/2016/04/05/introducing-r3-corda-a-distributed-ledger-designed-for-financial-services/),” *gendal.me*, April 5, 2016.
+1. Richard Gendal Brown: “[Introducing R3 Corda™: A Distributed Ledger Designed for Financial Services](https://gendal.me/2016/04/05/introducing-r3-corda-a-distributed-ledger-designed-for-financial-services/),” *gendal.me*, April 5, 2016.
-1. Trent McConaghy, Rodolphe Marques, Andreas Müller, et al.:
- “[BigchainDB: A Scalable Blockchain Database](https://www.bigchaindb.com/whitepaper/bigchaindb-whitepaper.pdf),” *bigchaindb.com*, June 8, 2016.
+1. Trent McConaghy, Rodolphe Marques, Andreas Müller, et al.: “[BigchainDB: A Scalable Blockchain Database](https://www.bigchaindb.com/whitepaper/bigchaindb-whitepaper.pdf),” *bigchaindb.com*, June 8, 2016.
-1. Ralph C. Merkle:
- “[A Digital Signature Based on a Conventional Encryption Function](https://people.eecs.berkeley.edu/~raluca/cs261-f15/readings/merkle.pdf),” at *CRYPTO '87*, August 1987.
- [doi:10.1007/3-540-48184-2_32](http://dx.doi.org/10.1007/3-540-48184-2_32)
+1. Ralph C. Merkle: “[A Digital Signature Based on a Conventional Encryption Function](https://people.eecs.berkeley.edu/~raluca/cs261-f15/readings/merkle.pdf),” at *CRYPTO '87*, August 1987. [doi:10.1007/3-540-48184-2_32](http://dx.doi.org/10.1007/3-540-48184-2_32)
-1. Ben Laurie:
- “[Certificate Transparency](http://queue.acm.org/detail.cfm?id=2668154),” *ACM Queue*, volume 12, number 8, pages 10-19, August 2014.
- [doi:10.1145/2668152.2668154](http://dx.doi.org/10.1145/2668152.2668154)
+1. Ben Laurie: “[Certificate Transparency](http://queue.acm.org/detail.cfm?id=2668154),” *ACM Queue*, volume 12, number 8, pages 10-19, August 2014. [doi:10.1145/2668152.2668154](http://dx.doi.org/10.1145/2668152.2668154)
-1. Mark D. Ryan:
- “[Enhanced Certificate Transparency and End-to-End Encrypted Mail](http://www.internetsociety.org/doc/enhanced-certificate-transparency-and-end-end-encrypted-mail),” at *Network and Distributed System
- Security Symposium* (NDSS), February 2014.
- [doi:10.14722/ndss.2014.23379](http://dx.doi.org/10.14722/ndss.2014.23379)
+1. Mark D. Ryan: “[Enhanced Certificate Transparency and End-to-End Encrypted Mail](http://www.internetsociety.org/doc/enhanced-certificate-transparency-and-end-end-encrypted-mail),” at *Network and Distributed System Security Symposium* (NDSS), February 2014. [doi:10.14722/ndss.2014.23379](http://dx.doi.org/10.14722/ndss.2014.23379)
1. “Software Engineering Code of Ethics and Professional
Practice,” Association for Computing Machinery, *acm.org*, 1999.
-1. François Chollet:
- “[Software development is starting to involve important ethical choices](https://twitter.com/fchollet/status/792958695722201088),” *twitter.com*, October 30, 2016.
+1. François Chollet: “[Software development is starting to involve important ethical choices](https://twitter.com/fchollet/status/792958695722201088),” *twitter.com*, October 30, 2016.
-1. Igor Perisic:
- “[Making Hard Choices: The Quest for Ethics in Machine Learning](https://engineering.linkedin.com/blog/2016/11/making-hard-choices--the-quest-for-ethics-in-machine-learning),” *engineering.linkedin.com*, November
- 2016.
+1. Igor Perisic: “[Making Hard Choices: The Quest for Ethics in Machine Learning](https://engineering.linkedin.com/blog/2016/11/making-hard-choices--the-quest-for-ethics-in-machine-learning),” *engineering.linkedin.com*, November 2016.
-1. John Naughton:
- “[Algorithm Writers Need a Code of Conduct](https://www.theguardian.com/commentisfree/2015/dec/06/algorithm-writers-should-have-code-of-conduct),” *theguardian.com*, December 6, 2015.
+1. John Naughton: “[Algorithm Writers Need a Code of Conduct](https://www.theguardian.com/commentisfree/2015/dec/06/algorithm-writers-should-have-code-of-conduct),” *theguardian.com*, December 6, 2015.
-1. Logan Kugler:
- “[What Happens When Big Data Blunders?](http://cacm.acm.org/magazines/2016/6/202655-what-happens-when-big-data-blunders/fulltext),” *Communications of the ACM*, volume 59, number 6, pages
-15–16, June 2016. [doi:10.1145/2911975](http://dx.doi.org/10.1145/2911975)
+1. Logan Kugler: “[What Happens When Big Data Blunders?](http://cacm.acm.org/magazines/2016/6/202655-what-happens-when-big-data-blunders/fulltext),” *Communications of the ACM*, volume 59, number 6, pages 15–16, June 2016. [doi:10.1145/2911975](http://dx.doi.org/10.1145/2911975)
-1. Bill Davidow:
- “[Welcome to Algorithmic Prison](http://www.theatlantic.com/technology/archive/2014/02/welcome-to-algorithmic-prison/283985/),” *theatlantic.com*, February 20, 2014.
+1. Bill Davidow: “[Welcome to Algorithmic Prison](http://www.theatlantic.com/technology/archive/2014/02/welcome-to-algorithmic-prison/283985/),” *theatlantic.com*, February 20, 2014.
-1. Don Peck:
- “[They're Watching You at Work](http://www.theatlantic.com/magazine/archive/2013/12/theyre-watching-you-at-work/354681/),” *theatlantic.com*, December 2013.
+1. Don Peck: “[They're Watching You at Work](http://www.theatlantic.com/magazine/archive/2013/12/theyre-watching-you-at-work/354681/),” *theatlantic.com*, December 2013.
-1. Leigh Alexander:
- “[Is an Algorithm Any Less Racist Than a Human?](https://www.theguardian.com/technology/2016/aug/03/algorithm-racist-human-employers-work)” *theguardian.com*, August 3, 2016.
+1. Leigh Alexander: “[Is an Algorithm Any Less Racist Than a Human?](https://www.theguardian.com/technology/2016/aug/03/algorithm-racist-human-employers-work)” *theguardian.com*, August 3, 2016.
-1. Jesse Emspak:
- “[How a Machine Learns Prejudice](https://www.scientificamerican.com/article/how-a-machine-learns-prejudice/),” *scientificamerican.com*, December 29, 2016.
+1. Jesse Emspak: “[How a Machine Learns Prejudice](https://www.scientificamerican.com/article/how-a-machine-learns-prejudice/),” *scientificamerican.com*, December 29, 2016.
-1. Maciej Cegłowski:
- “[The Moral Economy of Tech](http://idlewords.com/talks/sase_panel.htm),”
- *idlewords.com*, June 2016.
+1. Maciej Cegłowski: “[The Moral Economy of Tech](http://idlewords.com/talks/sase_panel.htm),” *idlewords.com*, June 2016.
-1. Cathy O'Neil:
- *Weapons of Math Destruction: How Big Data Increases Inequality and Threatens Democracy*. Crown Publishing, 2016.
- ISBN: 978-0-553-41881-1
+1. Cathy O'Neil: *Weapons of Math Destruction: How Big Data Increases Inequality and Threatens Democracy*. Crown Publishing, 2016. ISBN: 978-0-553-41881-1
-1. Julia Angwin:
- “[Make Algorithms Accountable](http://www.nytimes.com/2016/08/01/opinion/make-algorithms-accountable.html),” *nytimes.com*, August 1, 2016.
+1. Julia Angwin: “[Make Algorithms Accountable](http://www.nytimes.com/2016/08/01/opinion/make-algorithms-accountable.html),” *nytimes.com*, August 1, 2016.
-1. Bryce Goodman and Seth Flaxman:
- “[European Union Regulations on Algorithmic Decision-Making and a ‘Right to Explanation’](https://arxiv.org/abs/1606.08813),” *arXiv:1606.08813*, August 31,
- 2016.
+1. Bryce Goodman and Seth Flaxman: “[European Union Regulations on Algorithmic Decision-Making and a ‘Right to Explanation’](https://arxiv.org/abs/1606.08813),” *arXiv:1606.08813*, August 31, 2016.
1. “[A Review of the Data Broker Industry: Collection, Use, and Sale of Consumer Data for Marketing Purposes](https://www.commerce.senate.gov/public/index.cfm/reports?ID=57C428EC-8F20-44EE-BFB8-A570E9BE0CCC),” Staff Report, *United States Senate Committee on Commerce, Science, and Transportation*, *commerce.senate.gov*, December 2013.
-1. Olivia Solon:
- “[Facebook’s Failure: Did Fake News and Polarized Politics Get Trump Elected?](https://www.theguardian.com/technology/2016/nov/10/facebook-fake-news-election-conspiracy-theories)” *theguardian.com*, November 10,
- 2016.
+1. Olivia Solon: “[Facebook’s Failure: Did Fake News and Polarized Politics Get Trump Elected?](https://www.theguardian.com/technology/2016/nov/10/facebook-fake-news-election-conspiracy-theories)” *theguardian.com*, November 10, 2016.
-1. Donella H. Meadows and Diana Wright:
- *Thinking in Systems: A Primer*. Chelsea Green Publishing, 2008. ISBN: 978-1-603-58055-7
+1. Donella H. Meadows and Diana Wright: *Thinking in Systems: A Primer*. Chelsea Green Publishing, 2008. ISBN: 978-1-603-58055-7
-1. Daniel J. Bernstein:
- “[Listening to a ‘big data’/‘data science’ talk](https://twitter.com/hashbreaker/status/598076230437568512),” *twitter.com*, May 12, 2015.
+1. Daniel J. Bernstein: “[Listening to a ‘big data’/‘data science’ talk](https://twitter.com/hashbreaker/status/598076230437568512),” *twitter.com*, May 12, 2015.
-1. Marc Andreessen:
- “[Why Software Is Eating the World](http://genius.com/Marc-andreessen-why-software-is-eating-the-world-annotated),” *The Wall Street Journal*, 20 August 2011.
+1. Marc Andreessen: “[Why Software Is Eating the World](http://genius.com/Marc-andreessen-why-software-is-eating-the-world-annotated),” *The Wall Street Journal*, 20 August 2011.
-1. J. M. Porup:
- “[‘Internet of Things’ Security Is Hilariously Broken and Getting Worse](http://arstechnica.com/security/2016/01/how-to-search-the-internet-of-things-for-photos-of-sleeping-babies/),” *arstechnica.com*, January 23, 2016.
+1. J. M. Porup: “[‘Internet of Things’ Security Is Hilariously Broken and Getting Worse](http://arstechnica.com/security/2016/01/how-to-search-the-internet-of-things-for-photos-of-sleeping-babies/),” *arstechnica.com*, January 23, 2016.
-1. Bruce Schneier:
- *Data and Goliath: The Hidden Battles to Collect Your Data and Control Your World*. W. W. Norton, 2015.
- ISBN: 978-0-393-35217-7
+1. Bruce Schneier: *Data and Goliath: The Hidden Battles to Collect Your Data and Control Your World*. W. W. Norton, 2015. ISBN: 978-0-393-35217-7
-1. The Grugq:
- “[Nothing to Hide](https://grugq.tumblr.com/post/142799983558/nothing-to-hide),”
- *grugq.tumblr.com*, April 15, 2016.
+1. The Grugq: “[Nothing to Hide](https://grugq.tumblr.com/post/142799983558/nothing-to-hide),” *grugq.tumblr.com*, April 15, 2016.
-1. Tony Beltramelli:
- “[Deep-Spying: Spying Using Smartwatch and Deep Learning](https://arxiv.org/abs/1512.05616),” Masters Thesis, IT University of Copenhagen, December 2015. Available at
- *arxiv.org/abs/1512.05616*
+1. Tony Beltramelli: “[Deep-Spying: Spying Using Smartwatch and Deep Learning](https://arxiv.org/abs/1512.05616),” Masters Thesis, IT University of Copenhagen, December 2015. Available at *arxiv.org/abs/1512.05616*
-1. Shoshana Zuboff:
- “[Big Other: Surveillance Capitalism and the Prospects of an Information Civilization](http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2594754),” *Journal of Information
- Technology*, volume 30, number 1, pages 75–89, April 2015.
- [doi:10.1057/jit.2015.5](http://dx.doi.org/10.1057/jit.2015.5)
+1. Shoshana Zuboff: “[Big Other: Surveillance Capitalism and the Prospects of an Information Civilization](http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2594754),” *Journal of Information Technology*, volume 30, number 1, pages 75–89, April 2015.[doi:10.1057/jit.2015.5](http://dx.doi.org/10.1057/jit.2015.5)
-1. Carina C. Zona:
- “[Consequences of an Insightful Algorithm](https://www.youtube.com/watch?v=YRI40A4tyWU),”
- at *GOTO Berlin*, November 2016.
+1. Carina C. Zona: “[Consequences of an Insightful Algorithm](https://www.youtube.com/watch?v=YRI40A4tyWU),” at *GOTO Berlin*, November 2016.
-1. Bruce Schneier:
- “[Data Is a Toxic Asset, So Why Not Throw It Out?](https://www.schneier.com/essays/archives/2016/03/data_is_a_toxic_asse.html),” *schneier.com*, March 1, 2016.
+1. Bruce Schneier: “[Data Is a Toxic Asset, So Why Not Throw It Out?](https://www.schneier.com/essays/archives/2016/03/data_is_a_toxic_asse.html),” *schneier.com*, March 1, 2016.
-1. John E. Dunn:
- “[The UK’s 15 Most Infamous Data Breaches](http://www.techworld.com/security/uks-most-infamous-data-breaches-2016-3604586/),” *techworld.com*, November 18, 2016.
+1. John E. Dunn: “[The UK’s 15 Most Infamous Data Breaches](http://www.techworld.com/security/uks-most-infamous-data-breaches-2016-3604586/),” *techworld.com*, November 18, 2016.
-1. Cory Scott:
- “[Data is not toxic - which implies no benefit - but rather hazardous material, where we must balance need vs. want](https://twitter.com/cory_scott/status/706586399483437056),”
- *twitter.com*, March 6, 2016.
+1. Cory Scott: “[Data is not toxic - which implies no benefit - but rather hazardous material, where we must balance need vs. want](https://twitter.com/cory_scott/status/706586399483437056),” *twitter.com*, March 6, 2016.
-1. Bruce Schneier:
- “[Mission Creep: When Everything Is Terrorism](https://www.schneier.com/essays/archives/2013/07/mission_creep_when_e.html),” *schneier.com*, July 16, 2013.
+1. Bruce Schneier: “[Mission Creep: When Everything Is Terrorism](https://www.schneier.com/essays/archives/2013/07/mission_creep_when_e.html),” *schneier.com*, July 16, 2013.
-1. Lena Ulbricht and Maximilian von Grafenstein:
- “[Big Data: Big Power Shifts?](http://policyreview.info/articles/analysis/big-data-big-power-shifts),” *Internet Policy Review*, volume 5, number 1, March 2016.
- [doi:10.14763/2016.1.406](http://dx.doi.org/10.14763/2016.1.406)
+1. Lena Ulbricht and Maximilian von Grafenstein: “[Big Data: Big Power Shifts?](http://policyreview.info/articles/analysis/big-data-big-power-shifts),” *Internet Policy Review*, volume 5, number 1, March 2016. [doi:10.14763/2016.1.406](http://dx.doi.org/10.14763/2016.1.406)
-1. Ellen P. Goodman and Julia Powles:
- “[Facebook and Google: Most Powerful and Secretive Empires We've Ever Known](https://www.theguardian.com/technology/2016/sep/28/google-facebook-powerful-secretive-empire-transparency),” *theguardian.com*, September 28,
- 2016.
+1. Ellen P. Goodman and Julia Powles: “[Facebook and Google: Most Powerful and Secretive Empires We've Ever Known](https://www.theguardian.com/technology/2016/sep/28/google-facebook-powerful-secretive-empire-transparency),” *theguardian.com*, September 28, 2016.
1. [Directive 95/46/EC on the protection of individuals with regard to the processing of personal data and on the free movement of such data](http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:31995L0046), Official Journal of the European Communities No. L 281/31,
*eur-lex.europa.eu*, November 1995.
-1. Brendan Van Alsenoy:
- “[Regulating Data Protection: The Allocation of Responsibility and Risk Among Actors Involved in Personal Data Processing](https://lirias.kuleuven.be/handle/123456789/545027),”
- Thesis, KU Leuven Centre for IT and IP Law, August 2016.
+1. Brendan Van Alsenoy: “[Regulating Data Protection: The Allocation of Responsibility and Risk Among Actors Involved in Personal Data Processing](https://lirias.kuleuven.be/handle/123456789/545027),” Thesis, KU Leuven Centre for IT and IP Law, August 2016.
-1. Michiel Rhoen:
- “[Beyond Consent: Improving Data Protection Through Consumer Protection Law](http://policyreview.info/articles/analysis/beyond-consent-improving-data-protection-through-consumer-protection-law),” *Internet Policy Review*, volume 5, number 1, March 2016.
- [doi:10.14763/2016.1.404](http://dx.doi.org/10.14763/2016.1.404)
+1. Michiel Rhoen: “[Beyond Consent: Improving Data Protection Through Consumer Protection Law](http://policyreview.info/articles/analysis/beyond-consent-improving-data-protection-through-consumer-protection-law),” *Internet Policy Review*, volume 5, number 1, March 2016. [doi:10.14763/2016.1.404](http://dx.doi.org/10.14763/2016.1.404)
-1. Jessica Leber:
- “[Your Data Footprint Is Affecting Your Life in Ways You Can’t Even Imagine](https://www.fastcoexist.com/3057514/your-data-footprint-is-affecting-your-life-in-ways-you-cant-even-imagine),” *fastcoexist.com*, March 15,
- 2016.
+1. Jessica Leber: “[Your Data Footprint Is Affecting Your Life in Ways You Can’t Even Imagine](https://www.fastcoexist.com/3057514/your-data-footprint-is-affecting-your-life-in-ways-you-cant-even-imagine),” *fastcoexist.com*, March 15, 2016.
-1. Maciej Cegłowski:
- “[Haunted by Data](http://idlewords.com/talks/haunted_by_data.htm),” *idlewords.com*,
- October 2015.
+1. Maciej Cegłowski: “[Haunted by Data](http://idlewords.com/talks/haunted_by_data.htm),” *idlewords.com*, October 2015.
-1. Sam Thielman:
- “[You Are Not What You Read: Librarians Purge User Data to Protect Privacy](https://www.theguardian.com/us-news/2016/jan/13/us-library-records-purged-data-privacy),” *theguardian.com*,
- January 13, 2016.
+1. Sam Thielman: “[You Are Not What You Read: Librarians Purge User Data to Protect Privacy](https://www.theguardian.com/us-news/2016/jan/13/us-library-records-purged-data-privacy),” *theguardian.com*, January 13, 2016.
-1. Conor Friedersdorf:
- “[Edward Snowden’s Other Motive for Leaking](http://www.theatlantic.com/politics/archive/2014/05/edward-snowdens-other-motive-for-leaking/370068/),” *theatlantic.com*, May 13, 2014.
+1. Conor Friedersdorf: “[Edward Snowden’s Other Motive for Leaking](http://www.theatlantic.com/politics/archive/2014/05/edward-snowdens-other-motive-for-leaking/370068/),” *theatlantic.com*, May 13, 2014.
-1. Phillip Rogaway:
- “[The Moral Character of Cryptographic Work](http://web.cs.ucdavis.edu/~rogaway/papers/moral-fn.pdf),” Cryptology ePrint 2015/1162, December 2015.
+1. Phillip Rogaway: “[The Moral Character of Cryptographic Work](http://web.cs.ucdavis.edu/~rogaway/papers/moral-fn.pdf),” Cryptology ePrint 2015/1162, December 2015.
diff --git a/ch4.md b/ch4.md
index 1b6be85..3d77eae 100644
--- a/ch4.md
+++ b/ch4.md
@@ -372,7 +372,7 @@ Avro为静态类型编程语言提供了可选的代码生成功能,但是它
-### 服务中的数据流:REST 与 RPC
+### 服务中的数据流:REST与RPC
当您需要通过网络进行通信的进程时,安排该通信的方式有几种。最常见的安排是有两个角色:客户端和服务器。服务器通过网络公开API,并且客户端可以连接到服务器以向该API发出请求。服务器公开的API被称为服务。
diff --git a/ch7.md b/ch7.md
index 78993f0..321e30b 100644
--- a/ch7.md
+++ b/ch7.md
@@ -879,9 +879,9 @@ WHERE room_id = 123 AND
一个相当新的算法,避免了先前方法的大部分缺点。它使用乐观的方法,允许事务执行而无需阻塞。当一个事务想要提交时,它会进行检查,如果执行不可序列化,事务就会被中止。
-本章中的示例主要是在关系数据模型的上下文中。使用关系数据模型。但是,正如在讨论中,无论使用哪种数据模型,如“**[多对象事务的需求](#多对象事务的需求)**”中所讨论的,事务都是重要的数据库功能。
+ 本章中的示例主要是在关系数据模型的上下文中。使用关系数据模型。但是,正如在讨论中,无论使用哪种数据模型,如“**[多对象事务的需求](#多对象事务的需求)**”中所讨论的,事务都是重要的数据库功能。
-本章主要是在单机数据库的上下文中,探讨了各种概念与想法。分布式数据库中的事务,则引入了一系列新的困难挑战,将在接下来的两章中讨论。
+ 本章主要是在单机数据库的上下文中,探讨了各种概念与想法。分布式数据库中的事务,则引入了一系列新的困难挑战,将在接下来的两章中讨论。
@@ -902,16 +902,15 @@ WHERE room_id = 123 AND
10. Philip A. Bernstein, Vassos Hadzilacos, and Nathan Goodman: [*Concurrency Control and Recovery in Database Systems*](http://research.microsoft.com/en-us/people/philbe/ccontrol.aspx). Addison-Wesley, 1987. ISBN: 978-0-201-10715-9, available online at *research.microsoft.com*.
11. Alan Fekete, Dimitrios Liarokapis, Elizabeth O'Neil, et al.: “[Making Snapshot Isolation Serializable](https://www.cse.iitb.ac.in/infolab/Data/Courses/CS632/2009/Papers/p492-fekete.pdf),” *ACM Transactions on Database Systems*, volume 30, number 2, pages 492–528, June 2005.
[doi:10.1145/1071610.1071615](http://dx.doi.org/10.1145/1071610.1071615)
-12. Mai Zheng, Joseph Tucek, Feng Qin, and Mark Lillibridge: “[Understanding the Robustness of SSDs Under Power Fault](https://www.usenix.org/system/files/conference/fast13/fast13-final80.pdf),” at *11th USENIX Conference on File and Storage Technologies* (FAST), February 2013.
+12. Mai Zheng, Joseph Tucek, Feng Qin, and Mark Lillibridge: “[Understanding the Robustness of SSDs Under Power Fault](https://www.usenix.org/system/files/conference/fast13/fast13-final80.pdf),” at *11th USENIX Conference on File and Storage Technologies* (FAST), February 2013.
13. Laurie Denness: “[SSDs: A Gift and a Curse](https://laur.ie/blog/2015/06/ssds-a-gift-and-a-curse/),” *laur.ie*, June 2, 2015.
-14. Adam Surak: “[When Solid State Drives Are Not That Solid](https://blog.algolia.com/when-solid-state-drives-are-not-that-solid/),” *blog.algolia.com*, June 15, 2015.
+14. Adam Surak: “[When Solid State Drives Are Not That Solid](https://blog.algolia.com/when-solid-state-drives-are-not-that-solid/),” *blog.algolia.com*, June 15, 2015.
15. Thanumalayan Sankaranarayana Pillai, Vijay Chidambaram, Ramnatthan Alagappan, et al.: “[All File Systems Are Not Created Equal: On the Complexity of Crafting Crash-Consistent Applications](http://research.cs.wisc.edu/wind/Publications/alice-osdi14.pdf),” at *11th USENIX Symposium on Operating Systems Design and Implementation* (OSDI),
October 2014.
-16. Chris Siebenmann: “[Unix's File Durability Problem](https://utcc.utoronto.ca/~cks/space/blog/unix/FileSyncProblem),” *utcc.utoronto.ca*, April 14, 2016.
-17. Lakshmi N. Bairavasundaram, Garth R. Goodson, Bianca Schroeder, et al.: “[An Analysis of Data Corruption in the Storage Stack](http://research.cs.wisc.edu/adsl/Publications/corruption-fast08.pdf),” at *6th USENIX Conference on File and Storage
- Technologies* (FAST), February 2008.
+16. Chris Siebenmann: “[Unix's File Durability Problem](https://utcc.utoronto.ca/~cks/space/blog/unix/FileSyncProblem),” *utcc.utoronto.ca*, April 14, 2016.
+17. Lakshmi N. Bairavasundaram, Garth R. Goodson, Bianca Schroeder, et al.: “[An Analysis of Data Corruption in the Storage Stack](http://research.cs.wisc.edu/adsl/Publications/corruption-fast08.pdf),” at *6th USENIX Conference on File and Storage Technologies* (FAST), February 2008.
18. Bianca Schroeder, Raghav Lagisetty, and Arif Merchant: “[Flash Reliability in Production: The Expected and the Unexpected](https://www.usenix.org/conference/fast16/technical-sessions/presentation/schroeder),” at *14th USENIX Conference on File and Storage Technologies* (FAST), February 2016.
-19. Don Allison: “[SSD Storage – Ignorance of Technology Is No Excuse](https://blog.korelogic.com/blog/2015/03/24),” *blog.korelogic.com*, March 24, 2015.
+19. Don Allison: “[SSD Storage – Ignorance of Technology Is No Excuse](https://blog.korelogic.com/blog/2015/03/24),” *blog.korelogic.com*, March 24, 2015.
20. Dave Scherer: “[Those Are Not Transactions (Cassandra 2.0)](http://web.archive.org/web/20150526065247/http://blog.foundationdb.com/those-are-not-transactions-cassandra-2-0),” *blog.foundationdb.com*, September 6, 2013.
21. Kyle Kingsbury: “[Call Me Maybe: Cassandra](http://aphyr.com/posts/294-call-me-maybe-cassandra/),” *aphyr.com*, September 24, 2013.
22. “[ACID Support in Aerospike](http://www.aerospike.com/docs/architecture/assets/AerospikeACIDSupport.pdf),” Aerospike, Inc., June 2014.
@@ -935,9 +934,9 @@ WHERE room_id = 123 AND
38. Joel Jacobson: “[Riak 2.0: Data Types](http://blog.joeljacobson.com/riak-2-0-data-types/),” *blog.joeljacobson.com*, March 23, 2014.
39. Michael J. Cahill, Uwe Röhm, and Alan Fekete: “[Serializable Isolation for Snapshot Databases](http://www.cs.nyu.edu/courses/fall12/CSCI-GA.2434-001/p729-cahill.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2008. [doi:10.1145/1376616.1376690](http://dx.doi.org/10.1145/1376616.1376690)
40. Dan R. K. Ports and Kevin Grittner: “[Serializable Snapshot Isolation in PostgreSQL](http://drkp.net/papers/ssi-vldb12.pdf),” at *38th International Conference on Very Large Databases* (VLDB), August 2012.
-41. Tony Andrews: “[Enforcing Complex Constraints in Oracle](http://tonyandrews.blogspot.co.uk/2004/10/enforcing-complex-constraints-in.html),” *tonyandrews.blogspot.co.uk*, October 15, 2004.
+41. Tony Andrews: “[Enforcing Complex Constraints in Oracle](http://tonyandrews.blogspot.co.uk/2004/10/enforcing-complex-constraints-in.html),” *tonyandrews.blogspot.co.uk*, October 15, 2004.
42. Douglas B. Terry, Marvin M. Theimer, Karin Petersen, et al.: “[Managing Update Conflicts in Bayou, a Weakly Connected Replicated Storage System](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.141.7889&rep=rep1&type=pdf),” at *15th ACM Symposium on Operating Systems Principles* (SOSP), December 1995. [doi:10.1145/224056.224070](http://dx.doi.org/10.1145/224056.224070)
-43. Gary Fredericks: “[Postgres Serializability Bug](https://github.com/gfredericks/pg-serializability-bug),” *github.com*, September 2015.
+43. Gary Fredericks: “[Postgres Serializability Bug](https://github.com/gfredericks/pg-serializability-bug),” *github.com*, September 2015.
44. Michael Stonebraker, Samuel Madden, Daniel J. Abadi, et al.: “[The End of an Architectural Era (It’s Time for a Complete Rewrite)](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.137.3697&rep=rep1&type=pdf),” at *33rd International Conference on Very Large Data Bases* (VLDB), September 2007.
45. John Hugg: “[H-Store/VoltDB Architecture vs. CEP Systems and Newer Streaming Architectures](https://www.youtube.com/watch?v=hD5M4a1UVz8),” at *Data @Scale Boston*, November 2014.
46. Robert Kallman, Hideaki Kimura, Jonathan Natkins, et al.: “[H-Store: A High-Performance, Distributed Main Memory Transaction Processing System](http://www.vldb.org/pvldb/1/1454211.pdf),” *Proceedings of the VLDB Endowment*, volume 1, number 2, pages 1496–1499, August 2008.