mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
part-iii rough trans
This commit is contained in:
parent
e6df7385f6
commit
fd3cbf5701
@ -110,7 +110,7 @@
|
||||
| 第三部分:前言 | 精翻 | |
|
||||
| 第十章:批处理 | 草翻 | |
|
||||
| 第十一章:流处理 | 草翻 | |
|
||||
| 第十二章:数据系统的未来 | 机翻 | |
|
||||
| 第十二章:数据系统的未来 | 草翻 | |
|
||||
| 术语表 | - | |
|
||||
| 后记 | 机翻 | |
|
||||
|
||||
|
348
ch12.md
348
ch12.md
@ -14,11 +14,11 @@
|
||||
|
||||
对未来的看法和猜测当然是主观的,所以我在写这篇个人意见的时候会用到第一人称。我们欢迎您不同意并形成自己的观点,但我希望本章的观点至少可以成为一个富有成效的讨论的出发点,并为经常混淆的概念提供一些清晰。
|
||||
|
||||
第1章概述了本书的目标:探索如何创建可靠,可伸缩和可维护的应用程序和系统。这些主题贯穿了所有的章节:例如,我们讨论了许多有助于提高可靠性的容错算法,提高可扩展性的分区,以及提高可维护性的进化和抽象机制。在本章中,我们将把所有这些想法结合在一起,并以这些想法为基础来设想未来。我们的目标是发现如何设计比今天更好的应用程序 - 强大,正确,可演化,并最终对人类有益。
|
||||
第1章概述了本书的目标:探索如何创建可靠,可伸缩和可维护的应用程序和系统。这些主题贯穿了所有的章节:例如,我们讨论了许多有助于提高可靠性的容错算法,提高可扩展性的分区,以及提高可维护性的进化和抽象机制。在本章中,我们将把所有这些想法结合在一起,并以这些想法为基础来设想未来。我们的目标是发现如何设计比今天更好的应用程序——强大,正确,可演化,并最终对人类有益。
|
||||
|
||||
## 数据集成
|
||||
|
||||
本书中反复出现的主题是,对于任何给定的问题,都有几种解决方案,所有这些解决方案都有不同的优点,缺点和折衷。例如,在第3章讨论存储引擎时,我们看到了日志结构存储,B树和列式存储。在第5章讨论复制时,我们看到了单领导,多领导和无领导的方法。
|
||||
本书中反复出现的主题是,对于任何给定的问题,都有几种解决方案,所有这些解决方案都有不同的优点,缺点和折衷。例如,在[第3章](ch3.md)讨论存储引擎时,我们看到了日志结构存储,B树和列式存储。在[第5章](ch5.md)讨论复制时,我们看到了单领导,多领导和无领导的方法。
|
||||
|
||||
如果你有一个问题,例如“我想存储一些数据并稍后再查询”,那么没有一个正确的解决方案,但是在不同的情况下,每种方法都是适当的。软件实现通常必须选择一种特定的方法。要使一个代码路径健壮并且很好地尝试在一个软件中执行所有操作几乎可以保证实现效果很差,这很难。
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
例如,为了处理任意关键字的查询,需要将OLTP数据库与全文搜索索引集成在一起是很常见的。尽管一些数据库(如PostgreSQL)包含了全文索引功能,可以满足简单的应用需求【1】,但更复杂的搜索工具需要专业的信息检索工具。相反,搜索索引通常不适合作为一个持久的记录系统,因此许多应用程序需要结合两种不同的工具来满足所有要求。
|
||||
|
||||
我们谈到了将数据系统集成到“使系统保持同步”(第452页)的问题。随着数据的不同表示数量的增加,集成问题变得更加困难。除了数据库和搜索索引之外,也许您需要保留分析系统(数据仓库或批处理和流处理系统)中的数据副本。维护从原始数据衍生的对象的高速缓存或非规范化版本;通过机器学习,分类,排名或推荐系统传递数据;或根据对数据的更改发送通知。
|
||||
我们谈到了将数据系统集成到“[使系统保持同步]()”(第452页)的问题。随着数据的不同表示数量的增加,集成问题变得更加困难。除了数据库和搜索索引之外,也许您需要保留分析系统(数据仓库或批处理和流处理系统)中的数据副本。维护从原始数据衍生的对象的高速缓存或非规范化版本;通过机器学习,分类,排名或推荐系统传递数据;或根据对数据的更改发送通知。
|
||||
|
||||
令人惊讶的是,我经常看到软件工程师做出如下陈述:“根据我的经验,99%的人只需要X”或“......不需要X”(对于X的各种值)。我认为这样的陈述更多地讲述了讲话者的经验,而不是技术的实际有用性。您可能想要对数据执行的各种操作范围非常广泛。一个人认为是一个模糊和毫无意义的功能,可能是别人的核心要求。如果缩小数据流并考虑整个组织的数据流,那么对数据集成的需求往往就会变得明显。
|
||||
|
||||
@ -40,88 +40,88 @@
|
||||
|
||||
当需要在多个存储系统中维护相同数据的副本以满足不同的访问模式时,您需要非常清楚输入和输出:哪些数据先写入,哪些表示来自哪些源?如何以正确的格式将数据导入所有正确的地方?
|
||||
|
||||
例如,您可能会首先将数据写入记录数据库系统,捕获对该数据库所做的更改(请参阅第454页上的“更改数据捕获”),然后将更改应用于数据库中的搜索索引相同的顺序。如果更改数据捕获(CDC)是更新索引的唯一方式,则可以确定该索引完全来自记录系统,因此与其保持一致(禁止软件中的错误)。写入数据库是向该系统提供新输入的唯一方式。
|
||||
例如,您可能会首先将数据写入记录数据库系统,捕获对该数据库所做的更改(请参阅第454页上的“[捕获变更数据]()”),然后将更改应用于数据库中的搜索索引相同的顺序。如果更改数据捕获(CDC)是更新索引的唯一方式,则可以确定该索引完全来自记录系统,因此与其保持一致(禁止软件中的错误)。写入数据库是向该系统提供新输入的唯一方式。
|
||||
|
||||
允许应用程序直接写入搜索索引和数据库引入了图11-4所示的问题,其中两个客户端同时发送冲突写入,并且两个存储系统按不同顺序处理它们。在这种情况下,既不是数据库也不是
|
||||
允许应用程序直接写入搜索索引和数据库引入了[图11-4]()所示的问题,其中两个客户端同时发送冲突写入,并且两个存储系统按不同顺序处理它们。在这种情况下,既不是数据库也不是
|
||||
|
||||
基于事件日志更新衍生数据系统通常可以做出决定性的和幂等的(参见第478页的“幂等性”),使得从故障中恢复相当容易。
|
||||
基于事件日志更新衍生数据系统通常可以做出决定性的和幂等的(参见第478页的“[幂等性]()”),使得从故障中恢复相当容易。
|
||||
|
||||
#### 衍生数据与分布式事务
|
||||
|
||||
保持不同数据系统彼此一致的经典方法涉及分布式事务,如第354页的“原子提交和两阶段提交(2PC)”中所述。与分布式事务相比,使用衍生数据系统的方法如何?
|
||||
保持不同数据系统彼此一致的经典方法涉及分布式事务,如第354页的“[原子提交和两阶段提交(2PC)]()”中所述。与分布式事务相比,使用衍生数据系统的方法如何?
|
||||
|
||||
在抽象层面,他们通过不同的方式达到类似的目标。分布式事务通过使用锁定进行互斥来决定写入的顺序(请参阅第257页上的“两阶段锁定(2PL)”),而CDC和事件源使用日志进行排序。分布式事务使用原子提交来确保更改只生效一次,而基于日志的系统通常基于确定性重试和幂等性。
|
||||
在抽象层面,他们通过不同的方式达到类似的目标。分布式事务通过使用锁定进行互斥来决定写入的顺序(请参阅第257页上的“[两阶段锁定(2PL)]()”),而CDC和事件源使用日志进行排序。分布式事务使用原子提交来确保更改只生效一次,而基于日志的系统通常基于确定性重试和幂等性。
|
||||
|
||||
最大的不同之处在于事务系统通常提供线性(请参阅第324页的“线性化”),这意味着有用的保证,例如读取自己的写入(请参阅第162页的“读取自己的写入”)。另一方面,衍生数据系统通常是异步更新的,因此它们不会默认提供相同的时间保证。
|
||||
最大的不同之处在于事务系统通常提供线性(请参阅第324页的“[线性一致性]()”),这意味着有用的保证,例如读取自己的写入(请参阅第162页的“[读己之写]()”)。另一方面,衍生数据系统通常是异步更新的,因此它们不会默认提供相同的时间保证。
|
||||
|
||||
在愿意支付分布式交易成本的有限环境中,它们已被成功使用。但是,我认为XA的容错能力和性能特征较差(请参阅第364页的“实践中的分布式事务”),这严重限制了它的实用性。我相信有可能为分布式事务创建一个更好的协议,但是将这样一个协议广泛采用并与现有工具集成将是具有挑战性的,并且不太可能很快发生。
|
||||
在愿意支付分布式交易成本的有限环境中,它们已被成功使用。但是,我认为XA的容错能力和性能特征较差(请参阅第364页的“[实践中的分布式事务]()”),这严重限制了它的实用性。我相信有可能为分布式事务创建一个更好的协议,但是将这样一个协议广泛采用并与现有工具集成将是具有挑战性的,并且不太可能很快发生。
|
||||
|
||||
在没有广泛支持良好分布式事务协议的情况下,我认为基于日志的衍生数据是集成不同数据系统的最有前途的方法。然而,诸如阅读自己写作的保证是有用的,我认为告诉每个人“最终的一致性是不可避免的 - 吸收它并学会处理它”是没有成果的(至少不是没有良好的指导如何处理它)。
|
||||
在没有广泛支持良好分布式事务协议的情况下,我认为基于日志的衍生数据是集成不同数据系统的最有前途的方法。然而,诸如阅读自己写作的保证是有用的,我认为告诉每个人“最终的一致性是不可避免的 —— 吸收它并学会处理它”是没有成果的(至少不是没有良好的指导如何处理它)。
|
||||
|
||||
在第51页的“瞄准正确性”中,我们将讨论一些在异步衍生系统之上实现更强保障的方法,并在分布式事务和异步基于对数系统之间建立中间立场。
|
||||
|
||||
#### 全局有序的限制
|
||||
|
||||
对于足够小的系统,构建一个完全有序的事件日志是完全可行的(正如单引导程序复制数据库的流行所证明的那样,这正好构建了这样一个日志)。但是,随着系统向更大更复杂的工作负载扩展,限制开始出现:
|
||||
对于足够小的系统,构建一个完全有序的事件日志是完全可行的(正如单主复制数据库的流行所证明的那样,这正好构建了这样一个日志)。但是,随着系统向更大更复杂的工作负载扩展,限制开始出现:
|
||||
|
||||
* 在大多数情况下,构建完全有序的日志需要所有事件通过决定订购的单个领导节点。如果事件吞吐量大于单台计算机可处理的事件,则需要将其分割到多台计算机上(请参见第446页的“分区日志”)。然后,两个不同分区中的事件顺序不明确。
|
||||
* 在大多数情况下,构建完全有序的日志需要所有事件通过决定订购的单个领导节点。如果事件吞吐量大于单台计算机可处理的事件,则需要将其分割到多台计算机上(请参见第446页的“[分区日志]()”)。然后,两个不同分区中的事件顺序不明确。
|
||||
|
||||
|
||||
* 如果服务器分布在多个地理位置分散的数据中心上,例如为了容忍整个数据中心脱机,您通常在每个数据中心都有单独的领导者,因为网络延迟会导致同步的跨数据中心协调效率低下(请参阅“Multi -Leader复制“)。这意味着源自两个不同数据中心的事件的未定义排序。
|
||||
* 如果服务器分布在多个地理位置分散的数据中心上,例如为了容忍整个数据中心脱机,您通常在每个数据中心都有单独的领导者,因为网络延迟会导致同步的跨数据中心协调效率低下(请参阅“[多主复制]()“)。这意味着源自两个不同数据中心的事件的未定义排序。
|
||||
|
||||
|
||||
* 将应用程序部署为微服务时(请参阅第125页上的“通过服务进行数据流:REST和RPC”),常见的设计选择是将每个服务及其持久状态作为独立单元进行部署,服务之间不共享持久状态。当两个事件来自不同的服务时,这些事件没有定义的顺序。
|
||||
* 将应用程序部署为微服务时(请参阅第125页上的“[通过服务进行数据流:REST和RPC]()”),常见的设计选择是将每个服务及其持久状态作为独立单元进行部署,服务之间不共享持久状态。当两个事件来自不同的服务时,这些事件没有定义的顺序。
|
||||
|
||||
|
||||
* 某些应用程序保持客户端状态,该状态在用户输入时立即更新(无需等待服务器确认),甚至可以继续脱机工作(请参阅第170页的“脱机操作的客户端”)。有了这样的应用程序,客户端和服务器很可能以不同的顺序看到事件。
|
||||
* 某些应用程序保持客户端状态,该状态在用户输入时立即更新(无需等待服务器确认),甚至可以继续脱机工作(请参阅第170页的“[脱机操作的客户端]()”)。有了这样的应用程序,客户端和服务器很可能以不同的顺序看到事件。
|
||||
|
||||
在形式上,决定事件的总次序称为总次序广播,相当于共识(请参阅第366页上的“共识算法和总次序广播”)。大多数共识算法都是针对单个节点的吞吐量足以处理整个事件流的情况而设计的,并且这些算法不提供多个节点共享事件排序工作的机制。设计共识算法仍然是一个开放的研究问题,它可以扩展到单个节点的吞吐量之外,并且在地理上分散的环境中工作良好。
|
||||
在形式上,决定事件的总次序称为总次序广播,相当于共识(请参阅第366页上的“[共识算法和全序广播]()”)。大多数共识算法都是针对单个节点的吞吐量足以处理整个事件流的情况而设计的,并且这些算法不提供多个节点共享事件排序工作的机制。设计共识算法仍然是一个开放的研究问题,它可以扩展到单个节点的吞吐量之外,并且在地理上分散的环境中工作良好。
|
||||
|
||||
#### 订购事件以捕捉因果关系
|
||||
|
||||
在事件之间不存在因果关系的情况下,缺乏全部命令并不是一个大问题,因为并发事件可以任意排序。其他一些情况很容易处理:例如,当同一对象有多个更新时,它们可以通过将特定对象ID的所有更新路由到相同的日志分区来完全排序。然而,因果关系有时会以更微妙的方式出现(另请参阅“订购和因果关系”第319页)。
|
||||
在事件之间不存在因果关系的情况下,缺乏全部命令并不是一个大问题,因为并发事件可以任意排序。其他一些情况很容易处理:例如,当同一对象有多个更新时,它们可以通过将特定对象ID的所有更新路由到相同的日志分区来完全排序。然而,因果关系有时会以更微妙的方式出现(另请参阅“[顺序和因果关系]()”第319页)。
|
||||
|
||||
例如,考虑一个社交网络服务,以及两个相互关系但刚分手的用户。其中一个用户将另一个作为朋友移除,然后向其余的朋友发送消息,抱怨他们的前伴侣。用户的意图是他们的前配偶不应该看到粗鲁的信息,因为信息是在朋友状态被撤销后发送的。
|
||||
|
||||
但是,在一个地方存储友谊状态并在另一个地方存储消息的系统中,不友好事件和消息发送事件之间的顺序依赖关系可能会丢失。如果未捕获到因果依赖关系,则发送有关新消息的通知的服务可能会在不友好事件之前处理消息发送事件,从而错误地向前伙伴发送通知。
|
||||
|
||||
在本例中,通知实际上是消息和好友列表之间的连接,使得它与我们先前讨论的连接的时间问题有关(请参阅第475页的“连接的时间依赖性”)。不幸的是,这个问题似乎并没有一个简单的答案【2,3】。起点包括:
|
||||
在本例中,通知实际上是消息和好友列表之间的连接,使得它与我们先前讨论的连接的时间问题有关(请参阅第475页的“[连接的时间依赖性]()”)。不幸的是,这个问题似乎并没有一个简单的答案【2,3】。起点包括:
|
||||
|
||||
* 逻辑时间戳可以提供没有协调的全部订购(请参见“序列号排序”第页343),因此它们可能有助于总订单广播不可行的情况。但是,他们仍然要求收件人处理不按顺序发送的事件,并且需要传递其他元数据。
|
||||
* 如果您可以记录一个事件来记录用户在做出决定之前所看到的系统状态,并给该事件一个唯一的标识符,那么后面的任何事件都可以引用该事件标识符来记录因果关系【4】 。我们将在第513页的“Reads are events too”中回到这个想法。
|
||||
* 冲突解决算法(请参阅“自动冲突解决”(第165页))有助于处理以意外顺序传递的事件。它们对于维护状态很有用,但如果行为有外部副作用(例如,也许,随着时间的推移,应用程序开发模式将出现,使得能够有效地捕获因果依赖关系,并且保持正确的衍生状态,而不会迫使所有事件经历全部命令广播的瓶颈。
|
||||
* 逻辑时间戳可以提供没有协调的全部订购(请参见“[序列号排序]()”第页343),因此它们可能有助于全序广播不可行的情况。但是,他们仍然要求收件人处理不按顺序发送的事件,并且需要传递其他元数据。
|
||||
* 如果您可以记录一个事件来记录用户在做出决定之前所看到的系统状态,并给该事件一个唯一的标识符,那么后面的任何事件都可以引用该事件标识符来记录因果关系【4】 。我们将在第513页的“[读也是事件](#读也是事件)”中回到这个想法。
|
||||
* 冲突解决算法(请参阅“[自动冲突解决]()”(第165页))有助于处理以意外顺序传递的事件。它们对于维护状态很有用,但如果行为有外部副作用(例如,也许,随着时间的推移,应用程序开发模式将出现,使得能够有效地捕获因果依赖关系,并且保持正确的衍生状态,而不会迫使所有事件经历全部命令广播的瓶颈。
|
||||
|
||||
### 批量处理与流处理
|
||||
|
||||
我会说数据集成的目标是确保数据在所有正确的地方以正确的形式结束。这样做需要消耗投入,转化,加入,过滤,汇总,培训模型,评估并最终写入适当的输出。批处理和流处理器是实现这一目标的工具。
|
||||
|
||||
批处理和流处理的输出是衍生的数据集,例如搜索索引,实例化视图,向用户显示的建议,聚合度量等(请参阅“批处理工作流的输出”(第417页)和“流处理的用法”第465页)。
|
||||
批处理和流处理的输出是衍生的数据集,例如搜索索引,实例化视图,向用户显示的建议,聚合度量等(请参阅“[批处理工作流的输出]()”(第417页)和“[流处理的用法]()”第465页)。
|
||||
|
||||
正如我们在第10章和第11章中看到的,批处理和流处理有许多共同的原则,主要的根本区别在于流处理器在无界数据集上运行,而批处理输入是已知的有限大小。处理引擎的实现方式也有很多细节上的差异,但是这些区别开始模糊。
|
||||
正如我们在[第10章](ch10.md)和[第11章](ch11.md)中看到的,批处理和流处理有许多共同的原则,主要的根本区别在于流处理器在无界数据集上运行,而批处理输入是已知的有限大小。处理引擎的实现方式也有很多细节上的差异,但是这些区别开始模糊。
|
||||
|
||||
Spark在批处理引擎上执行流处理,将流分解为微格式,而Apache Flink则在流处理引擎上执行批处理【5】。原则上,一种类型的处理可以在另一种类型上仿真,但是性能特征会有所不同:例如,在跳跃或滑动窗口时,微博可能表现不佳【6】。
|
||||
|
||||
#### 保持衍生状态
|
||||
|
||||
批处理具有非常强大的功能特性(即使代码不是用函数式编程语言编写的):它鼓励确定性的纯函数,其输出仅依赖于输入,除了显式输出外没有副作用,处理输入作为不可变的,并作为附加的输出。流处理类似,但它扩展了运算符以允许受管理的容错状态(请参阅第478页的“重建失败后的状态”)。
|
||||
批处理具有非常强大的功能特性(即使代码不是用函数式编程语言编写的):它鼓励确定性的纯函数,其输出仅依赖于输入,除了显式输出外没有副作用,处理输入作为不可变的,并作为附加的输出。流处理类似,但它扩展了运算符以允许受管理的容错状态(请参阅第478页的“[重建失败后的状态”]())。
|
||||
|
||||
具有良好定义的输入和输出的确定性函数的原理不仅有利于容错(请参见第478页的“幂等性”),但也简化了有关组织中数据流的推理【7】。无论衍生数据是搜索索引,统计模型还是缓存,从数据管道角度来看,从另一个衍生出一件事情,通过功能应用程序代码推送一个系统中的状态更改和应用对衍生系统的影响。
|
||||
具有良好定义的输入和输出的确定性函数的原理不仅有利于容错(请参见第478页的“[幂等性]()”),但也简化了有关组织中数据流的推理【7】。无论衍生数据是搜索索引,统计模型还是缓存,从数据管道角度来看,从另一个衍生出一件事情,通过功能应用程序代码推送一个系统中的状态更改和应用对衍生系统的影响。
|
||||
|
||||
原则上,衍生数据系统可以同步维护,就像关系数据库在与被索引表写入操作相同的事务中同步更新辅助索引一样。然而,异步是基于事件日志的系统稳健的原因:它允许系统的一部分故障被本地包含,而如果任何一个参与者失败,分布式事务将中止,因此他们倾向于通过将故障扩展到系统的其余部分(请参阅第363页的“分布式事务的限制”)。
|
||||
原则上,衍生数据系统可以同步维护,就像关系数据库在与被索引表写入操作相同的事务中同步更新辅助索引一样。然而,异步是基于事件日志的系统稳健的原因:它允许系统的一部分故障被本地包含,而如果任何一个参与者失败,分布式事务将中止,因此他们倾向于通过将故障扩展到系统的其余部分(请参阅第363页的“[分布式事务的限制]()”)。
|
||||
|
||||
我们在第206页的“分区和二级索引”中看到,二级索引经常跨越分区边界。具有二级索引的分区系统需要将写入发送到多个分区(如果索引是分词)或将读取发送到所有分区(如果索引是文档分区的话)。如果索引是异步维护的,这种交叉分区通信也是最可靠和可扩展的【8】(另请参阅“多分区数据处理”第479页)。
|
||||
我们在第206页的“分区和二级索引”中看到,二级索引经常跨越分区边界。具有二级索引的分区系统需要将写入发送到多个分区(如果索引是分词)或将读取发送到所有分区(如果索引是文档分区的话)。如果索引是异步维护的,这种交叉分区通信也是最可靠和可扩展的【8】(另请参阅“[多分区数据处理]()”第479页)。
|
||||
|
||||
#### 为应用程序演变重新处理数据
|
||||
|
||||
在维护衍生数据时,批处理和流处理都是有用的。流处理允许将输入中的变化以低延迟反映在衍生视图中,而批处理允许重新处理大量累积的历史数据以便将新视图导出到现有数据集上。
|
||||
|
||||
特别是,重新处理现有数据为维护系统提供了一个良好的机制,并将其发展为支持新功能和变更需求(参见第4章)。如果不进行重新处理,模式演化就会局限于简单的变化,例如向记录中添加新的可选字段或添加新类型的记录。无论是在写模式还是在读模式中都是如此(请参阅第39页的“文档模型中的模式灵活性”)。另一方面,通过重新处理,可以将数据集重组为一个完全不同的模型,以便更好地满足新的要求。
|
||||
特别是,重新处理现有数据为维护系统提供了一个良好的机制,并将其发展为支持新功能和变更需求(参见[第4章](ch4.md))。如果不进行重新处理,模式演化就会局限于简单的变化,例如向记录中添加新的可选字段或添加新类型的记录。无论是在写模式还是在读模式中都是如此(请参阅第39页的“[文档模型中的模式灵活性]()”)。另一方面,通过重新处理,可以将数据集重组为一个完全不同的模型,以便更好地满足新的要求。
|
||||
|
||||
> ### 在铁路上的模式迁移
|
||||
>
|
||||
> 大规模的“模式迁移”也发生在非计算机系统中。例如,在19世纪英国铁路建设初期,轨距(两轨之间的距离)就有了各种各样的交易标准。为一个测量仪而建的列车不能在另一个测量仪的轨道上运行,这限制了火车网络中可能的相互连接【9】。
|
||||
>
|
||||
> 在1846年最终确定了一个标准仪表之后,其他仪表的轨道必须转换 - 但是如何在不关闭火车线路的情况下进行数月甚至数年?解决的办法是首先将轨道转换为双轨或混合轨距,方法是增加第三轨。这种转换可以逐渐完成,当完成时,两个仪表的列车可以在三条轨道中的两条轨道上运行。事实上,一旦所有的列车都转换成标准轨距,那么可以移除提供非标准轨距的轨道。
|
||||
> 在1846年最终确定了一个标准仪表之后,其他仪表的轨道必须转换 —— 但是如何在不关闭火车线路的情况下进行数月甚至数年?解决的办法是首先将轨道转换为双轨或混合轨距,方法是增加第三轨。这种转换可以逐渐完成,当完成时,两个仪表的列车可以在三条轨道中的两条轨道上运行。事实上,一旦所有的列车都转换成标准轨距,那么可以移除提供非标准轨距的轨道。
|
||||
>
|
||||
> 以这种方式“再加工”现有的轨道,让新旧版本并存,可以在几年的时间内逐渐改变轨距。然而,这是一项昂贵的事业,这就是今天非标准仪表仍然存在的原因。例如,旧金山湾区的BART系统使用与美国大部分地区不同的仪表。
|
||||
|
||||
@ -133,15 +133,15 @@ Spark在批处理引擎上执行流处理,将流分解为微格式,而Apache
|
||||
|
||||
如果批处理用于重新处理历史数据,并且流处理用于处理最近的更新,那么您如何将这两者结合起来?拉姆达体系结构【12】是这方面的一个建议,引起了很多关注。
|
||||
|
||||
lambda体系结构的核心思想是通过将不可变事件附加到不断增长的数据集来记录传入数据,这类似于事件源(请参阅第457页上的“事件源”)。从这些事件中,推导出读取优化的视图。 lambda体系结构建议并行运行两个不同的系统:批处理系统(如Hadoop MapReduce)和独立的流处理系统(如Storm)。
|
||||
lambda体系结构的核心思想是通过将不可变事件附加到不断增长的数据集来记录传入数据,这类似于事件源(请参阅第457页上的“[事件源]()”)。从这些事件中,推导出读取优化的视图。 Lambda体系结构建议并行运行两个不同的系统:批处理系统(如Hadoop MapReduce)和独立的流处理系统(如Storm)。
|
||||
|
||||
在lambda方法中,流处理器消耗事件并快速生成对视图的近似更新;批处理器稍后将使用同一组事件并生成衍生视图的更正版本。这个设计背后的原因是批处理更简单,因此不易出错,而流处理器被认为是不太可靠和难以容错的(请参阅“故障容错”)。而且,流处理可以使用快速近似算法,而批处理使用较慢的精确算法。
|
||||
在lambda方法中,流处理器消耗事件并快速生成对视图的近似更新;批处理器稍后将使用同一组事件并生成衍生视图的更正版本。这个设计背后的原因是批处理更简单,因此不易出错,而流处理器被认为是不太可靠和难以容错的(请参阅“[故障容错]()”)。而且,流处理可以使用快速近似算法,而批处理使用较慢的精确算法。
|
||||
|
||||
拉姆达体系结构是一个有影响力的想法,它将数据系统的设计变得更好,尤其是通过推广将视图衍生到不可变事件流和在需要时重新处理事件的原则。但是,我也认为它有一些实际问题:
|
||||
|
||||
* 必须保持相同的逻辑才能在批处理和流处理框架中运行,这是额外的工作。虽然像Summingbird 【13】这样的库提供了一个抽象的计算,可以在一个批处理或流的上下文中运行,调试,调整和维护两个不同系统的操作复杂性仍然【14】。
|
||||
* 由于流管道和批处理管道产生单独的输出,因此需要合并它们以响应用户请求。如果计算是通过滚动窗口的简单聚合,则合并相当容易,但如果使用更复杂的操作(例如连接和会话化)导出视图,或者输出不是时间序列,则显得非常困难。
|
||||
* 尽管有能力重新处理整个历史数据集是很好的,但在大型数据集上这样做经常会很昂贵。因此,批处理流水线通常需要设置为处理增量批处理(例如,在每小时结束时处理一小时的数据),而不是重新处理所有内容。这引发了第468页的“关于时间的推理”中讨论的问题,例如处理分段器和处理跨批次边界的窗口。增加批量计算会增加复杂性,使其更类似于流式传输层,这与保持批处理层尽可能简单的目标背道而驰。
|
||||
* 尽管有能力重新处理整个历史数据集是很好的,但在大型数据集上这样做经常会很昂贵。因此,批处理流水线通常需要设置为处理增量批处理(例如,在每小时结束时处理一小时的数据),而不是重新处理所有内容。这引发了第468页的“[关于时间的推理]()”中讨论的问题,例如处理分段器和处理跨批次边界的窗口。增加批量计算会增加复杂性,使其更类似于流式传输层,这与保持批处理层尽可能简单的目标背道而驰。
|
||||
|
||||
#### 统一批处理和流处理
|
||||
|
||||
@ -149,9 +149,9 @@ lambda体系结构的核心思想是通过将不可变事件附加到不断增
|
||||
|
||||
在一个系统中统一批处理和流处理需要以下功能,这些功能越来越广泛:
|
||||
|
||||
* 通过处理最近事件流的相同处理引擎来重放历史事件的能力。例如,基于日志的消息代理可以重放消息(请参阅第451页的“重放旧消息”),某些流处理器可以从HDFS等分布式文件系统读取输入。
|
||||
* 对于流处理器来说,只有一次语义 - 即确保输出与未发生故障的输出相同,即使事实上发生故障(请参阅“故障容错”(第476页))。与批处理一样,这需要丢弃任何失败任务的部分输出。
|
||||
* 按事件时间进行窗口化的工具,而不是按处理时间进行窗口化,因为处理历史事件时的处理时间毫无意义(请参阅第468页的“关于时间的推理”)。例如,Apache Beam提供了用于表达这种计算的API,然后可以使用Apache Flink或Google Cloud Dataflow运行。
|
||||
* 通过处理最近事件流的相同处理引擎来重放历史事件的能力。例如,基于日志的消息代理可以重放消息(请参阅第451页的“[重放旧消息]()”),某些流处理器可以从HDFS等分布式文件系统读取输入。
|
||||
* 对于流处理器来说,只有一次语义 —— 即确保输出与未发生故障的输出相同,即使事实上发生故障(请参阅“[故障容错]()”(第476页))。与批处理一样,这需要丢弃任何失败任务的部分输出。
|
||||
* 按事件时间进行窗口化的工具,而不是按处理时间进行窗口化,因为处理历史事件时的处理时间毫无意义(请参阅第468页的“[关于时间的推理]()”)。例如,Apache Beam提供了用于表达这种计算的API,然后可以使用Apache Flink或Google Cloud Dataflow运行。
|
||||
|
||||
|
||||
|
||||
@ -161,7 +161,7 @@ lambda体系结构的核心思想是通过将不可变事件附加到不断增
|
||||
|
||||
## 拆分数据库
|
||||
|
||||
在最抽象的层面上,数据库,Hadoop和操作系统都执行相同的功能:它们存储一些数据,并允许您处理和查询数据【16】。数据库将数据存储在某些数据模型(表中的文档,文档中的顶点,图形中的顶点等)的记录中,而操作系统的文件系统则将数据存储在文件中 - 但在其核心上,都是“信息管理”系统[ 17。正如我们在第10章中看到的,Hadoop生态系统有点像Unix的分布式版本。
|
||||
在最抽象的层面上,数据库,Hadoop和操作系统都执行相同的功能:它们存储一些数据,并允许您处理和查询数据【16】。数据库将数据存储在某些数据模型(表中的文档,文档中的顶点,图形中的顶点等)的记录中,而操作系统的文件系统则将数据存储在文件中——但在其核心上,都是“信息管理”系统【17】。正如我们在[第10章](ch10.md)中看到的,Hadoop生态系统有点像Unix的分布式版本。
|
||||
|
||||
当然,有很多实际的差异。例如,许多文件系统不能很好地处理包含1000万个小文件的目录,而包含1000万个小记录的数据库是完全正常且不起眼的。无论如何,操作系统和数据库之间的相似之处和差异值得探讨。
|
||||
|
||||
@ -177,34 +177,34 @@ Unix和关系数据库以非常不同的哲学来处理信息管理问题。 Uni
|
||||
|
||||
在本书的过程中,我们讨论了数据库提供的各种功能及其工作原理,其中包括:
|
||||
|
||||
* 二级索引,使您可以根据字段的值有效地搜索记录(请参阅第79页上的“其他索引结构”
|
||||
* 物化视图,这是一种预先计算的查询结果缓存(请参阅“聚合:数据立方体和物化视图”(第101页)
|
||||
* 复制日志,保持其他节点上数据的副本最新(请参阅第158页中的“复制日志的实现”)
|
||||
* 全文搜索索引,允许在文本中进行关键字搜索(请参见第88页上的“全文搜索和模糊索引”)以及内置于某些关系数据库【1】
|
||||
* 二级索引,使您可以根据字段的值有效地搜索记录(请参阅第79页上的“[其他索引结构]()”)
|
||||
* 物化视图,这是一种预先计算的查询结果缓存(请参阅“[聚合:数据立方体和物化视图]()”,第101页)
|
||||
* 复制日志,保持其他节点上数据的副本最新(请参阅第158页中的“[复制日志的实现]()”)
|
||||
* 全文搜索索引,允许在文本中进行关键字搜索(请参见第88页上的“[全文搜索和模糊索引]()”)以及内置于某些关系数据库【1】
|
||||
|
||||
在第十章和第十一章中,出现了类似的主题。我们讨论了如何构建全文搜索索引(请参阅第357页上的“批处理工作流的输出”),了解有关实例化视图维护(请参阅“维护实例化视图”一节第437页)以及有关将更改从数据库复制到衍生数据系统(请参阅第454页的“更改数据捕获”)。
|
||||
在第十章和第十一章中,出现了类似的主题。我们讨论了如何构建全文搜索索引(请参阅第357页上的“[批处理工作流的输出]()”),了解有关实例化视图维护(请参阅“[维护实例化视图]()”一节第437页)以及有关将更改从数据库复制到衍生数据系统(请参阅第454页的“[更改数据捕获]()”)。
|
||||
|
||||
数据库中内置的功能与人们用批处理和流处理器构建的衍生数据系统似乎有相似之处。
|
||||
|
||||
#### 创建一个索引
|
||||
|
||||
想想当你运行CREATE INDEX在关系数据库中创建一个新的索引时会发生什么。数据库必须扫描表的一致性快照,挑选出所有被索引的字段值,对它们进行排序,然后写出索引。然后它必须处理自一致快照以来所做的写入操作(假设表在创建索引时未被锁定,所以写操作可能会继续)。一旦完成,只要事务写入表中,数据库就必须继续保持索引最新。
|
||||
想想当你运行`CREATE INDEX`在关系数据库中创建一个新的索引时会发生什么。数据库必须扫描表的一致性快照,挑选出所有被索引的字段值,对它们进行排序,然后写出索引。然后它必须处理自一致快照以来所做的写入操作(假设表在创建索引时未被锁定,所以写操作可能会继续)。一旦完成,只要事务写入表中,数据库就必须继续保持索引最新。
|
||||
|
||||
此过程非常类似于设置新的追随者副本(请参阅第155页的“设置新的追随者”),也非常类似于流系统中的引导更改数据捕获(请参阅第455页的“初始快照”)。
|
||||
此过程非常类似于设置新的追随者副本(请参阅第155页的“[设置新的追随者]()”),也非常类似于流系统中的引导更改数据捕获(请参阅第455页的“[初始快照]()”)。
|
||||
|
||||
无论何时运行CREATE INDEX,数据库都会重新处理现有数据集(如第494页的“重新处理应用程序数据的演变数据”中所述),并将该索引作为新视图导出到现有数据上。现有数据可能是状态的快照,而不是所有发生变化的日志,但两者密切相关(请参阅“状态,数据流和不变性”第459页)。
|
||||
无论何时运行CREATE INDEX,数据库都会重新处理现有数据集(如第494页的“[重新处理应用程序数据的演变数据]()”中所述),并将该索引作为新视图导出到现有数据上。现有数据可能是状态的快照,而不是所有发生变化的日志,但两者密切相关(请参阅“[状态,数据流和不变性]()”第459页)。
|
||||
|
||||
#### 一切的元数据库
|
||||
|
||||
有鉴于此,我认为整个组织的数据流开始像一个巨大的数据库【7】。每当批处理,流或ETL过程将数据从一个地方传输到另一个地方并形成表单时,就像数据库子系统一样,使索引或物化视图保持最新。
|
||||
|
||||
像这样看,批处理和流处理器就像触发器,存储过程和物化视图维护例程的精细实现。他们维护的衍生数据系统就像不同的索引类型。例如,关系数据库可能支持B树索引,散列索引,空间索引(请参阅第79页的“多列索引”)以及其他类型的索引。在新兴的衍生数据系统体系结构中,不是将这些设施作为单个集成数据库产品的功能实现,而是由各种不同的软件提供,运行在不同的机器上,由不同的团队管理。
|
||||
像这样看,批处理和流处理器就像触发器,存储过程和物化视图维护例程的精细实现。他们维护的衍生数据系统就像不同的索引类型。例如,关系数据库可能支持B树索引,散列索引,空间索引(请参阅第79页的“[多列索引]()”)以及其他类型的索引。在新兴的衍生数据系统体系结构中,不是将这些设施作为单个集成数据库产品的功能实现,而是由各种不同的软件提供,运行在不同的机器上,由不同的团队管理。
|
||||
|
||||
这些发展在未来将会把我们带到哪里?如果我们从没有适合所有访问模式的单一数据模型或存储格式的前提出发,我推测有两种途径可以将不同的存储和处理工具组合成一个有凝聚力的系统:
|
||||
|
||||
**联合数据库:统一读取**
|
||||
|
||||
可以为各种各样的底层存储引擎和处理方法提供一个统一的查询接口 - 一种称为联邦数据库或多存储的方法【18,19】。例如,PostgreSQL的外部数据包装功能符合这种模式【20】。需要专用数据模型或查询接口的应用程序仍然可以直接访问底层存储引擎,而想要组合来自不同位置的数据的用户可以通过联合接口轻松完成操作。
|
||||
可以为各种各样的底层存储引擎和处理方法提供一个统一的查询接口 —— 一种称为联邦数据库或多存储的方法【18,19】。例如,PostgreSQL的外部数据包装功能符合这种模式【20】。需要专用数据模型或查询接口的应用程序仍然可以直接访问底层存储引擎,而想要组合来自不同位置的数据的用户可以通过联合接口轻松完成操作。
|
||||
|
||||
联合查询接口遵循单一集成系统的关系传统,具有高级查询语言和优雅的语义,但却是一个复杂的实现。
|
||||
|
||||
@ -212,28 +212,28 @@ Unix和关系数据库以非常不同的哲学来处理信息管理问题。 Uni
|
||||
|
||||
虽然联合会解决了跨多个不同系统的只读查询问题,但它并没有很好的解决跨系统同步写入的问题。我们说过,在单个数据库中,创建一致的索引是一项内置功能。当我们构建多个存储系统时,我们同样需要确保所有数据更改都会在所有正确的位置结束,即使在出现故障时也是如此。将存储系统可靠地插接在一起(例如,通过更改数据捕获和事件日志)更容易,就像将数据库的索引维护功能以可以跨不同技术同步写入的方式分开【7,21】。
|
||||
|
||||
unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件事【22】,通过统一的低级API(管道)进行通信,并且可以使用更高级别的语言(shell)【16】 。
|
||||
非捆绑方法遵循Unix传统的小型工具,它可以很好地完成一件事【22】,通过统一的低级API(管道)进行通信,并且可以使用更高级别的语言(shell)【16】 。
|
||||
|
||||
#### 开展分拆工作
|
||||
|
||||
联邦和非捆绑是同一个硬币的两个方面:用不同的组件构成可靠,可扩展和可维护的系统。联合只读联邦和非捆绑是同一个硬币的两个方面:用不同的组件构成可靠,可扩展和可维护的系统。联合只读查询需要将一个数据模型映射到另一个数据模型,这需要一些思考,但最终还是一个可管理的问题。我认为保持写入到几个存储系统是同步的更困难的工程问题,所以我将重点关注它。
|
||||
|
||||
传统的同步写入方法需要跨异构存储系统的分布式事务【18】,我认为这是错误的解决方案(请参阅“导出的数据与分布式事务”第495页)。单个存储或流处理系统内的事务是可行的,但是当数据跨越不同技术之间的边界时,我认为具有幂等写入的异步事件日志是一种更加健壮和实用的方法。
|
||||
传统的同步写入方法需要跨异构存储系统的分布式事务【18】,我认为这是错误的解决方案(请参阅“[导出的数据与分布式事务]()”第495页)。单个存储或流处理系统内的事务是可行的,但是当数据跨越不同技术之间的边界时,我认为具有幂等写入的异步事件日志是一种更加健壮和实用的方法。
|
||||
|
||||
例如,分布式事务在某些流处理器中使用,以精确匹配一次语义(请参阅第477页的“重新访问原子提交”),这可以很好地工作。然而,当事务需要涉及由不同人群编写的系统时(例如,当数据从流处理器写入分布式键值存储或搜索索引时),缺乏标准化的事务协议会使集成更难。有幂等消费者的事件的有序日志(参见第478页的“幂等性”)是一种更简单的抽象,因此在异构系统中实现更加可行【7】。
|
||||
例如,分布式事务在某些流处理器中使用,以精确匹配一次语义(请参阅第477页的“[重新访问原子提交]()”),这可以很好地工作。然而,当事务需要涉及由不同人群编写的系统时(例如,当数据从流处理器写入分布式键值存储或搜索索引时),缺乏标准化的事务协议会使集成更难。有幂等消费者的事件的有序日志(参见第478页的“[幂等性]()”)是一种更简单的抽象,因此在异构系统中实现更加可行【7】。
|
||||
|
||||
基于日志的集成的一大优势是各个组件之间的松散耦合,这体现在两个方面:
|
||||
|
||||
1. 在系统级别,异步事件流使整个系统对各个组件的中断或性能下降更加稳健。如果使用者运行缓慢或失败,那么事件日志可以缓冲消息(请参阅“磁盘空间使用情况”第369页),以便生产者和任何其他使用者可以继续不受影响地运行。有问题的消费者可以在固定时赶上,因此不会错过任何数据,并且包含故障。相比之下,分布式事务的同步交互往往会将本地故障升级为大规模故障(请参见第363页的“分布式事务的限制”)。
|
||||
1. 在系统级别,异步事件流使整个系统对各个组件的中断或性能下降更加稳健。如果使用者运行缓慢或失败,那么事件日志可以缓冲消息(请参阅“[磁盘空间使用情况]()”第369页),以便生产者和任何其他使用者可以继续不受影响地运行。有问题的消费者可以在固定时赶上,因此不会错过任何数据,并且包含故障。相比之下,分布式事务的同步交互往往会将本地故障升级为大规模故障(请参见第363页的“[分布式事务的限制]()”)。
|
||||
2. 在人力方面,分拆数据系统允许不同的团队独立开发,改进和维护不同的软件组件和服务。专业化使得每个团队都可以专注于做好一件事,并与其他团队的系统进行明确的界面。事件日志提供了一个足够强大的接口,以捕获相当强的一致性属性(由于持久性和事件的顺序),但也足够普遍适用于几乎任何类型的数据。
|
||||
|
||||
#### 非捆绑与集成系统
|
||||
|
||||
如果分拆确实成为未来的方式,它将不会取代目前形式的数据库 - 它们仍然会像以往一样需要。数据库仍然需要维护流处理器中的状态,并且为批处理和流处理器的输出提供查询服务(请参阅第419页上的“批处理工作流的输出”和第464页上的“处理流”)。专门的查询引擎将继续对特定的工作负载非常重要:例如,MPP数据仓库中的查询引擎针对探索性分析查询进行了优化,并且能够很好地处理这种类型的工作负载(请参阅第417页的“将Hadoop与分布式数据库进行比较” 。
|
||||
如果分拆确实成为未来的方式,它将不会取代目前形式的数据库 —— 它们仍然会像以往一样需要。数据库仍然需要维护流处理器中的状态,并且为批处理和流处理器的输出提供查询服务(请参阅第419页上的“[批处理工作流的输出]()”和第464页上的“[处理流]()”)。专门的查询引擎将继续对特定的工作负载非常重要:例如,MPP数据仓库中的查询引擎针对探索性分析查询进行了优化,并且能够很好地处理这种类型的工作负载(请参阅第417页的“[将Hadoop与分布式数据库进行比较]()” 。
|
||||
|
||||
运行几个不同基础架构的复杂性可能是一个问题:每一个软件都有一个学习曲线,配置问题和操作怪癖,因此值得部署尽可能少的移动部件。与由应用程序代码【23】组成的多个工具组成的系统相比,单一集成软件产品也可以在其设计的工作负载类型上实现更好,更可预测的性能。正如我在前言中所说的那样,为了扩大规模而建设你不需要的是浪费精力,并且可能会将你锁定在一个不灵活的设计中。实际上,这是一种过早优化的形式。
|
||||
|
||||
分拆的目标不是要针对个别数据库与特定工作负载的性能进行竞争;我们的目标是允许您结合多个不同的数据库,以便在比单个软件可能实现的更广泛的工作负载范围内实现更好的性能。这是关于广度,而不是深度 - 与我们在第414页上的“比较Hadoop与分布式数据库”中讨论的存储和处理模型的多样性一样。
|
||||
分拆的目标不是要针对个别数据库与特定工作负载的性能进行竞争;我们的目标是允许您结合多个不同的数据库,以便在比单个软件可能实现的更广泛的工作负载范围内实现更好的性能。这是关于广度,而不是深度 —— 与我们在第414页上的“[比较Hadoop与分布式数据库]()”中讨论的存储和处理模型的多样性一样。
|
||||
|
||||
因此,如果有一项技术可以满足您的所有需求,那么您最好使用该产品,而不是试图用低级组件重新实现它。只有当没有单一软件满足您的所有需求时,才会出现拆分和合成的优势。
|
||||
|
||||
@ -241,13 +241,13 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
用于组成数据系统的工具正在变得越来越好,但我认为缺少一个主要部分:我们还没有Unix shell的非捆绑式数据库(即,用于组成存储和处理系统的高级语言简单和陈述的方式)。
|
||||
|
||||
例如,如果我们可以简单地声明mysql |,我就会喜欢它elasticsearch,类似于Unix管道【22】,这将成为CREATE INDEX的非捆绑等价物:它将MySQL数据库中的所有文档并将其索引到Elasticsearch集群中。然后它会不断捕获对数据库所做的所有更改,并自动将它们应用于搜索索引,而无需编写自定义应用程序代码。几乎任何类型的存储或索引系统都可以实现这种集成。
|
||||
例如,如果我们可以简单地声明`mysql |elasticsearch`,类似于Unix管道【22】,这将成为`CREATE INDEX`的非捆绑等价物:它将MySQL数据库中的所有文档并将其索引到Elasticsearch集群中。然后它会不断捕获对数据库所做的所有更改,并自动将它们应用于搜索索引,而无需编写自定义应用程序代码。几乎任何类型的存储或索引系统都可以实现这种集成。
|
||||
|
||||
同样,能够更容易地预先计算和更新缓存将是一件好事。回想一下,物化视图本质上是一个预先计算的缓存,所以您可以通过为复杂查询声明指定物化视图来创建缓存,包括图上的递归查询(请参阅第49页上的“类图数据模型”)和应用逻辑。在这方面有一些有趣的早期研究,如差异数据流【24,25】,我希望这些想法能够在生产系统中找到自己的方法。
|
||||
同样,能够更容易地预先计算和更新缓存将是一件好事。回想一下,物化视图本质上是一个预先计算的缓存,所以您可以通过为复杂查询声明指定物化视图来创建缓存,包括图上的递归查询(请参阅第49页上的“[图数据模型]()”)和应用逻辑。在这方面有一些有趣的早期研究,如差异数据流【24,25】,我希望这些想法能够在生产系统中找到自己的方法。
|
||||
|
||||
### 围绕数据流设计应用
|
||||
|
||||
通过使用应用程序代码组成专门的存储和处理系统来分离数据库的方法也被称为“数据库内外”方法【26】,在2014年的一次会议演讲标题之后【27】。然而,称它为“新建筑”太宏大。我把它看作是一个设计模式,一个讨论的起点,我们只是简单地给它起一个名字,以便我们可以更好地谈论它。
|
||||
通过使用应用程序代码组成专门的存储和处理系统来分离数据库的方法也被称为“数据库内外”方法【26】,在2014年的一次会议演讲标题之后【27】。然而,称它为“新架构”太宏大。我把它看作是一个设计模式,一个讨论的起点,我们只是简单地给它起一个名字,以便我们可以更好地谈论它。
|
||||
|
||||
这些想法不是我的;他们只是我认为我们应该学习的其他人思想的融合。尤其是,数据流语言(如Oz 【28】和Juttle 【29】),功能反应式编程(FRP)语言(如Elm 【30,31】)和逻辑编程语言(如Bloom [ 32。 Jay Kreps 【7】提出了在这种背景下解绑的术语。
|
||||
|
||||
@ -261,14 +261,14 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
当一个数据集来自另一个数据集时,它会经历某种转换函数。例如:
|
||||
|
||||
* 辅助索引是一种具有直接转换函数的衍生数据集:对于基表中的每一行或文档,它挑选被索引的列或字段中的值,并按这些值排序(假设B - 树或SSTable索引,按键排序,如第3章所述)。
|
||||
* 辅助索引是一种具有直接转换函数的衍生数据集:对于基表中的每一行或文档,它挑选被索引的列或字段中的值,并按这些值排序(假设B树或SSTable索引,按键排序,如[第3章](ch3.md)所述)。
|
||||
* 通过应用各种自然语言处理功能(如语言检测,分词,词干或词汇化,拼写纠正和同义词识别)创建全文搜索索引,然后构建用于高效查找的数据结构(例如作为倒排索引)。
|
||||
* 在机器学习系统中,我们可以将模型视为通过应用各种特征提取和统计分析功能从训练数据中导出。当模型应用于新的输入数据时,模型的输出是从输入和模型(因此间接地从训练数据)中导出的。
|
||||
* 缓存通常包含将以用户界面(UI)显示的形式的数据聚合。因此填充缓存需要知道UI中引用的字段; UI中的更改可能需要更新缓存填充方式的定义以及重建缓存。
|
||||
* 缓存通常包含将以用户界面(UI)显示的形式的数据聚合。因此填充缓存需要知道UI中引用的字段;UI中的更改可能需要更新缓存填充方式的定义以及重建缓存。
|
||||
|
||||
辅助索引的衍生函数通常是必需的,因此它作为核心特性被构建到许多数据库中,您可以仅通过说CREATE INDEX来调用它。对于全文索引,常见语言的基本语言特征可能内置到数据库中,但更复杂的特征通常需要特定于域的调整。在机器学习中,特征工程是众所周知的特定于应用程序的特征,并且通常必须包含关于应用程序的用户交互和部署的详细知识【35】。
|
||||
辅助索引的衍生函数通常是必需的,因此它作为核心特性被构建到许多数据库中,您可以仅通过说`CREATE INDEX`来调用它。对于全文索引,常见语言的基本语言特征可能内置到数据库中,但更复杂的特征通常需要特定于域的调整。在机器学习中,特征工程是众所周知的特定于应用程序的特征,并且通常必须包含关于应用程序的用户交互和部署的详细知识【35】。
|
||||
|
||||
当创建衍生数据集的函数不是像创建二级索引那样的标准Cookie切割函数时,需要自定义代码来处理特定于应用程序的方面。而这个自定义代码是许多数据库难以抗争虽然关系数据库通常支持触发器,存储过程和用户定义的函数,它们可以用来在数据库中执行应用程序代码,但它们在数据库设计中已经有所反应了(请参阅“传输事件流”(第447页))。
|
||||
当创建衍生数据集的函数不是像创建二级索引那样的标准Cookie切割函数时,需要自定义代码来处理特定于应用程序的方面。而这个自定义代码是许多数据库难以抗争虽然关系数据库通常支持触发器,存储过程和用户定义的函数,它们可以用来在数据库中执行应用程序代码,但它们在数据库设计中已经有所反应了(请参阅“[传输事件流]()”(第447页))。
|
||||
|
||||
#### 应用程序代码和状态的分离
|
||||
|
||||
@ -283,21 +283,21 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
在这个典型的Web应用程序模型中,数据库充当一种可以通过网络同步访问的可变共享变量。应用程序可以读取和更新变量,并且数据库负责保持持久性,提供一些并发控制和容错。
|
||||
|
||||
但是,在大多数编程语言中,您无法订阅可变变量中的更改 - 您只能定期读取它。与电子表格不同,如果变量的值发生变化,变量的读者不会收到通知。 (您可以在自己的代码中实现这样的通知 - 这被称为观察者模式 - 但大多数语言没有将此模式作为内置功能。)
|
||||
但是,在大多数编程语言中,您无法订阅可变变量中的更改 —— 您只能定期读取它。与电子表格不同,如果变量的值发生变化,变量的读者不会收到通知。 (您可以在自己的代码中实现这样的通知 —— 这被称为观察者模式 —— 但大多数语言没有将此模式作为内置功能。)
|
||||
|
||||
数据库继承了这种可变数据的被动方法:如果你想知道数据库的内容是否发生了变化,通常你唯一的选择就是轮询(即定期重复你的查询)。 订阅更改只是刚刚开始出现的功能(请参阅第455页的“更改流的API支持”)。
|
||||
数据库继承了这种可变数据的被动方法:如果你想知道数据库的内容是否发生了变化,通常你唯一的选择就是轮询(即定期重复你的查询)。 订阅更改只是刚刚开始出现的功能(请参阅第455页的“[更改流的API支持]()”)。
|
||||
|
||||
#### 数据流:状态变化和应用程序代码之间的相互影响
|
||||
|
||||
从数据流的角度思考应用意味着重新谈判应用代码和状态管理之间的关系。我们不是将数据库视为被应用程序操纵的被动变量,而是更多地考虑状态,状态更改和处理它们的代码之间的相互作用和协作。应用程序代码通过在另一个地方触发状态更改来响应状态更改。
|
||||
|
||||
我们在第451页的“数据库和数据流”中看到了这一思路,我们讨论将数据库更改的日志作为我们可以指定的事件流处理。消息传递系统(如角色)(请参阅第136页的“消息传递数据流”)也具有响应事件的概念。早在20世纪80年代,元组空间模型探索表示分布式计算的过程,观察状态变化并对它们做出反应【38,39】。
|
||||
我们在第451页的“[数据库和数据流]()”中看到了这一思路,我们讨论将数据库更改的日志作为我们可以指定的事件流处理。消息传递系统(如角色)(请参阅第136页的“[消息传递数据流]()”)也具有响应事件的概念。早在20世纪80年代,元组空间模型探索表示分布式计算的过程,观察状态变化并对它们做出反应【38,39】。
|
||||
|
||||
如前所述,当触发器由于数据更改而触发时,或者次级索引更新以反映索引表中的更改时,数据库内部会发生类似的情况。分解数据库意味着将此想法应用于在主数据库之外创建衍生数据集:缓存,全文搜索索引,机器学习或分析系统。我们可以为此使用流处理和消息传递系统。
|
||||
|
||||
需要记住的重要一点是,维护衍生数据与传统设计消息传递系统的异步作业执行不同(请参阅第448页上的“与传统消息传递相比的日志”):
|
||||
需要记住的重要一点是,维护衍生数据与传统设计消息传递系统的异步作业执行不同(请参阅第448页上的“[与传统消息传递相比的日志]()”):
|
||||
|
||||
•在维护衍生数据时,状态更改的顺序通常很重要(如果多个视图是从事件日志衍生的,则需要按照相同的顺序处理事件,以便它们保持一致)。如第445页上的“确认和重新传递”中所述,许多消息代理在重新传送未确认消息时没有此属性。双重写入也被排除(请参阅第454页上的“保持系统同步”)。
|
||||
•在维护衍生数据时,状态更改的顺序通常很重要(如果多个视图是从事件日志衍生的,则需要按照相同的顺序处理事件,以便它们保持一致)。如第445页上的“[确认和重新传递]()”中所述,许多消息代理在重新传送未确认消息时没有此属性。双重写入也被排除(请参阅第454页上的“[保持系统同步]()”)。
|
||||
|
||||
•容错是导出数据的关键:仅丢失单个消息会导致衍生数据集永远与其数据源不同步。消息传递和衍生状态更新都必须可靠。例如,许多角色系统默认在内存中维护角色状态和消息,所以如果运行角色的机器崩溃,他们就会丢失。
|
||||
|
||||
@ -309,16 +309,16 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
将流操作符合并到数据流系统中与微服务方法有很多相似的特征【40】。但是,底层的通信机制是非常不同的:单向异步消息流而不是同步请求/响应交互。
|
||||
|
||||
除了第136页上的“消息传递数据流”中列出的优点(如更好的容错性),数据流系统还可以获得更好的性能。例如,假设客户正在购买以一种货币定价但以另一种货币支付的商品。为了执行货币转换,您需要知道当前的汇率。这个操作可以通过两种方式实现【40,41】:
|
||||
除了第136页上的“[消息传递数据流]()”中列出的优点(如更好的容错性),数据流系统还可以获得更好的性能。例如,假设客户正在购买以一种货币定价但以另一种货币支付的商品。为了执行货币转换,您需要知道当前的汇率。这个操作可以通过两种方式实现【40,41】:
|
||||
|
||||
1. 在微服务方法中,处理购买的代码可能会查询汇率服务或数据库以获取特定货币的当前汇率。
|
||||
2. 在数据流方法中,处理采购的代码将提前订阅汇率更新流,并在当地数据库发生更改时将当前汇率记录下来。处理采购时,只需查询本地数据库即可。
|
||||
|
||||
第二种方法已经将同步网络请求替换为对本地数据库进行查询的另一服务(即使在同一个进程中,该请求也可能在同一台机器上)[^ii]。数据流不仅方法更快,而且更稳健到另一项服务的失败。最快和最可靠的网络请求根本就没有网络请求!我们现在不使用RPC,而是在购买事件和汇率更新事件之间建立流联接(请参阅第473页的“流表联接(流增强)”)。
|
||||
第二种方法已经将同步网络请求替换为对本地数据库进行查询的另一服务(即使在同一个进程中,该请求也可能在同一台机器上)[^ii]。数据流不仅方法更快,而且更稳健到另一项服务的失败。最快和最可靠的网络请求根本就没有网络请求!我们现在不使用RPC,而是在购买事件和汇率更新事件之间建立流联接(请参阅第473页的“[流表联接(流增强)]()”)。
|
||||
|
||||
[^ii]: 在微服务方法中,您可以通过在处理购买的服务中本地缓存汇率来避免同步网络请求。 但是,为了使缓存保持新鲜,您需要定期轮询更新的汇率,或订阅更改流 - 这正是数据流方法中发生的情况。
|
||||
[^ii]: 在微服务方法中,您可以通过在处理购买的服务中本地缓存汇率来避免同步网络请求。 但是,为了使缓存保持新鲜,您需要定期轮询更新的汇率,或订阅更改流——这正是数据流方法中发生的情况。
|
||||
|
||||
加入是时间相关的:如果购买事件在稍后的时间点被重新处理,汇率将会改变。如果要重建原始输出,则需要获取原始购买时的历史汇率。无论您是查询服务还是订阅汇率更新流,您都需要处理这种时间依赖性(请参阅第475页的“连接的时间依赖性”)。
|
||||
加入是时间相关的:如果购买事件在稍后的时间点被重新处理,汇率将会改变。如果要重建原始输出,则需要获取原始购买时的历史汇率。无论您是查询服务还是订阅汇率更新流,您都需要处理这种时间依赖性(请参阅第475页的“[连接的时间依赖性]()”)。
|
||||
|
||||
订阅一系列更改,而不是在需要时查询当前状态,使我们更接近类似电子表格的计算模型:当某些数据发生更改时,依赖于此的所有衍生数据都可以快速更新。还有很多未解决的问题,例如围绕时间依赖连接等问题,但我认为围绕数据流想法构建应用程序是一个非常有希望的方向。
|
||||
|
||||
@ -326,7 +326,7 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
### 观察衍生数据状态
|
||||
|
||||
在抽象层面,上一节讨论的数据流系统为您提供了创建衍生数据集(例如搜索索引,物化视图和预测模型)并使其保持最新的过程。让我们称这个过程为写入路径:只要某些信息被写入系统,它可能会经历批处理和流处理的多个阶段,并且最终每个衍生数据集都会更新以合并写入的数据。图12-1显示了更新搜索索引的示例。
|
||||
在抽象层面,上一节讨论的数据流系统为您提供了创建衍生数据集(例如搜索索引,物化视图和预测模型)并使其保持最新的过程。让我们称这个过程为写入路径:只要某些信息被写入系统,它可能会经历批处理和流处理的多个阶段,并且最终每个衍生数据集都会更新以合并写入的数据。[图12-1](img/fig12-1.png)显示了更新搜索索引的示例。
|
||||
|
||||
![](img/fig12-1.png)
|
||||
|
||||
@ -334,9 +334,9 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
但为什么你首先创建衍生数据集?很可能是因为你想在以后再次查询它。这是读取路径:在提供从衍生数据集中读取的用户请求时,可能会对结果执行一些更多处理,然后构建对用户的响应。
|
||||
|
||||
总而言之,写入路径和读取路径涵盖了数据的整个旅程,从收集数据的地步到使用数据(可能是由另一个人)。写入路径是预先计算的行程的一部分 - 即,一旦数据进入,即刻完成,无论是否有人要求查看它。阅读路径是旅程中只有当有人要求时才会发生的部分。如果您熟悉函数式编程语言,则可能会注意到写入路径类似于急切的评估,读取路径类似于懒惰评估。
|
||||
总而言之,写入路径和读取路径涵盖了数据的整个旅程,从收集数据的地步到使用数据(可能是由另一个人)。写入路径是预先计算的行程的一部分 —— 即,一旦数据进入,即刻完成,无论是否有人要求查看它。阅读路径是旅程中只有当有人要求时才会发生的部分。如果您熟悉函数式编程语言,则可能会注意到写入路径类似于急切的评估,读取路径类似于懒惰评估。
|
||||
|
||||
如图12-1所示,衍生数据集是写入路径和读取路径相遇的地方。它代表了在写入时需要完成的工作量与在读取时需要完成的工作量之间的权衡。
|
||||
如[图12-1](img/fig12-1.png)所示,衍生数据集是写入路径和读取路径相遇的地方。它代表了在写入时需要完成的工作量与在读取时需要完成的工作量之间的权衡。
|
||||
|
||||
#### 物化视图和缓存
|
||||
|
||||
@ -370,7 +370,7 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
#### 将状态更改推送给客户端
|
||||
|
||||
在典型的网页中,如果您在Web浏览器中加载页面,并且随后服务器上的数据发生更改,则浏览器在重新加载页面之前不会查找有关更改。浏览器只能在一个时间点读取数据,假设它是静态的 - 它不会订阅来自服务器的更新。因此,设备上的状态是一个陈旧的缓存,除非您明确轮询更改,否则不会更新。 (像RSS这样的基于HTTP的订阅源订阅协议实际上只是一种基本的调查形式。)
|
||||
在典型的网页中,如果您在Web浏览器中加载页面,并且随后服务器上的数据发生更改,则浏览器在重新加载页面之前不会查找有关更改。浏览器只能在一个时间点读取数据,假设它是静态的 —— 它不会订阅来自服务器的更新。因此,设备上的状态是一个陈旧的缓存,除非您明确轮询更改,否则不会更新。 (像RSS这样的基于HTTP的订阅源订阅协议实际上只是一种基本的调查形式。)
|
||||
|
||||
更新的协议已经超越了HTTP的基本请求/响应模式:服务器发送的事件(EventSource API)和WebSockets提供了通信渠道,通过这些通信渠道,Web浏览器可以与服务器保持开放的TCP连接,服务器可以只要保持连接状态,就会主动将消息推送到浏览器。这为服务器提供了一个机会,主动通知最终用户客户端本地存储状态的任何变化,从而减少客户端状态的陈旧程度。
|
||||
|
||||
@ -380,13 +380,13 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
#### 端到端的事件流
|
||||
|
||||
最近用于开发有状态客户端和用户界面的工具(如Elm语言【30】和Facebook的React,Flux和Redux工具链)已经通过订阅表示用户的事件流来管理内部客户端状态输入或来自服务器的响应,其结构与事件源相似(请参阅第457页的“事件源”)。
|
||||
最近用于开发有状态客户端和用户界面的工具(如Elm语言【30】和Facebook的React,Flux和Redux工具链)已经通过订阅表示用户的事件流来管理内部客户端状态输入或来自服务器的响应,其结构与事件源相似(请参阅第457页的“[事件源]()”)。
|
||||
|
||||
将这种编程模型扩展为允许服务器将状态改变事件推送到客户端事件管道中是非常自然的。因此,状态变化可以通过端到端的写入路径流动:从触发状态改变的一个设备上的交互,通过事件日志以及通过多个衍生的数据系统和流处理器,一直到用户界面在另一台设备上观察状态的人。这些状态变化可以以相当低的延迟传播 - 比如说,在一秒内结束。
|
||||
将这种编程模型扩展为允许服务器将状态改变事件推送到客户端事件管道中是非常自然的。因此,状态变化可以通过端到端的写入路径流动:从触发状态改变的一个设备上的交互,通过事件日志以及通过多个衍生的数据系统和流处理器,一直到用户界面在另一台设备上观察状态的人。这些状态变化可以以相当低的延迟传播——比如说,在一秒内结束。
|
||||
|
||||
一些应用程序(如即时消息传递和在线游戏)已经具有这种“实时”体系结构(从低延迟的交互意义上说,不是“响应时间保证”在本页中的含义)。但为什么我们不用这种方式构建所有的应用程序?
|
||||
|
||||
挑战在于无状态客户端和请求/响应交互的假设在我们的数据库,库,框架和协议中非常深入。许多数据存储支持读取和写入操作,请求返回一个响应,但是少得多提供订阅更改的能力 - 即随着时间的推移返回响应流的请求(请参阅“更改流的API支持” 。
|
||||
挑战在于无状态客户端和请求/响应交互的假设在我们的数据库,库,框架和协议中非常深入。许多数据存储支持读取和写入操作,请求返回一个响应,但是少得多提供订阅更改的能力 —— 即随着时间的推移返回响应流的请求(请参阅“更改流的API支持” 。
|
||||
|
||||
为了将写入路径扩展到最终用户,我们需要从根本上重新思考我们构建这些系统的方式:从请求/响应交互转向发布/订阅数据流【27】。我认为更具响应性的用户界面和更好的离线支持的优势将使其值得付出努力。如果您正在设计数据系统,我希望您会记住订阅更改的选项,而不只是查询当前状态。
|
||||
|
||||
@ -394,36 +394,37 @@ unbundled方法遵循Unix传统的小型工具,它可以很好地完成一件
|
||||
|
||||
我们讨论过,当流处理器将衍生数据写入存储(数据库,缓存或索引)时,以及当用户请求查询该存储时,存储将充当写入路径和读取路径之间的边界。该商店允许对数据进行随机访问读取查询,否则这些查询将需要扫描整个事件日志。
|
||||
|
||||
在很多情况下,数据存储与流式传输系统是分开的。但请记住,流处理器还需要维护状态以执行聚合和连接(请参阅第472页的“流连接”)。这种状态通常隐藏在流处理器内部,但是一些框架允许它也被外部客户端查询【45】,将流处理器本身变成一种简单的数据库。
|
||||
在很多情况下,数据存储与流式传输系统是分开的。但请记住,流处理器还需要维护状态以执行聚合和连接(请参阅第472页的“[流连接]()”)。这种状态通常隐藏在流处理器内部,但是一些框架允许它也被外部客户端查询【45】,将流处理器本身变成一种简单的数据库。
|
||||
|
||||
我想进一步考虑这个想法。正如到目前为止所讨论的那样,对商店的写入是通过事件日志进行的,而读取是瞬时网络请求,直接进入存储被查询数据的节点。这是一个合理的设计,但不是唯一可能的设计。也可以将读取请求表示为事件流,并通过流处理器发送读取事件和写入事件;处理器通过将读取结果发送到输出流来响应读取事件【46】。
|
||||
|
||||
当写入和读取都被表示为事件,并且被路由到同一个流操作符以便处理时,我们实际上是在读查询流和数据库之间执行流表连接。读取事件需要发送到保存数据的数据库分区(请参阅第214页的“请求路由”),就像批处理和流处理器在连接时需要在同一个键上共同输入一样(请参阅“Reduce-Side连接和分组“)。
|
||||
当写入和读取都被表示为事件,并且被路由到同一个流操作符以便处理时,我们实际上是在读查询流和数据库之间执行流表连接。读取事件需要发送到保存数据的数据库分区(请参阅第214页的“[请求路由]()”),就像批处理和流处理器在连接时需要在同一个键上共同输入一样(请参阅“[Reduce端连接和分组]()“)。
|
||||
|
||||
服务请求和正在执行的连接之间的这种对应关系是非常重要的【47】。一次性读取请求只是通过连接运算符传递请求,然后立即忘记它;订阅请求是与连接另一端的过去和未来事件的持续连接。
|
||||
|
||||
记录读取事件的日志可能对于追踪整个系统中的因果关系和数据来源也有好处:它可以让您在做出特定决策之前重建用户看到的内容。例如,在网上商店,向客户显示的预测出货日期和库存状态可能影响他们是否选择购买物品【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】。
|
||||
|
||||
MPP数据库的内部查询执行图具有相似的特征(请参阅第417页的“比较Hadoop与分布式数据库”)。如果您需要执行这种多分区连接,使用提供此功能的数据库可能比使用流处理器实现它更简单。但是,将查询视为流提供了一个选项,可以实现大规模应用程序,这些应用程序可以在传统的现成解决方案的限制下运行。
|
||||
MPP数据库的内部查询执行图具有相似的特征(请参阅第417页的“[比较Hadoop与分布式数据库]()”)。如果您需要执行这种多分区连接,使用提供此功能的数据库可能比使用流处理器实现它更简单。但是,将查询视为流提供了一个选项,可以实现大规模应用程序,这些应用程序可以在传统的现成解决方案的限制下运行。
|
||||
|
||||
|
||||
|
||||
## 目标是正确性
|
||||
|
||||
对于只读取数据的无状态服务,出现问题时不会造成什么大问题:您可以修复该错误并重新启动服务,并且一切都恢复正常。像数据库这样的有状态的系统并不是那么简单:它们被设计成永远记住事物(或多或少),所以如果出现问题,效果也可能永远持续下去,这意味着它们需要更仔细的思考【50】。
|
||||
|
||||
我们希望构建可靠和正确的应用程序(即,即使面对各种故障,其语义也能很好地定义和理解的程序)。大约四十年来,原子性,隔离性和耐久性(第7章)的交易特性一直是构建正确应用的首选工具。但是,这些基础比看起来更弱:例如见证弱隔离级别的混合(请参见“弱隔离级别”(第233页))。
|
||||
我们希望构建可靠和正确的应用程序(即,即使面对各种故障,其语义也能很好地定义和理解的程序)。大约四十年来,原子性,隔离性和耐久性(第7章)的交易特性一直是构建正确应用的首选工具。但是,这些基础比看起来更弱:例如见证弱隔离级别的混合(请参见“[弱隔离级别]()”(第233页))。
|
||||
|
||||
在某些领域,事务被完全抛弃,并被提供更好性能和可伸缩性的模型取代,但是更复杂的语义(例如,请参阅第167页上的“无Leaderless复制”)。一致性经常被讨论,但定义不明确(参见第224页的“一致性”和第9章)。有些人主张我们应该“为了更好的可用性而拥抱弱一致性”,而对实际上的实际意义缺乏清晰的认识。
|
||||
在某些领域,事务被完全抛弃,并被提供更好性能和可伸缩性的模型取代,但是更复杂的语义(例如,请参阅第167页上的“[无主复制]()”)。一致性经常被讨论,但定义不明确(参见第224页的“[一致性]()”和[第9章](ch9.md))。有些人主张我们应该“为了更好的可用性而拥抱弱一致性”,而对实际上的实际意义缺乏清晰的认识。
|
||||
|
||||
对于如此重要的话题,我们的理解和我们的工程方法是惊人的片状。例如,确定在特定事务隔离级别或复制配置下运行特定应用程序是否安全是非常困难的【51,52】。通常简单的解决方案似乎在并发性低的情况下正常工作,并且没有错误,但是在要求更高的情况下会出现许多细微的错误。
|
||||
|
||||
@ -437,23 +438,23 @@ MPP数据库的内部查询执行图具有相似的特征(请参阅第417页
|
||||
|
||||
仅仅因为应用程序使用提供比较强的安全属性的数据系统(例如可序列化的事务),并不意味着应用程序可以保证没有数据丢失或损坏。例如,如果一个应用程序有一个错误导致它写入不正确的数据,或者从数据库中删除数据,那么可序列化的事务不会为你节省。
|
||||
|
||||
这个例子可能看起来很无聊,但值得认真对待:应用程序错误发生,人们犯错误。我在第459页的“状态,流和不可变性”中使用了这个例子来支持不可变和只能追加的数据,因为如果删除错误代码的能力来破坏好的数据,更容易从这些错误中恢复数据。
|
||||
这个例子可能看起来很无聊,但值得认真对待:应用程序错误发生,人们犯错误。我在第459页的“[状态,流和不可变性]()”中使用了这个例子来支持不可变和只能追加的数据,因为如果删除错误代码的能力来破坏好的数据,更容易从这些错误中恢复数据。
|
||||
|
||||
虽然不变性是有用的,但它本身并非万能的。让我们看看可能发生的数据损坏的一个更为简单的例子。
|
||||
|
||||
#### 正好执行一次操作
|
||||
|
||||
在第476页的“容错”中,我们遇到了一种精确调用一次(或有效一次)语义的想法。如果在处理消息时出现问题,您可以放弃(丢弃消息 - 即导致数据丢失)或再次尝试。如果再试一次,第一次就有成功的风险,但是你没有发现成功,所以这个消息最终被处理了两次。
|
||||
在第476页的“[容错]()”中,我们遇到了一种精确调用一次(或有效一次)语义的想法。如果在处理消息时出现问题,您可以放弃(丢弃消息 —— 即导致数据丢失)或再次尝试。如果再试一次,第一次就有成功的风险,但是你没有发现成功,所以这个消息最终被处理了两次。
|
||||
|
||||
处理两次是数据损坏的一种形式:对于相同的服务向客户收费两次(计费太多)或增加计数器两次(夸大一些度量)是不可取的。在这种情况下,正好一次就意味着安排计算,使得最终效果与没有发生错误的情况相同,即使操作实际上由于某种错误而被重试。我们以前讨论过实现这一目标的几种方法。
|
||||
|
||||
最有效的方法之一是使幂等操作(参见第478页的“幂等性”);即确保它具有相同的效果,无论是执行一次还是多次。但是,采取一种不自然是幂等的操作并使其具有幂等性需要付出一定的努力和关注:您可能需要维护一些额外的元数据(例如更新了值的操作ID集合),并在从一个节点到另一个节点(请参阅第295页上的“领导和锁定”)。
|
||||
最有效的方法之一是使幂等操作(参见第478页的“[幂等性]()”);即确保它具有相同的效果,无论是执行一次还是多次。但是,采取一种不自然是幂等的操作并使其具有幂等性需要付出一定的努力和关注:您可能需要维护一些额外的元数据(例如更新了值的操作ID集合),并在从一个节点到另一个节点(请参阅第295页上的“[领导和锁定]()”)。
|
||||
|
||||
#### 重复抑制
|
||||
|
||||
除了流处理之外,还需要抑制重复的相同模式出现在许多其他位置。例如,TCP使用数据包上的序列号将它们按正确的顺序排列在收件人处,并确定网络上是否有数据包丢失或重复。任何丢失的数据包都会被重新传输,并且在将数据交给应用程序之前,TCP堆栈会删除任何重复数据包。
|
||||
|
||||
但是,此重复抑制仅适用于单个TCP连接的上下文中。假设TCP连接是一个客户端与数据库的连接,并且它正在执行示例12-1中的事务。在许多数据库中,事务与客户端连接有关(如果客户端发送了多个查询,数据库就知道它们属于同一个事务,因为它们是在同一个TCP连接上发送的)。如果客户端在发送COMMIT之后但在从数据库服务器回听之前遇到网络中断和连接超时,则不知道事务是否已被提交或中止(图8-1)。
|
||||
但是,此重复抑制仅适用于单个TCP连接的上下文中。假设TCP连接是一个客户端与数据库的连接,并且它正在执行[例12-1]()中的事务。在许多数据库中,事务与客户端连接有关(如果客户端发送了多个查询,数据库就知道它们属于同一个事务,因为它们是在同一个TCP连接上发送的)。如果客户端在发送COMMIT之后但在从数据库服务器回听之前遇到网络中断和连接超时,则不知道事务是否已被提交或中止([图8-1](img/fig8-1.png))。
|
||||
|
||||
**例12-1 将资金从一个账户转移到另一个账户的非赦免**
|
||||
|
||||
@ -464,16 +465,16 @@ 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
|
||||
@ -487,15 +488,16 @@ 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】:
|
||||
|
||||
只有在通信系统端点的应用程序的知识和帮助下,所讨论的功能才能够完全正确地实现。因此,将这种被质疑的功能作为通信系统本身的功能是不可能的。 (有时,通信系统提供的功能的不完整版本可能有助于提高性能。)
|
||||
|
||||
在我们的例子中,有问题的函数是重复抑制。我们看到TCP在TCP连接级别抑制重复的数据包,一些流处理器在消息处理级别提供了所谓的唯一的语义,但是这不足以防止用户提交重复请求一次。 TCP,数据库事务和流处理器本身并不能完全排除这些重复。解决这个问题需要一个端到端的解决方案:从最终用户客户端一直传递到数据库的事务标识符。
|
||||
|
||||
端到端参数也适用于检查数据的完整性:以太网,TCP和TLS中内置的校验和可以检测网络中数据包的损坏情况,但是它们无法检测到发送和接收软件中的错误网络连接的末端,或数据存储在磁盘上的损坏。如果您想要捕获所有可能的数据损坏源,则还需要端到端的校验和。
|
||||
@ -508,11 +510,11 @@ COMMIT;
|
||||
|
||||
这使我回到我的原始论文:仅仅因为应用程序使用提供比较强的安全属性的数据系统(如可序列化事务),并不意味着应用程序保证不会丢失数据或损坏。应用程序本身也需要采取端到端的措施,例如重复压制。
|
||||
|
||||
这是一个耻辱,因为容错机制很难得到正确的。低级可靠性机制(如TCP中的可靠性机制)运行良好,因此其余较高级别的故障发生得相当少。将抽象中的高级容错机制封装起来非常好,以便应用程序代码不必担心它 - 但是我担心我们还没有找到合适的抽象。
|
||||
这是一个耻辱,因为容错机制很难得到正确的。低级可靠性机制(如TCP中的可靠性机制)运行良好,因此其余较高级别的故障发生得相当少。将抽象中的高级容错机制封装起来非常好,以便应用程序代码不必担心它 —— 但是我担心我们还没有找到合适的抽象。
|
||||
|
||||
长期以来,交易被认为是一个很好的抽象,我相信它们是有用的。正如第7章介绍中所讨论的那样,它们会涉及各种可能的问题(并发写入,约束违规,崩溃,网络中断,磁盘故障),并将其折叠为两种可能的结果:提交或中止。这是编程模型的一个巨大的简化,但我担心这是不够的。
|
||||
|
||||
事务处理非常昂贵,尤其是涉及异构存储技术时(请参阅第364页的“实践中的分布式事务”)。当我们拒绝使用分布式事务是因为它们太昂贵时,我们最终不得不在应用程序代码中重新实现容错机制。正如本书中大量的例子所显示的那样,关于并发性和部分失败的推理是困难且违反直觉的,所以我怀疑大多数应用程序级别的机制不能正常工作。结果是丢失或损坏的数据。
|
||||
事务处理非常昂贵,尤其是涉及异构存储技术时(请参阅第364页的“[实践中的分布式事务]()”)。当我们拒绝使用分布式事务是因为它们太昂贵时,我们最终不得不在应用程序代码中重新实现容错机制。正如本书中大量的例子所显示的那样,关于并发性和部分失败的推理是困难且违反直觉的,所以我怀疑大多数应用程序级别的机制不能正常工作。结果是丢失或损坏的数据。
|
||||
|
||||
出于这些原因,我认为值得探索的容错抽象方法能够容易地提供特定于应用程序的端到端正确性属性,而且还可以在大型分布式环境中保持良好的性能和良好的操作特性。
|
||||
|
||||
@ -520,39 +522,39 @@ COMMIT;
|
||||
|
||||
### 强制实施约束
|
||||
|
||||
让我们考虑非捆绑数据库的想法背景下的正确性(“剥离数据库”,第499页)。我们看到,使用从客户端一直传递到记录写入的数据库的请求标识可以实现端到端的重复压缩。其他类型的限制呢?
|
||||
让我们考虑非捆绑数据库的想法背景下的正确性(“[剥离数据库]()”,第499页)。我们看到,使用从客户端一直传递到记录写入的数据库的请求标识可以实现端到端的重复压缩。其他类型的限制呢?
|
||||
|
||||
特别是让我们关注唯一性约束 - 例如我们在例12-2中所依赖的约束。在第330页的“约束和唯一性保证”中,我们看到了几个其他需要强制实施唯一性的应用程序功能示例:用户名或电子邮件地址必须唯一标识用户,文件存储服务不能包含多个文件同名,两个人不能在航班或剧院预订同一个座位。
|
||||
特别是让我们关注唯一性约束 —— 例如我们在[例12-2]()中所依赖的约束。在第330页的“[约束和唯一性保证]()”中,我们看到了几个其他需要强制实施唯一性的应用程序功能示例:用户名或电子邮件地址必须唯一标识用户,文件存储服务不能包含多个文件同名,两个人不能在航班或剧院预订同一个座位。
|
||||
|
||||
其他类型的约束非常相似:例如,确保帐户余额永远不会变为负数,您不会出售比仓库中的库存更多的物料,或者会议室没有重复的预订。执行唯一性的技术通常也可以用于这些约束。
|
||||
|
||||
#### 唯一性约束需要达成共识
|
||||
|
||||
在第9章中我们看到,在分布式环境中,强制执行唯一性约束需要达成共识:如果存在多个具有相同值的并发请求,则系统需要决定哪个冲突操作被接受,并拒绝其他违规操作的约束。
|
||||
在[第9章](ch9.md)中我们看到,在分布式环境中,强制执行唯一性约束需要达成共识:如果存在多个具有相同值的并发请求,则系统需要决定哪个冲突操作被接受,并拒绝其他违规操作的约束。
|
||||
|
||||
达成这一共识的最常见方式是将单个节点作为领导者,并将其负责制定所有决策。只要您不介意通过单个节点发送所有请求(即使客户端位于世界的另一端),并且只要该节点没有失败,就可以正常工作。如果您需要容忍领导者失败,那么您又回到了共识问题(请参阅第367页上的“单领导表示和共识”)。
|
||||
达成这一共识的最常见方式是将单个节点作为领导者,并将其负责制定所有决策。只要您不介意通过单个节点发送所有请求(即使客户端位于世界的另一端),并且只要该节点没有失败,就可以正常工作。如果您需要容忍领导者失败,那么您又回到了共识问题(请参阅第367页上的“[单领导表示和共识]()”)。
|
||||
|
||||
唯一性检查可以根据需要唯一的值进行划分。例如,如果需要通过请求标识确保唯一性(如例12-2所示),则可以确保具有相同请求标识的所有请求都路由到同一分区(请参阅第6章)。如果您需要用户名是唯一的,您可以通过用户名的哈希分区。
|
||||
唯一性检查可以根据需要唯一的值进行划分。例如,如果需要通过请求标识确保唯一性(如[例12-2]()所示),则可以确保具有相同请求标识的所有请求都路由到同一分区(请参阅[第6章](ch6.md))。如果您需要用户名是唯一的,您可以通过用户名的哈希分区。
|
||||
|
||||
但是,排除了异步多主复制,因为它可能会导致不同的主设备同时接受冲突的写操作,因此这些值不再是唯一的(请参阅第295页的“实现可线性化系统”)。如果你想立即拒绝任何违反约束的写入,同步协调是不可避免的【56】。
|
||||
但是,排除了异步多主复制,因为它可能会导致不同的主设备同时接受冲突的写操作,因此这些值不再是唯一的(请参阅第295页的“[实现可线性化系统]()”)。如果你想立即拒绝任何违反约束的写入,同步协调是不可避免的【56】。
|
||||
|
||||
#### 基于日志的消息传递的唯一性
|
||||
|
||||
该日志确保所有消费者以相同的顺序查看消息 - 这种保证在形式上被称为全部命令广播并且等同于共识(参见第346页上的“全部命令广播”)。在使用基于日志的消息传递的非捆绑数据库方法中,我们可以使用非常类似的方法来执行唯一性约束。
|
||||
该日志确保所有消费者以相同的顺序查看消息 —— 这种保证在形式上被称为全部命令广播并且等同于共识(参见第346页上的“[全序广播]()”)。在使用基于日志的消息传递的非捆绑数据库方法中,我们可以使用非常类似的方法来执行唯一性约束。
|
||||
|
||||
流处理器在单个线程上依次占用日志分区中的所有消息(请参见第448页的“与传统消息传递相比的日志”)。因此,如果日志根据需要唯一的值进行分区,则流处理器可以明确并确定性地确定几个冲突操作中的哪一个先到达。例如,在多个用户尝试声明相同用户名的情况下【57】:
|
||||
流处理器在单个线程上依次占用日志分区中的所有消息(请参见第448页的“[与传统消息传递相比的日志]()”)。因此,如果日志根据需要唯一的值进行分区,则流处理器可以明确并确定性地确定几个冲突操作中的哪一个先到达。例如,在多个用户尝试声明相同用户名的情况下【57】:
|
||||
|
||||
1. 对用户名的每个请求都被编码为一条消息,并附加到由用户名散列确定的分区。
|
||||
2. 流处理器使用本地数据库连续读取日志中的请求,以跟踪使用哪些用户名。对于每个可用的用户名请求,它都会记录该名称并将成功消息发送到输出流。对于每个已经被使用的用户名请求,它都会向输出流发送拒绝消息。
|
||||
3. 请求用户名的客户端观察输出流并等待与其请求相对应的成功或拒绝消息。
|
||||
|
||||
该算法基本上与第363页上的“使用全阶广播实现线性化存储”中的算法相同。它可以通过增加分区数容易地扩展为较大的请求吞吐量,因为可以独立处理每个分区。
|
||||
该算法基本上与第363页上的“[使用全序广播实现线性化存储]()”中的算法相同。它可以通过增加分区数容易地扩展为较大的请求吞吐量,因为可以独立处理每个分区。
|
||||
|
||||
该方法不仅适用于唯一性约束,而且适用于许多其他类型的约束。其基本原理是任何可能冲突的写入都会路由到相同的分区并按顺序处理。正如第174页上的“什么是冲突?”和第246页上的“写入偏移和幻影”中所述,冲突的定义可能取决于应用程序,但流处理器可以使用任意逻辑来验证请求。这个想法与Bayou在20世纪90年代开创的方法类似【58】。
|
||||
该方法不仅适用于唯一性约束,而且适用于许多其他类型的约束。其基本原理是任何可能冲突的写入都会路由到相同的分区并按顺序处理。正如第174页上的“什么是冲突?”和第246页上的“[写入偏差和幻读]()”中所述,冲突的定义可能取决于应用程序,但流处理器可以使用任意逻辑来验证请求。这个想法与Bayou在20世纪90年代开创的方法类似【58】。
|
||||
|
||||
#### 多分区请求处理
|
||||
|
||||
当涉及多个分区时,确保操作以原子方式执行,同时满足约束条件变得更有趣。在示例12-2中,可能有三个分区:一个包含请求ID,一个包含收款人账户,另一个包含付款人账户。没有理由把这三件事放在同一个分区,因为它们都是相互独立的。
|
||||
当涉及多个分区时,确保操作以原子方式执行,同时满足约束条件变得更有趣。在示[例12-2]()中,可能有三个分区:一个包含请求ID,一个包含收款人账户,另一个包含付款人账户。没有理由把这三件事放在同一个分区,因为它们都是相互独立的。
|
||||
|
||||
在数据库的传统方法中,执行此事务需要跨所有三个分区进行原子提交,这实质上会将它强制为与任何这些分区上的所有其他事务的总顺序。由于现在存在跨分区协调,不能再独立处理不同的分区,因此吞吐量可能会受到影响。
|
||||
但是,事实证明,使用分区日志可以实现同等的正确性,并且不需要原子提交:
|
||||
@ -561,15 +563,15 @@ COMMIT;
|
||||
2. 流处理器读取请求的日志。对于每个请求消息,它发出两条消息以输出流:付款人账户A(由A分配)的借方指令和收款人账户B(由B分区)的信贷指令。原始的请求ID包含在那些发出的消息中。
|
||||
3. 其他处理器使用信用卡和借记指令流,通过请求ID进行扣除,并将更改应用于账户余额。
|
||||
|
||||
步骤1和步骤2是必要的,因为如果客户直接发送信用和借记指令,则需要在这两个分区之间进行原子提交以确保两者都不发生。为了避免分布式事务的需要,我们首先将请求永久记录为单条消息,然后从第一条消息中获取信用和借记指令。单对象写入在几乎所有数据系统中都是原子性的(请参阅“单对象写入”第213页),因此请求既可以出现在日志中,也可以不出现,而不需要多分区原子com-麻省理工学院。
|
||||
步骤1和步骤2是必要的,因为如果客户直接发送信用和借记指令,则需要在这两个分区之间进行原子提交以确保两者都不发生。为了避免分布式事务的需要,我们首先将请求永久记录为单条消息,然后从第一条消息中获取信用和借记指令。单对象写入在几乎所有数据系统中都是原子性的(请参阅“[单对象写入]()”第213页),因此请求既可以出现在日志中,也可以不出现,而不需要多分区原子com-麻省理工学院。
|
||||
|
||||
如果步骤2中的流处理器崩溃,则从上一个检查点恢复处理。这样做时,它不会跳过任何请求消息,但可能会多次处理请求并产生重复的信用和借记指令。但是,由于它是确定性的,因此它只会再次生成相同的指令,并且步骤3中的处理器可以使用端到端请求ID轻松地对它们进行重复数据删除。
|
||||
|
||||
如果您想确保付款人帐户不会因此次转账而透支,您可以额外使用流处理器(分区)使用多个不同分区的阶段的想法与我们所讨论的类似“多分区数据处理”一节第514页(另请参阅“并发控制”一节第462页)。
|
||||
如果您想确保付款人帐户不会因此次转账而透支,您可以额外使用流处理器(分区)使用多个不同分区的阶段的想法与我们所讨论的类似“多分区数据处理”一节第514页(另请参阅“[并发控制]()”一节第462页)。
|
||||
|
||||
### 及时性与完整性
|
||||
|
||||
事务的一个方便属性是它们通常是可线性化的(请参阅“可用性”),也就是说,一个作者等待事务提交,之后其所有读者立即可以看到它的写入。
|
||||
事务的一个方便属性是它们通常是可线性化的(请参阅“[可用性]()”),也就是说,一个作者等待事务提交,之后其所有读者立即可以看到它的写入。
|
||||
|
||||
在跨流处理器的多个阶段拆分操作时情况并非如此:日志的使用者在设计上是异步的,因此发送者不会等到其消息已经被消费者处理。但是,客户端可能会等待消息出现在输出流上。这是我们在检查是否满足唯一性约束时在“基于日志的消息传递中的唯一性”一节中所做的操作。
|
||||
|
||||
@ -579,18 +581,18 @@ COMMIT;
|
||||
|
||||
***及时性***
|
||||
|
||||
及时性意味着确保用户观察系统处于最新状态。我们之前看到,如果用户从数据的陈旧副本中读取数据,他们可能会以不一致的状态观察数据(请参阅第161页上的“复制滞后的问题”)。但是,这种不一致是暂时的,最终只能通过等待和再次尝试来解决。
|
||||
及时性意味着确保用户观察系统处于最新状态。我们之前看到,如果用户从数据的陈旧副本中读取数据,他们可能会以不一致的状态观察数据(请参阅第161页上的“[复制延迟问题]()”)。但是,这种不一致是暂时的,最终只能通过等待和再次尝试来解决。
|
||||
|
||||
CAP定理(参见第359页的“线性化成本”)使用线性化的意义上的一致性,这是实现及时性的强有力的方法。像写后读一致性这样的时效性较弱的属性(请参阅第162页的“读取自己写的内容”)也很有用。
|
||||
CAP定理(参见第359页的“[线性一致性的代价]()”)使用线性一致的意义上的一致性,这是实现及时性的强有力的方法。像写后读一致性这样的时效性较弱的属性(请参阅第162页的“[读己之写]()”)也很有用。
|
||||
|
||||
***完整性***
|
||||
诚信意味着没有腐败;即没有数据丢失,并且没有矛盾或错误的数据。尤其是,如果将某些衍生数据集作为某些基础数据的视图进行维护(请参阅“从事件日志导出当前状态”(第458页)),衍生必须正确。例如,数据库索引必须正确地反映数据库的内容 - 缺少某些记录的索引不是很有用。如果完整性受到侵犯,这种不一致是永久性的:在大多数情况下,等待并再次尝试不会修复数据库损坏。相反,需要明确的检查和修理。在ACID事务的上下文中(参见第223页上的“ACID的含义”),一致性通常被理解为某种特定于应用程序的完整性概念。原子性和耐久性是保持完整性的重要工具。
|
||||
诚信意味着没有腐败;即没有数据丢失,并且没有矛盾或错误的数据。尤其是,如果将某些衍生数据集作为某些基础数据的视图进行维护(请参阅“[从事件日志导出当前状态]()”(第458页)),衍生必须正确。例如,数据库索引必须正确地反映数据库的内容 —— 缺少某些记录的索引不是很有用。如果完整性受到侵犯,这种不一致是永久性的:在大多数情况下,等待并再次尝试不会修复数据库损坏。相反,需要明确的检查和修理。在ACID事务的上下文中(参见第223页上的“[ACID的含义]()”),一致性通常被理解为某种特定于应用程序的完整性概念。原子性和耐久性是保持完整性的重要工具。
|
||||
|
||||
口号形式:违反及时性是“最终一致性”,而违反诚信则是“永久不一致”。
|
||||
|
||||
我要断言,在大多数应用中,完整性比时间要重要得多。违反时效可能令人讨厌和混淆,但是对正直的侵犯可能是灾难性的。
|
||||
|
||||
例如,在您的信用卡对账单上,如果您在过去24小时内完成的交易尚未出现,这并不奇怪 - 这些系统有一定的滞后是正常的。我们知道银行协调和异步结算交易,并且这里的及时性并不重要【3】。但是,如果报表余额不等于交易总和加上先前的报表余额(数额错误),或者交易是向您收取但未支付给商家的话,那将是非常糟糕的(消失的金钱)。这样的问题会违反系统的完整性。
|
||||
例如,在您的信用卡对账单上,如果您在过去24小时内完成的交易尚未出现,这并不奇怪 —— 这些系统有一定的滞后是正常的。我们知道银行协调和异步结算交易,并且这里的及时性并不重要【3】。但是,如果报表余额不等于交易总和加上先前的报表余额(数额错误),或者交易是向您收取但未支付给商家的话,那将是非常糟糕的(消失的金钱)。这样的问题会违反系统的完整性。
|
||||
|
||||
#### 数据流系统的正确性
|
||||
|
||||
@ -598,16 +600,16 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
|
||||
|
||||
另一方面,我们在本章中讨论的基于事件的数据流系统的一个有趣特性是它们将时间性和完整性分开。在异步处理事件流时,不能保证及时性,除非在返回之前明确地构建等待消息到达的消费者。但是,完整性实际上是流式传输系统的核心。
|
||||
|
||||
一次或一次有效的语义(请参阅“故障容错”一节第437页)是一种保持完整性的机制。如果事件丢失,或者事件发生两次,数据系统的完整性可能被侵犯。因此,容错消息传递和重复抑制(例如,幂等操作)对于在面对故障时保持数据系统的完整性是重要的。
|
||||
一次或一次有效的语义(请参阅“[故障容错]()”一节第437页)是一种保持完整性的机制。如果事件丢失,或者事件发生两次,数据系统的完整性可能被侵犯。因此,容错消息传递和重复抑制(例如,幂等操作)对于在面对故障时保持数据系统的完整性是重要的。
|
||||
|
||||
正如我们在上一节看到的那样,可靠的流处理系统可以在不需要分布式事务和原子提交协议的情况下保持整体性,这意味着它们可以实现更好的可比较的正确性
|
||||
|
||||
性能和运行稳健性。我们通过以下机制的结合实现了这一完整性:
|
||||
|
||||
* 将写入操作的内容表示为单个消息,可以轻松地以原子方式编写 - 这种方法非常适合事件采购(请参阅第457页的“事件采购”)。
|
||||
* 使用确定性描述函数从该单个消息中获取所有其他状态更新,这与存储过程类似(请参见第252页的“实际的串行执行”和第479页的“作为衍生函数的应用程序代码”
|
||||
* 将写入操作的内容表示为单个消息,可以轻松地以原子方式编写 —— 这种方法非常适合事件采购(请参阅第457页的“事件采购”)。
|
||||
* 使用确定性描述函数从该单个消息中获取所有其他状态更新,这与存储过程类似(请参见第252页的“[真的串行执行]()”和第479页的“[作为衍生函数的应用程序代码]()”)
|
||||
* 通过所有这些级别的处理传递客户端生成的请求ID,启用端到端重复抑制和幂等性
|
||||
* 使消息不可变并允许衍生数据不时重新处理,这使得从错误中恢复变得更加容易(请参阅“不可变事件的优点”第367页)
|
||||
* 使消息不可变并允许衍生数据不时重新处理,这使得从错误中恢复变得更加容易(请参阅“[不可变事件的优点]()”第367页)
|
||||
|
||||
这种机制的组合在我看来是未来构建容错应用程序的一个非常有前途的方向。
|
||||
|
||||
@ -617,7 +619,7 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
|
||||
|
||||
然而,另一个要认识到的是,许多真正的应用程序实际上可以摆脱唯一性较弱的概念:
|
||||
|
||||
* 如果两个人同时注册相同的用户名或预订相同的座位,则可以发送其中一个消息来道歉,并要求他们选择不同的用户名。这种纠正错误的变化被称为补偿性交易【59,60】。
|
||||
* 如果两个人同时注册相同的用户名或预订相同的座位,则可以发送其中一个消息来道歉,并要求他们选择不同的用户名。这种纠正错误的变化被称为补偿性事务【59,60】。
|
||||
* 如果客户订购的物品多于仓库中的物品,则可以订购更多库存,为延误向客户道歉,并向他们提供折扣。实际上,如果叉车在仓库中的某些物品上方跑过来,而库存的物品数量比您想象的要少,那么您就必须这样做【61】。因此,无论如何,道歉工作流程已经需要成为业务流程的一部分,因此可能不需要对库存中的项目数量进行线性化约束。
|
||||
* 同样地,许多航空公司预计飞行员会错过飞机,许多旅馆超额预订客房,预计部分客人将取消预订。在这些情况下,出于商业原因故意违反了“每个座位一人”的约束,并且处理补偿过程(退款,升级,在邻近酒店提供免费房间)以处理需求超过供应的情况。即使没有超额预订,为了应对由于恶劣天气而被取消的航班或者罢工的员工,这些问题的恢复仅仅是商业活动的正常组成部分,就需要道歉和赔偿流程。
|
||||
* 如果有人收取比他们账户中更多的钱,银行可以向他们收取透支费用,并要求他们偿还欠款。通过限制每天的提款总额,银行的风险是有限的。
|
||||
@ -637,21 +639,21 @@ 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】。我只想指出,他们并没有超越可能性领域,所以他们值得关注。
|
||||
|
||||
@ -663,7 +665,7 @@ ACID事务通常既提供时间性(例如线性化)又提供完整性(例
|
||||
|
||||
当涉及到应用程序代码时,我们不得不承担更多的错误,因为大多数应用程序在数据库代码所做的评审和测试的数量上没有接近的地方。许多应用程序甚至没有正确使用数据库提供的用于保存完整性的功能,例如外键或唯一性约束【36】。
|
||||
|
||||
ACID意义上的一致性(请参阅第224页的“一致性”)基于数据库以一致状态启动并且事务将其从一个一致状态转换为另一个一致状态的想法。因此,我们期望数据库始终处于一致状态。但是,如果您认为该交易没有错误,则这个概念才有意义。如果应用程序错误地使用数据库以某种方式,例如,不安全地使用弱隔离级别,数据库的完整性无法得到保证。
|
||||
ACID意义上的一致性(请参阅第224页的“[一致性]()”)基于数据库以一致状态启动并且事务将其从一个一致状态转换为另一个一致状态的想法。因此,我们期望数据库始终处于一致状态。但是,如果您认为该交易没有错误,则这个概念才有意义。如果应用程序错误地使用数据库以某种方式,例如,不安全地使用弱隔离级别,数据库的完整性无法得到保证。
|
||||
|
||||
#### 不要盲目信任他们的承诺
|
||||
|
||||
@ -677,7 +679,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
#### 验证的文化
|
||||
|
||||
像HDFS和S3这样的系统仍然必须假定磁盘大部分时间都能正常工作 - 这是一个合理的假设,但假设它们始终正常工作并不相同。然而,目前还没有多少系统有这种“信任但是验证”的方式来持续审计自己。许多人认为正确性保证是绝对的,并且没有为罕见数据损坏的可能性做出规定。我希望将来我们会看到更多的自我验证或自我审计系统,不断检查自己的诚信,而不是依赖盲目的信任【68】。
|
||||
像HDFS和S3这样的系统仍然必须假定磁盘大部分时间都能正常工作 —— 这是一个合理的假设,但假设它们始终正常工作并不相同。然而,目前还没有多少系统有这种“信任但是验证”的方式来持续审计自己。许多人认为正确性保证是绝对的,并且没有为罕见数据损坏的可能性做出规定。我希望将来我们会看到更多的自我验证或自我审计系统,不断检查自己的诚信,而不是依赖盲目的信任【68】。
|
||||
|
||||
我担心ACID数据库的文化导致我们在盲目信任技术(如交易机制)的基础上开发应用程序,而忽视了这种过程中的任何可审计性。由于我们所信任的技术在大多数情况下工作得很好,审计机制并不值得投资。
|
||||
|
||||
@ -685,19 +687,19 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
#### 设计可审计性
|
||||
|
||||
如果一个事务在一个数据库中改变了多个对象,事实上很难说事实是什么意思。即使您捕获事务日志(请参阅第454页上的“更改数据捕获”),各种表中的插入,更新和删除操作并不一定清楚表明为何执行这些突变。决定这些突变的应用逻辑的调用是暂时的,不能被复制。
|
||||
如果一个事务在一个数据库中改变了多个对象,事实上很难说事实是什么意思。即使您捕获事务日志(请参阅第454页上的“[更改数据捕获]()”),各种表中的插入,更新和删除操作并不一定清楚表明为何执行这些突变。决定这些突变的应用逻辑的调用是暂时的,不能被复制。
|
||||
|
||||
相比之下,基于事件的系统可以提供更好的可审计性。在事件源方法中,系统的用户输入被表示为一个单一的不可变事件,并且任何结果状态更新都来自该事件。衍生可以做出确定性和可重复性,以便通过相同版本的衍生代码运行相同的事件日志将导致相同的状态更新。
|
||||
|
||||
清楚地看到数据流(请参阅第419页上的“批处理输出的原理”)可以使数据的来源更加清晰,从而使完整性检查更加可行。对于事件日志,我们可以使用散列来检查事件存储没有被破坏。对于任何衍生状态,我们可以重新运行从事件日志中衍生它的批处理和流处理器,以检查是否获得相同的结果,或者甚至并行运行冗余衍生。
|
||||
清楚地看到数据流(请参阅第419页上的“[批处理输出的原理]()”)可以使数据的来源更加清晰,从而使完整性检查更加可行。对于事件日志,我们可以使用散列来检查事件存储没有被破坏。对于任何衍生状态,我们可以重新运行从事件日志中衍生它的批处理和流处理器,以检查是否获得相同的结果,或者甚至并行运行冗余衍生。
|
||||
|
||||
确定性和定义良好的数据流也使调试和跟踪系统的执行变得容易,以确定它为什么做了某些事情【4,69】。如果出现意想不到的事情,那么具有诊断能力来重现导致意外事件的确切环境 - 一种时间行程调试功能是非常有价值的。
|
||||
确定性和定义良好的数据流也使调试和跟踪系统的执行变得容易,以确定它为什么做了某些事情【4,69】。如果出现意想不到的事情,那么具有诊断能力来重现导致意外事件的确切环境 —— 一种时间行程调试功能是非常有价值的。
|
||||
|
||||
#### 再次是端到端的论点
|
||||
|
||||
如果我们不能完全相信系统的每个组件都不会发生腐败 - 每一个硬件都是无错的,并且每一个软件都没有缺陷 - 那么我们至少必须定期检查数据的完整性。如果我们不检查,我们就不会发现腐败,直到它太晚了,并且已经造成了一些下游损害,在这一点上追踪这个问题将变得更加困难和昂贵。
|
||||
如果我们不能完全相信系统的每个组件都不会发生腐败 —— 每一个硬件都是无错的,并且每一个软件都没有缺陷 —— 那么我们至少必须定期检查数据的完整性。如果我们不检查,我们就不会发现腐败,直到它太晚了,并且已经造成了一些下游损害,在这一点上追踪这个问题将变得更加困难和昂贵。
|
||||
|
||||
检查数据系统的完整性最好是以端到端的方式完成(请参阅“数据库的端到端争论”页码:51):我们可以在完整性检查中包含的系统越多,那里的机会就越少在这个过程的某个阶段,腐败是被忽视的。如果我们可以检查整个衍生数据管道是端到端正确的,那么沿着路径的任何磁盘,网络,服务和算法都隐含在检查中。
|
||||
检查数据系统的完整性最好是以端到端的方式完成(请参阅“[数据库的端到端争论]()”,51页):我们可以在完整性检查中包含的系统越多,那里的机会就越少在这个过程的某个阶段,腐败是被忽视的。如果我们可以检查整个衍生数据管道是端到端正确的,那么沿着路径的任何磁盘,网络,服务和算法都隐含在检查中。
|
||||
|
||||
持续的端到端完整性检查可以提高您对系统正确性的信心,从而使您的移动速度更快【70】。与自动化测试一样,审计增加了发现错误的可能性,从而降低了系统更改或新存储技术造成损害的风险。如果您不害怕进行更改,您可以更好地开发应用程序以满足不断变化的需求。
|
||||
|
||||
@ -709,7 +711,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
我没有资格评论这些技术作为商定合同的货币或机制的优点。但是,从数据系统的角度来看,它们包含了一些有趣的想法。实质上,它们是分布式数据库,具有数据模型和事务机制,不同副本可以由互不信任的组织托管。复制品不断检查彼此的完整性,并使用共识协议来约定应执行的交易。
|
||||
|
||||
我对这些技术的拜占庭容错方面有些怀疑(参见第304页的“拜占庭故障”),而且我发现工作证明(比如比特币挖掘)技术非常浪费。比特币的交易吞吐量相当低,尽管出于政治和经济原因而不是技术交易。但是,完整性检查方面很有趣。
|
||||
我对这些技术的拜占庭容错方面有些怀疑(参见第304页的“[拜占庭故障]()”),而且我发现工作证明(比如比特币挖掘)技术非常浪费。比特币的交易吞吐量相当低,尽管出于政治和经济原因而不是技术交易。但是,完整性检查方面很有趣。
|
||||
|
||||
我可以想象完整性检查和审计算法(如证书透明度和分布式分类账算法)在一般数据系统中得到越来越广泛的应用。 一些工作将需要使它们具有同样的可扩展性,因为没有加密审计的系统,并且尽可能降低性能损失。 但我认为这是一个值得关注的领域。
|
||||
|
||||
@ -765,11 +767,11 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
当预测分析影响人们的生活时,特别是由于自我强化反馈循环而出现有害问题。例如,考虑雇主使用信用评分来评估潜在员工的情况。你可能是一个信誉好的好员工,但是由于你不能控制的不幸,你会突然发现自己陷入财务困境。由于您错过了账单付款,您的信用评分会受到影响,您将不太可能找到工作。失业使你陷入贫困,这进一步恶化了你的分数,使其更难找到工作【87】。由于有毒的假设,这是一个下降的螺旋,隐藏在数学严谨和数据伪装的背后。
|
||||
|
||||
我们不能总是预测这种反馈循环何时发生。然而,通过考虑整个系统(不仅仅是计算机化的部分,而且还有与之互动的人),可以预测许多后果 - 一种称为系统思维的方法【92】。我们可以尝试理解数据分析系统如何响应不同的行为,结构或特性。该系统是否加强和扩大了人们之间现有的差异(例如,使富人更穷或更穷),还是试图打击不公正?即使有最好的意图,我们也必须小心意想不到的后果。
|
||||
我们不能总是预测这种反馈循环何时发生。然而,通过考虑整个系统(不仅仅是计算机化的部分,而且还有与之互动的人),可以预测许多后果—— 一种称为系统思维的方法【92】。我们可以尝试理解数据分析系统如何响应不同的行为,结构或特性。该系统是否加强和扩大了人们之间现有的差异(例如,使富人更穷或更穷),还是试图打击不公正?即使有最好的意图,我们也必须小心意想不到的后果。
|
||||
|
||||
#### 隐私和跟踪
|
||||
|
||||
除了预测分析的问题 - 即使用数据来做出关于人的自动决策 - 数据收集本身也存在道德问题。收集数据的组织与正在收集数据的人之间有什么关系?
|
||||
除了预测分析的问题 —— 即使用数据来做出关于人的自动决策 —— 数据收集本身也存在道德问题。收集数据的组织与正在收集数据的人之间有什么关系?
|
||||
|
||||
当系统仅存储用户明确输入的数据时,系统会以特定方式存储和处理数据,系统正在为用户执行服务:用户就是客户。但是,当用户的活动被跟踪并记录为他们正在做的其他事情的副作用时,这种关系就不那么清晰了。该服务不再仅仅是用户告诉它做的事情,而是服务于它自己的利益,这可能与用户的兴趣相冲突。
|
||||
|
||||
@ -783,11 +785,11 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
作为一个思想实验,尝试用监视来替换单词数据,并观察常用短语是否听起来如此好【93】。这样如何:“在我们的监控驱动的组织中,我们收集实时监控流并将它们存储在我们的监控仓库中。我们的监控科学家使用高级分析和监测处理来获得新的见解。“
|
||||
|
||||
这个思想实验对于本书“设计监控 - 密集型应用程序”来说是异乎寻常的争论,但我认为需要强调的话来强调这一点。在我们制作软件“吃世界”的尝试中【94】,我们已经建立了世界上迄今为止所见过的最伟大的大众监视基础设施。我们正朝着物联网迈进,我们正在迅速接近这样一个世界:每个有人居住的空间至少包含一个互联网连接的麦克风,以智能手机,智能电视,语音控制助理设备,婴儿监视器甚至儿童玩具使用基于云的语音识别。这些设备中的很多都有可怕的安全记录【95】。
|
||||
这个思想实验对于本书“设计监控 —— 密集型应用程序”来说是异乎寻常的争论,但我认为需要强调的话来强调这一点。在我们制作软件“吃世界”的尝试中【94】,我们已经建立了世界上迄今为止所见过的最伟大的大众监视基础设施。我们正朝着物联网迈进,我们正在迅速接近这样一个世界:每个有人居住的空间至少包含一个互联网连接的麦克风,以智能手机,智能电视,语音控制助理设备,婴儿监视器甚至儿童玩具使用基于云的语音识别。这些设备中的很多都有可怕的安全记录【95】。
|
||||
|
||||
即使是极权主义和专制政权也只能梦想在每个房间放置一个麦克风,并强迫每个人不断地携带能够追踪其位置和动作的设备。然而,我们显然自愿地,甚至热心地投身于这个全面监视的世界。不同之处在于数据是由公司而不是由政府机构收集的【96】。
|
||||
|
||||
并不是所有的数据收集都必须符合监督的要求,但检查它们可以帮助我们理解我们与数据收集者的关系。为什么我们似乎很乐意接受企业的监督?也许你觉得你没有隐瞒 - 换句话说,你完全符合现有的权力结构,你不是被边缘化的少数派,你不必害怕迫害【97】。不是每个人都如此幸运。也许这是因为目的似乎是良性的 - 这不是强制性的,也不是强制性的,而只是更好的建议和更个性化的营销。但是,结合上一节中对预测分析的讨论,这种区分似乎不太清晰。
|
||||
并不是所有的数据收集都必须符合监督的要求,但检查它们可以帮助我们理解我们与数据收集者的关系。为什么我们似乎很乐意接受企业的监督?也许你觉得你没有隐瞒 —— 换句话说,你完全符合现有的权力结构,你不是被边缘化的少数派,你不必害怕迫害【97】。不是每个人都如此幸运。也许这是因为目的似乎是良性的 —— 这不是强制性的,也不是强制性的,而只是更好的建议和更个性化的营销。但是,结合上一节中对预测分析的讨论,这种区分似乎不太清晰。
|
||||
|
||||
我们已经看到与汽车追踪设备挂钩的汽车保险费以及取决于佩戴健身追踪设备的人的健康保险范围。当监视被用来确定在生活的重要方面如保险或就业等方面的东西时,它开始变得不那么温和。此外,数据分析可以揭示出令人惊讶的侵入性事物:例如,智能手表或健身追踪器中的运动传感器可用于以相当好的准确度计算出您正在输入的内容(例如密码)【98】。而分析算法只会变得更好。
|
||||
|
||||
@ -795,10 +797,10 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
我们可能会断言用户自愿选择使用跟踪其活动的服务,并且他们已同意服务条款和隐私政策,因此他们同意收集数据。我们甚至可以声称,用户正在接受有价值的服务,以换取所提供的数据,并且为了提供服务,跟踪是必要的。毫无疑问,社交网络,搜索引擎以及其他各种免费的在线服务对于用户来说都是有价值的,但是这个说法存在问题。
|
||||
|
||||
用户几乎不知道他们提供给我们的数据库的数据,或者它们如何保留和处理 - 而大多数隐私政策的作用更多的是模糊而不是照亮。如果不了解他们的数据会发生什么,用户无法给出任何有意义的同意。通常,来自一个用户的数据还说明关于不是该服务的用户并且没有同意任何条款的其他人的事情。我们在本书的这一部分讨论的衍生数据集(来自整个用户群的数据可能与行为跟踪和外部数据源相结合)恰恰是用户无法获得任何有意义理解的数据种类。
|
||||
用户几乎不知道他们提供给我们的数据库的数据,或者它们如何保留和处理 —— 而大多数隐私政策的作用更多的是模糊而不是照亮。如果不了解他们的数据会发生什么,用户无法给出任何有意义的同意。通常,来自一个用户的数据还说明关于不是该服务的用户并且没有同意任何条款的其他人的事情。我们在本书的这一部分讨论的衍生数据集(来自整个用户群的数据可能与行为跟踪和外部数据源相结合)恰恰是用户无法获得任何有意义理解的数据种类。
|
||||
而且,数据是通过单向过程从用户中提取出来的,而不是真正的互惠关系,而不是公平的价值交换。没有对话,用户无法选择他们提供的数据量以及他们收到的服务回报:服务和用户之间的关系是非常不对称和片面的。这些条款是由服务设置,而不是由用户【99】。
|
||||
|
||||
对于不同意监视的用户,唯一真正的选择就是不使用服务。但是这个选择也不是免费的:如果一项服务如此受欢迎以至于“被大多数人认为是基本社会参与的必要条件”【99】,那么指望人们选择退出这项服务是不合理的 - 使用它事实上是强制性的。例如,在大多数西方社会群体中,携带智能手机,使用Facebook进行社交以及使用Google查找信息已成为常态。特别是当一项服务具有网络效应时,人们选择不使用它会产生社会成本。
|
||||
对于不同意监视的用户,唯一真正的选择就是不使用服务。但是这个选择也不是免费的:如果一项服务如此受欢迎以至于“被大多数人认为是基本社会参与的必要条件”【99】,那么指望人们选择退出这项服务是不合理的 —— 使用它事实上是强制性的。例如,在大多数西方社会群体中,携带智能手机,使用Facebook进行社交以及使用Google查找信息已成为常态。特别是当一项服务具有网络效应时,人们选择不使用它会产生社会成本。
|
||||
|
||||
由于跟踪用户而拒绝使用服务,这只是少数拥有足够的时间和知识来了解其隐私政策的人员的一种选择,并且有可能错过社会参与或专业人士如果他们参与了服务,可能会出现机会。对于处境不太好的人来说,没有任何意义上的自由选择:监督变得不可避免。
|
||||
|
||||
@ -814,21 +816,21 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
即使特定用户无法从特定广告定位的人群中个人身份识别,他们已经失去了一些关于披露一些隐私信息的机构,例如他们是否患有某种疾病。决定根据个人喜好向谁透露什么的不是用户,而是公司以最大化利润为目标行使隐私权。
|
||||
|
||||
许多公司都有一个不被视为令人毛骨悚然的目标 - 避免了他们的数据收集的实际侵入性问题,而是专注于管理用户感知。即使这些看法经常被糟糕的管理:例如,事实可能是事实上的正确,但如果它触发痛苦的回忆,用户可能不希望被提醒它【100】。对于任何类型的数据,我们都应该期望在某种程度上出现错误,不可取或不适当的可能性,我们需要建立处理这些失败的机制。无论是“不可取的”还是“不适当的”,当然都是由人的判断决定的;除非我们明确地规划它们尊重人类的需求,否则算法会忽略这些概念。作为这些系统的工程师,我们必须谦虚,接受和规划这些失败。
|
||||
许多公司都有一个不被视为令人毛骨悚然的目标 —— 避免了他们的数据收集的实际侵入性问题,而是专注于管理用户感知。即使这些看法经常被糟糕的管理:例如,事实可能是事实上的正确,但如果它触发痛苦的回忆,用户可能不希望被提醒它【100】。对于任何类型的数据,我们都应该期望在某种程度上出现错误,不可取或不适当的可能性,我们需要建立处理这些失败的机制。无论是“不可取的”还是“不适当的”,当然都是由人的判断决定的;除非我们明确地规划它们尊重人类的需求,否则算法会忽略这些概念。作为这些系统的工程师,我们必须谦虚,接受和规划这些失败。
|
||||
|
||||
允许在线服务的用户控制其他用户可以看到的数据的哪些方面的隐私设置是将一些控制交还给用户的起点。但是,无论设置如何,服务本身仍然可以不受限制地访问数据,并且可以以隐私策略允许的任何方式自由使用它。即使服务承诺不会将数据出售给第三方,它通常会授予自己不受限制的权利,以在内部处理和分析数据,而且往往比用户明显看到的要多得多。
|
||||
|
||||
这种从个人到公司的大规模隐私权转移历史上是史无前例的【99】。监控一直存在,但它过去是昂贵和手动的,不可扩展和自动化。信托关系一直存在,例如患者与其医生之间,或被告与其律师之间 - 但在这些情况下,数据的使用严格受到道德,法律和监管限制的约束。互联网服务使得在没有有意义的同意的情况下收集大量敏感信息变得容易得多,并且在没有用户理解他们的私人数据正在发生的情况下大规模使用它。
|
||||
这种从个人到公司的大规模隐私权转移历史上是史无前例的【99】。监控一直存在,但它过去是昂贵和手动的,不可扩展和自动化。信托关系一直存在,例如患者与其医生之间,或被告与其律师之间 —— 但在这些情况下,数据的使用严格受到道德,法律和监管限制的约束。互联网服务使得在没有有意义的同意的情况下收集大量敏感信息变得容易得多,并且在没有用户理解他们的私人数据正在发生的情况下大规模使用它。
|
||||
|
||||
#### 数据作为资产和权力
|
||||
|
||||
由于行为数据是用户与服务交互的副产品,因此有时称为“数据耗尽” - 表明数据是毫无价值的浪费材料。从这个角度来看,行为和预测分析可以被看作是一种从数据中提取价值的回收形式,否则这些数据会被抛弃。
|
||||
由于行为数据是用户与服务交互的副产品,因此有时称为“数据耗尽” —— 表明数据是毫无价值的浪费材料。从这个角度来看,行为和预测分析可以被看作是一种从数据中提取价值的回收形式,否则这些数据会被抛弃。
|
||||
|
||||
更准确的说是反过来看:从经济的角度来看,如果有针对性的广告是为服务付费的话,那么关于人的行为数据就是服务的核心资产。在这种情况下,用户与之交互的应用程序仅仅是一种诱骗用户将更多的个人信息提供给监控基础设施的手段【99】。数据提取机器人讥讽地发现,在线服务中常常表现出令人愉快的人类创造力和社交关系。
|
||||
|
||||
个人数据是宝贵资产的说法得到了数据中介的支持,这个数据中介是一个隐蔽的行业,它主要是为了市场目的而采购,收集,分析,推断和转售侵入性个人数据[ 90。初创公司通过他们的用户数量,通过“眼球”,即通过他们的监视能力来估价。
|
||||
个人数据是宝贵资产的说法得到了数据中介的支持,这个数据中介是一个隐蔽的行业,它主要是为了市场目的而采购,收集,分析,推断和转售侵入性个人数据【90】。初创公司通过他们的用户数量,通过“眼球”,即通过他们的监视能力来估价。
|
||||
|
||||
因为数据很有价值,所以很多人都想要它。当然,公司需要它 - 这就是为什么他们收集它的原因。但政府也想获得它:通过秘密交易,胁迫,法律强制或者只是偷窃【101】。当公司破产时,收集到的个人资料就是被出售的资产之一。而且,数据难以确保,因此违规事件经常令人不安地发生【102】。
|
||||
因为数据很有价值,所以很多人都想要它。当然,公司需要它 —— 这就是为什么他们收集它的原因。但政府也想获得它:通过秘密交易,胁迫,法律强制或者只是偷窃【101】。当公司破产时,收集到的个人资料就是被出售的资产之一。而且,数据难以确保,因此违规事件经常令人不安地发生【102】。
|
||||
|
||||
这些观察已经导致批评者说数据不仅仅是一种资产,而是一种“有毒资产”【101】,或者至少是“有害物质”【103】。即使我们认为我们有能力防止滥用数据,但是每当我们收集数据时,我们都需要平衡这些好处和落入他们手中的风险:计算机系统可能会被犯罪分子或敌对的外国情报服务,数据可能会被内部人士泄露,公司可能会落入不合法的管理层之中,而这些管理层不会分享我们的价值观,或者这个国家可能会被毫无疑问迫使我们交出数据的政权所接管。
|
||||
|
||||
@ -844,7 +846,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
就像工业革命有一个黑暗的一面需要管理一样,我们对信息时代的转变也有我们需要面对和解决的重大问题。我相信收集和使用数据是其中的一个问题。用布鲁斯·施奈尔(Bruce Schneier)【96】的话来说:
|
||||
|
||||
> 数据是信息时代的污染问题,保护隐私是环境挑战。几乎所有的电脑都能生成信息。它停留在周围,溃烂。我们如何处理它 - 我们如何控制它以及如何处理它 - 对我们信息经济的健康至关重要。正如我们今天回顾工业时代的早期十年,并想知道我们的祖先在匆忙建立一个工业世界的过程中如何忽略污染,我们的孙辈在信息时代的前几十年将回顾我们,就我们如何应对数据收集和滥用的挑战来判断我们。
|
||||
> 数据是信息时代的污染问题,保护隐私是环境挑战。几乎所有的电脑都能生成信息。它停留在周围,溃烂。我们如何处理它 —— 我们如何控制它以及如何处理它 —— 对我们信息经济的健康至关重要。正如我们今天回顾工业时代的早期十年,并想知道我们的祖先在匆忙建立一个工业世界的过程中如何忽略污染,我们的孙辈在信息时代的前几十年将回顾我们,就我们如何应对数据收集和滥用的挑战来判断我们。
|
||||
>
|
||||
> 我们应该设法让他们感到骄傲。
|
||||
|
||||
@ -858,7 +860,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
从根本上说,我认为我们需要在个人数据方面进行科技行业的文化转变。我们应该停止将用户作为度量标准进行优化,并记住他们是值得尊重,尊严和代理的人。我们应该自我调节我们的数据收集和处理实践,以建立和维持依赖我们软件的人们的信任【111】。我们应该自己去教育最终用户如何使用他们的数据,而不是让他们处于黑暗中。
|
||||
|
||||
我们应该允许每个人保持他们的隐私 - 即他们控制自己的数据 - 而不是通过监视来窃取他们的控制权。我们控制数据的个人权利就像是一个国家公园的自然环境:如果我们没有明确的保护和关心,它将被破坏。这将是公地的悲剧,我们都会因此而变得更糟。无所不在的监视不是不可避免的,我们仍然能够阻止它。
|
||||
我们应该允许每个人保持他们的隐私 —— 即他们控制自己的数据 —— 而不是通过监视来窃取他们的控制权。我们控制数据的个人权利就像是一个国家公园的自然环境:如果我们没有明确的保护和关心,它将被破坏。这将是公地的悲剧,我们都会因此而变得更糟。无所不在的监视不是不可避免的,我们仍然能够阻止它。
|
||||
|
||||
我们究竟能做到这一点是一个悬而未决的问题。首先,我们不应该永久保留数据,但一旦不再需要就立即清除数据【111,112】。清除数据与不变性的想法背道而驰(请参阅第463页的“不变性的限制”),但可以解决该问题。我所看到的一种很有前途的方法是通过加密协议来实施访问控制,而不仅仅是通过策略【113,114】。总的来说,文化和态度的变化是必要的。
|
||||
|
||||
@ -890,8 +892,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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.
|
||||
“[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
|
||||
@ -911,8 +912,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
*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.
|
||||
“[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*.
|
||||
|
||||
@ -924,19 +924,16 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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:
|
||||
<a href="https://www.manning.com/books/big-data">*Big Data: Principles and Best Practices of
|
||||
Scalable Real-Time Data Systems*</a>. Manning, 2015. ISBN: 978-1-617-29034-3
|
||||
<a href="https://www.manning.com/books/big-data">*Big Data: Principles and Best Practices of Scalable Real-Time Data Systems*</a>. 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.
|
||||
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. 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.
|
||||
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.
|
||||
@ -959,8 +956,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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.
|
||||
“[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,
|
||||
@ -985,8 +981,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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:
|
||||
<a href="http://www.epsa.org/forms/uploadFiles/3B6300000000.filename.booksingle.pdf">*Concepts,
|
||||
Techniques, and Models of Computer Programming*</a>. MIT Press, 2004.
|
||||
<a href="http://www.epsa.org/forms/uploadFiles/3B6300000000.filename.booksingle.pdf">*Concepts, Techniques, and Models of Computer Programming*</a>. MIT Press, 2004.
|
||||
ISBN: 978-0-262-22069-9
|
||||
|
||||
1. “[Juttle Documentation](http://juttle.github.io/juttle/),” *juttle.github.io*, 2016.
|
||||
@ -1049,8 +1044,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
|
||||
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.
|
||||
“[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:
|
||||
@ -1063,8 +1057,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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.
|
||||
“[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.
|
||||
@ -1078,13 +1071,11 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
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.
|
||||
“[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.
|
||||
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.
|
||||
@ -1123,8 +1114,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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.
|
||||
“[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:
|
||||
@ -1169,8 +1159,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
[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.
|
||||
“[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:
|
||||
@ -1178,8 +1167,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
Security Symposium* (NDSS), February 2014.
|
||||
[doi:10.14722/ndss.2014.23379](http://dx.doi.org/10.14722/ndss.2014.23379)
|
||||
|
||||
1. “<a
|
||||
href="http://www.acm.org/about/se-code">Software Engineering Code of Ethics and Professional
|
||||
1. “<a href="http://www.acm.org/about/se-code">Software Engineering Code of Ethics and Professional
|
||||
Practice</a>,” Association for Computing Machinery, *acm.org*, 1999.
|
||||
|
||||
1. François Chollet:
|
||||
@ -1213,8 +1201,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
*idlewords.com*, June 2016.
|
||||
|
||||
1. Cathy O'Neil:
|
||||
<a href="https://weaponsofmathdestructionbook.com/">*Weapons of Math Destruction: How Big Data
|
||||
Increases Inequality and Threatens Democracy*</a>. Crown Publishing, 2016.
|
||||
<a href="https://weaponsofmathdestructionbook.com/">*Weapons of Math Destruction: How Big Data Increases Inequality and Threatens Democracy*</a>. Crown Publishing, 2016.
|
||||
ISBN: 978-0-553-41881-1
|
||||
|
||||
1. Julia Angwin:
|
||||
@ -1224,8 +1211,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[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. “[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,
|
||||
@ -1244,8 +1230,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
“[‘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:
|
||||
<a href="https://www.schneier.com/books/data_and_goliath/">*Data and Goliath: The Hidden Battles
|
||||
to Collect Your Data and Control Your World*</a>. W. W. Norton, 2015.
|
||||
<a href="https://www.schneier.com/books/data_and_goliath/">*Data and Goliath: The Hidden Battles to Collect Your Data and Control Your World*</a>. W. W. Norton, 2015.
|
||||
ISBN: 978-0-393-35217-7
|
||||
|
||||
1. The Grugq:
|
||||
@ -1294,8 +1279,7 @@ ACID意义上的一致性(请参阅第224页的“一致性”)基于数据
|
||||
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.
|
||||
“[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:
|
||||
|
Loading…
Reference in New Issue
Block a user