diff --git a/ddia/ch5.md b/ddia/ch5.md index acf8a8b5cb7782a4066fb594844f06878a34b12d..4d40f2e239b02e8fb6e576db7bc513d5f1b00aa2 100644 --- a/ddia/ch5.md +++ b/ddia/ch5.md @@ -90,7 +90,7 @@ 建立跟随者的实际步骤因数据库而异。在某些系统中,这个过程是完全自动化的,而在另外一些系统中,它可能是一个有点神秘的多步骤工作流程,需要由管理员手动执行。 -### 处理节点停机 +### 处理节点宕机 系统中的任何节点都可能停机,也许因为意外的故障,也可能是计划内的维护(例如,重启机器以安装内核安全补丁)。对运维而言,能在系统不中断服务的情况下重启单个节点好处多多。我们的目标是,即使个别节点失效,也能保持整个系统运行,并尽可能控制节点停机带来的影响。 @@ -213,7 +213,7 @@ PostgreSQL和Oracle等使用这种复制方法[16]。主要缺点是日志记录 **图5-3 用户写入后从旧副本中读取数据。需要写后读(read-after-write)的一致性来防止这种异常** -在这种情况下,我们需要**写后读一致性(read-after-write consistency)**,也称为**读己之写一致性(read-your-writes consistency)**[24]。这是一个保证,如果用户重新加载页面,他们总会看到他们自己提交的任何更新。它不会对其他用户的写入做出承诺:其他用户的更新可能稍等才会看到。它保证用户自己的输入已被正确保存。 +在这种情况下,我们需要**读写一致性(read-after-write consistency)**,也称为**读己之写一致性(read-your-writes consistency)**[24]。这是一个保证,如果用户重新加载页面,他们总会看到他们自己提交的任何更新。它不会对其他用户的写入做出承诺:其他用户的更新可能稍等才会看到。它保证用户自己的输入已被正确保存。 如何在基于领导者的复制系统中实现读后一致性?有各种可能的技术,这里说一些: diff --git a/ddia/ch7.md b/ddia/ch7.md index caf80970c41889db64581ff4e35ddfe23c6ae20c..a7fa30ca431b732d6213c9d8a249a10668b07199 100644 --- a/ddia/ch7.md +++ b/ddia/ch7.md @@ -10,48 +10,48 @@ [TOC] -在数据系统的残酷现实中,很多事情可能会出错: +在数据系统的残酷现实中,很多事情都可能出错: -- 数据库软件或硬件可能随时发生故障(包括写到一半时)。 -- 应用程序可能随时崩溃(包括一系列操作的中间)。 -- 网络中断可能会意外地切断来自数据库与应用的联系,或数据库之间的联系。 +- 数据库软件、硬件可能在任意时刻发生故障(包括写操作进行到一半时)。 +- 应用程序可能在任意时刻崩溃(包括一系列操作的中间)。 +- 网络中断可能会意外切断数据库与应用的连接,或数据库之间的连接。 - 多个客户端可能会同时写入数据库,覆盖彼此的更改。 -- 客户可能读取到无意义的数据,因为数据只是部分更新。 +- 客户端可能读取到无意义的数据,因为数据只更新了一部分。 - 客户之间的竞争条件可能导致令人惊讶的错误。 -为了可靠,系统必须处理这些故障并确保它们不会导致整个系统的灾难性故障。但是,实现容错机制工作量很大。需要仔细考虑所有可能出错的事情,并进行大量的测试以确保解决方案真正管用。 +为了实现可靠性,系统必须处理这些故障,确保它们不会导致整个系统的灾难性故障。但是实现容错机制工作量巨大。需要仔细考虑所有可能出错的事情,并进行大量的测试,以确保解决方案真正管用。 -数十年来,**事务(transaction)**一直是简化这些问题的首选机制。事务是应用程序将多个读取和写入组合成逻辑单元的一种方式。从概念上讲,事务中的所有读写都是作为一个操作执行的:整个事务成功(**提交(commit)**)或失败(**中止(abort)**,**回滚(rollback)**)。如果失败,应用程序可以安全地重试。对于事务来说,应用程序的错误处理变得简单多了,因为它不需要担心部分失败,即某些操作成功,有些失败(无论出于何种原因)的情况。 +数十年来,**事务(transaction)**一直是简化这些问题的首选机制。事务是应用程序将多个读写操作组合成一个逻辑单元的一种方式。从概念上讲,事务中的所有读写操作被视作单个操作来执行:整个事务要么成功(**提交(commit)**)要么失败(**中止(abort)**,**回滚(rollback)**)。如果失败,应用程序可以安全地重试。对于事务来说,应用程序的错误处理变得简单多了,因为它不用再担心部分失败的情况了,即某些操作成功,某些失败(无论出于何种原因)。 -和事务打交道时间长了,你可能会觉得它很明显。但我们不应该把它们视为理所当然。事务不是一种自然规律;它们是为了**简化应用编程模型**而创建的。通过使用事务,应用程序可以自由地忽略某些潜在的错误情况和并发问题,因为数据库会替它们处理好这些。(我们称之为**安全保证(safety guarantees)**)。 +和事务打交道时间长了,你可能会觉得它显而易见。但我们不应将其视为理所当然。事务不自然法;它们是为了**简化应用编程模型**而创建的。通过使用事务,应用程序可以自由地忽略某些潜在的错误情况和并发问题,因为数据库会替应用处理好这些。(我们称之为**安全保证(safety guarantees)**)。 -并不是所有的应用都需要事务,有时候弱化事务保证或完全放弃它们也是有利的(例如,为了获得更高的性能或更高的可用性)。可以在没有事务的情况下实现一些安全属性。 +并不是所有的应用都需要事务,有时候弱化事务保证、或完全放弃事务也是有好处的(例如,为了获得更高性能或更高可用性)。一些安全属性也可以在没有事务的情况下实现。 -你怎么知道你是否需要事务?为了回答这个问题,我们首先需要确切地理解事务可以提供的安全保障,以及与这些事务相关的成本。尽管乍一看事务似乎很简单,但实际上有许多微妙而重要的细节在发挥作用。 +怎样知道你是否需要事务?为了回答这个问题,首先需要确切理解事务可以提供的安全保障,以及它们的代价。尽管乍看事务似乎很简单,但实际上有许多微妙但重要的细节在起作用。 -在本章中,我们将研究许多可能出错的事例,并探讨数据库用于防范这些问题的算法。我们将在并发控制领域特别深入地讨论可能发生的各种竞争条件以及数据库如何实现**读已提交**,**快照隔离**和**可串行化**等隔离级别。 +本章将研究许多出错案例,并探索数据库用于防范这些问题的算法。尤其会深入**并发控制**的领域,讨论各种可能发生的竞争条件,以及数据库如何实现**读已提交**,**快照隔离**和**可串行化**等隔离级别。 -本章同时适用于单点与分布式数据库;在第8章中,我们将重点讨论仅在分布式系统中出现的特殊挑战。 +本章同时适用于单机数据库与分布式数据库;在[第8章](ch8.md)中将重点讨论仅出现在分布式系统中的特殊挑战。 ## 事务的棘手概念 -现在几乎所有的关系数据库和一些非关系数据库都支持事务处理。他们中的大多数遵循IBM系统R(第一个SQL数据库)在1975年引入的风格[1,2,3]。尽管一些实现细节已经改变,但总体思路在40年中几乎保持不变:MySQL,PostgreSQL,Oracle,SQL Server等中的事务支持与系统R出乎寻常地相似。 +现今,几乎所有的关系型数据库和一些非关系数据库都支持**事务**。其中大多数遵循IBM System R(第一个SQL数据库)在1975年引入的风格【1,2,3】。40年里,尽管一些实现细节发生了变化,但总体思路大同小异:MySQL,PostgreSQL,Oracle,SQL Server等数据库中的事务支持与System R异乎寻常地相似。 -在二十一世纪末期,非关系(NoSQL)数据库开始普及。它们的目标是通过提供新的数据模型选择(参见第2章),并通过默认包含复制(第5章)和分区(第6章)来改善关系现状。事务是这种运动的主要原因:这些新一代数据库中的许多数据库完全放弃了事务,或者重新定义了这个词,描述比以前理解所更弱的一套保证[4]。 +2000年以后,非关系(NoSQL)数据库开始普及。它们的目标是通过提供新的数据模型选择(参见第2章),并通过默认包含复制(第5章)和分区(第6章)来改善关系现状。事务是这种运动的主要原因:这些新一代数据库中的许多数据库完全放弃了事务,或者重新定义了这个词,描述比以前理解所更弱的一套保证【4】。 -随着这种新型分布式数据库的炒作,人们普遍认为事务是可扩展性的对立面,任何大型系统都必须放弃事务以保持良好的性能和高可用性[5, 6]。另一方面,数据库厂商有时将事务保证作为“重要应用”和“有价值数据”的基本要求。这两种观点都是**纯粹的夸张**。 +随着这种新型分布式数据库的炒作,人们普遍认为事务是可扩展性的对立面,任何大型系统都必须放弃事务以保持良好的性能和高可用性【5,6】。另一方面,数据库厂商有时将事务保证作为“重要应用”和“有价值数据”的基本要求。这两种观点都是**纯粹的夸张**。 事实并非如此简单:与其他技术设计选择一样,事务有其优势和局限性。为了理解这些权衡,让我们了解事务所提供保证的细节——无论是在正常运行中还是在各种极端(但是现实存在)情况下。 ### ACID的含义 -事务所提供的安全保证通常由众所周知的首字母缩略词ACID来描述,ACID代表**原子性(Atomicity)**,**一致性(Consistency)**,**隔离性(Isolation)**和**持久性(Durability)**。它由TheoHärder和Andreas Reuter于1983年创建,旨在为数据库中的容错机制建立精确的术语。 +事务所提供的安全保证,通常由众所周知的首字母缩略词ACID来描述,ACID代表**原子性(Atomicity)**,**一致性(Consistency)**,**隔离性(Isolation)**和**持久性(Durability)**。它由TheoHärder和Andreas Reuter于1983年创建,旨在为数据库中的容错机制建立精确的术语。 -但实际上,不同数据库的ACID实现并不相同。例如,我们将会看到,围绕着**隔离(Isolation)**的含义有许多含糊不清[8]。高层次上的想法是合理的,但魔鬼隐藏在细节中。今天,当一个系统声称自己“符合ACID”时,实际上能期待的是什么保证并不清楚。不幸的是,ACID现在几乎已经变成了一个营销术语。 +但实际上,不同数据库的ACID实现并不相同。例如,我们将会看到,围绕着**隔离性(Isolation)**的含义有许多含糊不清【8】。高层次上的想法是合理的,但魔鬼隐藏在细节里。今天,当一个系统声称自己“符合ACID”时,实际上能期待的是什么保证并不清楚。不幸的是,ACID现在几乎已经变成了一个营销术语。 -(不符合ACID标准的系统有时被称为BASE,它代表**基本可用性(Basically Available)**,**软状态(Soft State)**和**最终一致性(Eventual consistency)**[9],这比ACID的定义更加模糊,似乎BASE的唯一合理的定义是“不是ACID”,即它几乎可以代表任何你想要的东西。) +(不符合ACID标准的系统有时被称为BASE,它代表**基本可用性(Basically Available)**,**软状态(Soft State)**和**最终一致性(Eventual consistency)**【9】,这比ACID的定义更加模糊,似乎BASE的唯一合理的定义是“不是ACID”,即它几乎可以代表任何你想要的东西。) 让我们深入了解原子性,一致性,隔离性和持久性的定义,这可以让我们提炼出事务的思想。 @@ -59,22 +59,22 @@ 一般来说,原子是指不能分解成小部分的东西。这个词在计算的不同分支中意味着相似但又微妙不同的东西。例如,在多线程编程中,如果一个线程执行一个原子操作,这意味着另一个线程无法看到该操作的一半结果。系统只能处于操作之前或操作之后的状态,而不是介于两者之间的状态。 -相比之下,ACID的原子性并**不**是关于**并发(concurrent)**的。它并不是在描述如果几个进程试图同时访问相同的数据会发生什么情况,这种情况包含在I中,即隔离(Isolation)(请参见“隔离”(第195页))。 +相比之下,ACID的原子性并**不**是关于**并发(concurrent)**的。它并不是在描述如果几个进程试图同时访问相同的数据会发生什么情况,这种情况包含在缩写***I***中,即[**隔离性(Isolation)**](#隔离性(Isolation)) ACID的原子性描述了,当客户想进行多次写入,但在一些写入处理完之后出现故障的情况。例如进程崩溃,网络连接中断,磁盘变满或者某种完整性约束被违反。如果这些写入被分组到一个原子事务中,并且该事务由于错误而不能完成(提交),则该事务将被中止,并且数据库必须丢弃或撤消该事务中迄今为止所做的任何写入。 如果没有原子性,在多处更改进行到一半时发生错误,很难知道哪些更改已经生效,哪些没有生效。该应用程序可以再试一次,但冒着进行两次相同变更的风险,可能会导致数据重复或错误的数据。原子性简化了这个问题:如果事务被**中止(abort)**,应用程序可以确定它没有改变任何东西,所以可以安全地重试。 -ACID原子性的定义特征是:能够在错误中止事务,并丢弃该事务所有变更的能力。或许**可中止性(abortability)**将是一个比原子性更好的术语,但是我们将坚持使用原子性,因为这是通常使用的词。 +ACID原子性的定义特征是:能够在错误时中止事务,丢弃该事务进行的所有写入变更的能力。也许**可终止性(abortability)**是更好的术语,但本书将继续使用原子性,因为这是惯用词。 #### 一致性(Consistency) 一致性这个词重载的很厉害: -* 在第5章中,我们讨论了副本一致性以及在异步复制系统中出现的最终一致性问题(请参阅第161页上的“复制滞后问题”)。 -* 一致性哈希是某些系统用于重新分区的一种分区方法(请参阅“一致性散列”第191页)。 -* 在CAP定理(参见第9章)中,一致性一词用于表示可线性化(请参见“线性化”(第295页))。 -* 在ACID的情况下,**一致性**是指数据库在应用程序的特定概念中处于“良好状态”。 +* 在[第5章](ch5.md)中,我们讨论了副本一致性,以及异步复制系统中的最终一致性问题(参阅“[复制延迟问题](ch5.md#复制延迟问题)”)。 +* **一致性散列(Consistency Hash)**是某些系统用于重新分区的一种分区方法(参阅“[一致性散列](ch6.md#一致性散列)”)。 +* 在CAP定理(参见[第9章](ch9.md#CAP定理))中,一致性一词用于表示**可线性化**(参见“[线性化](ch9.md#线性化)”)。 +* 在ACID的上下文中,**一致性**是指数据库在应用程序的特定概念中处于“良好状态”。 不幸的是,同一个词至少有四种不同的含义。 @@ -86,27 +86,27 @@ ACID一致性的概念是,对数据的一组始终为真的特定陈述。即* [^i]: 乔·海勒斯坦(Joe Hellerstein)指出,在海德尔和路透社的论文中,“ACID中的C”是被“扔进去凑缩写单词的”,而且那时候C并没有被认为很重要。 -#### 隔离(Isolation) +#### 隔离性(Isolation) 大多数数据库会同时被多个客户端访问。如果它们各自读写数据库的不同部分,这是没有问题的,但是如果它们正在访问相同的数据库记录,则可能会遇到**并发**问题(**竞争条件(race conditions)**)。 -图7-1是这类问题的一个简单例子。假设你有两个客户同时增加一个存储在数据库中的计数器。每个客户端需要读取当前值,加1再回写新值(假设数据库中没有自增操作)。在图7-1中,计数器应该从42增至44,因为发生了两次自增,但由于竞态条件,实际上只到了43。 +[图7-1](img/fig7-1.png)是这类问题的一个简单例子。假设你有两个客户同时增加一个存储在数据库中的计数器。每个客户端需要读取当前值,加1再回写新值(假设数据库中没有自增操作)。在[图7-1](img/fig7-1.png)中,计数器应该从42增至44,因为发生了两次自增,但由于竞态条件,实际上只到了43。 -ACID意义上的隔离意味着同时执行的事务是相互隔离的:它们不能踩到彼此的脚。传统的数据库教科书将隔离形式化为**可序列化(Serializability)**,这意味着每个事务可以假装它是在整个数据库上运行的唯一事务。数据库确保当事务已经提交时,结果与它们按顺序运行(一个接一个)是一样的,尽管实际上它们可能是并发运行的[10]。 +ACID意义上的隔离性意味着,同时执行的事务是相互隔离的:它们不能踩到彼此的脚。传统的数据库教科书将隔离性形式化为**可序列化(Serializability)**,这意味着每个事务可以假装它是唯一在整个数据库上运行的事务。数据库确保当事务已经提交时,结果与它们按顺序运行(一个接一个)是一样的,尽管实际上它们可能是并发运行的【10】。 ![](img/fig7-1.png) -**图7-1 两个客户之间的竞争状态同时递增计数器。** +**图7-1 两个客户之间的竞争状态同时递增计数器** -然而在实践中,很少会使用可序列化隔离,因为它会带来性能损失。一些流行的数据库,如Oracle 11g,甚至没有实现它。在Oracle中有一个名为“serializable”的隔离级别,但实际上它实现了一种叫做**快照隔离**的功能,这是一种比可序列化更弱的保证[8,11]。我们将在第233页的“[弱隔离等级]()”中探索快照隔离和其他形式的隔离。 +然而在实践中,很少会使用可序列化隔离,因为它会带来性能损失。一些流行的数据库,如Oracle 11g,甚至没有实现它。在Oracle中有一个名为“serializable”的隔离级别,但实际上它实现了一种叫做**快照隔离**的功能,这是一种比可序列化更弱的保证【8,11】。我们将在第233页的“[弱隔离等级]()”中探索快照隔离和其他形式的隔离。 #### 持久性(Durability) -数据库系统的目的是提供一个安全的地方,可以存储数据而不用担心丢失数据。Durability是一个承诺,即一旦事务成功完成,即使存在硬件故障或数据库崩溃,所写的任何数据也不会被遗忘。 +数据库系统的目的是提供一个安全的地方,可以存储数据而不用担心丢失数据。持久性是一个承诺,即一旦事务成功完成,即使存在硬件故障或数据库崩溃,所写的任何数据也不会被遗忘。 -在单节点数据库中,持久性通常意味着数据已被写入非易失性存储设备,如硬盘驱动器或SSD。它通常还包括预写日志或类似的文件(请参阅第77页的“[使B树可靠]()”),以便在磁盘上的数据结构损坏时进行恢复。在有复制的数据库中,可用性可能意味着数据已成功复制到一些节点。为了提供持久性保证,数据库必须等到这些写入或复制完成后才能报告事务成功提交。 +在单节点数据库中,持久性通常意味着数据已被写入非易失性存储设备,如硬盘驱动器或SSD。它通常还包括预写日志或类似的文件(参阅“[让B树更可靠](ch3.md#让B树更可靠)”),以便在磁盘上的数据结构损坏时进行恢复。在有复制的数据库中,可用性可能意味着数据已成功复制到一些节点。为了提供持久性保证,数据库必须等到这些写入或复制完成后才能报告事务成功提交。 -如第6页的“可靠性”中所述,完美的持久性不存在:如果所有硬盘和所有备份同时被销毁,那么显然没有任何数据库能拯救你了。 +如“[可靠性](ch1.md#可靠性)”一节所述,完美的持久性是不存在的:如果所有硬盘和所有备份同时被销毁,那显然没有任何数据库能拯救你。 > #### 复制和持久性 > @@ -117,13 +117,13 @@ ACID意义上的隔离意味着同时执行的事务是相互隔离的:它们 > 真相是,没有什么是完美的: > > * 如果您写入磁盘并且机器死机,即使您的数据没有丢失,在您修复机器或将磁盘传输到另一台机器之前,也无法访问。复制的系统可以保持可用。 -> * 一个相关的故障(停电或一个在特定输入时导致所有节点崩溃的Bug)可能会一次性删除所有副本(请参阅第6页的「可靠性」),丢失任何仅存在内存中的数据。因此内存数据库仍然与磁盘写入相关。 -> * 在异步复制系统中,当主库不可用时,最近的写入操作可能会丢失(请参阅第156页的「处理节点中断」)。 -> * 当电源突然断电时,特别是固态硬盘被证明有时会违反应有的保证:甚至fsync也不能保证正常工作[12]。磁盘固件可能有错误,就像任何其他类型的软件一样[13,14]。 -> * 存储引擎和文件系统之间的微妙交互可能会导致难以追踪的错误,并可能导致磁盘上的文件在崩溃后被损坏[15,16]。 -> * 磁盘上的数据可能会逐渐被破坏而不会被检测到[17]。如果数据已损坏一段时间,副本和最近的备份也可能损坏。在这种情况下,您将需要尝试从历史备份中恢复数据。 -> * 一项关于固态硬盘的研究发现,在运行的前四年,30%到80%的硬盘至少发生一个坏块[18]。磁性硬盘驱动器的坏道率较低,但比SSD更高的完全故障率。 -> * 如果SSD断电,可能会在几周内开始丢失数据,具体取决于温度[19]。 +> * 一个相关的故障(停电或一个在特定输入时导致所有节点崩溃的Bug)可能会一次性删除所有副本(请参阅「[可靠性](ch1.md#可靠性)」),丢失任何仅存在内存中的数据。因此内存数据库仍然与磁盘写入相关。 +> * 在异步复制系统中,当主库不可用时,最近的写入操作可能会丢失(参阅第156页的「[处理节点宕机](ch5.md#处理节点宕机)」)。 +> * 当电源突然断电时,特别是固态硬盘被证明有时会违反应有的保证:甚至fsync也不能保证正常工作【12】。磁盘固件可能有错误,就像任何其他类型的软件一样【13,14】。 +> * 存储引擎和文件系统之间的微妙交互可能会导致难以追踪的错误,并可能导致磁盘上的文件在崩溃后被损坏【15,16】。 +> * 磁盘上的数据可能会逐渐破损而不会被检测到【17】。如果数据已损坏一段时间,副本和最近的备份也可能损坏。在这种情况下,您将需要尝试从历史备份中恢复数据。 +> * 一项关于固态硬盘的研究发现,在运行的前四年,30%到80%的硬盘至少发生一个坏块【18】。磁性硬盘驱动器的坏道率较低,但比SSD更高的完全故障率。 +> * 如果SSD断电,可能会在几周内开始丢失数据,具体取决于温度【19】。 > > 在实践中,没有一种技术可以提供绝对的保证。只有各种降低风险的技术,包括写入磁盘,复制到远程机器和备份——它们可以并且应该一起使用。与往常一样,最好抱着怀疑的态度采纳任何理论上的“保证” @@ -135,7 +135,7 @@ ACID意义上的隔离意味着同时执行的事务是相互隔离的:它们 如果在一系列写操作的中途发生错误,则应中止事务处理,并丢弃当前事务的所有写入。换句话说,数据库免去了用户对部分失败的担忧——通过提供“要么不做,要做全做”的保证。 -***隔离*** +***隔离性*** 同时运行的事务不应该互相干扰。例如,如果一个事务进行多次写入,则另一个事务应该看到全部或者全部写入,而不是一些子集。 @@ -147,15 +147,15 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true 但是,如果电子邮件太多,您可能会发现这个查询太慢,并决定将未读邮件的数量存储在一个单独的字段(一种规范化)中。现在,每当一个新的消息进来,你也必须增加未读的计数器,每当一个消息被标记为已读,你也必须减少未读的计数器。 -在图7-2中,用户2遇到异常情况:邮箱列表显示未读消息,但计数器显示零未读消息,因为计数器增量还没有发生.ii隔离将通过确保用户2看到插入的电子邮件和更新的计数器,或者都不是,但不是一个不一致的中间点。 +在[图7-2](img/fig7-2.png)中,用户2遇到异常情况:邮箱列表显示未读消息,但计数器显示零未读消息,因为计数器增量还没有发生[^ii]。隔离将通过确保用户2看到插入的电子邮件和更新的计数器,或者都不是,但不是一个不一致的中间点。 [^ii]: 可以说,电子邮件应用程序中的错误计数器并不是特别重要的问题。 或者,考虑一个客户的账户余额,而不是未读的计数器,一次支付事务而不是电子邮件。 ![](img/fig7-2.png) -**图7-2 违反隔离:一个事务读取另一个事务的未被执行的写入(“脏读”)。** +**图7-2 违反隔离性:一个事务读取另一个事务的未被执行的写入(“脏读”)。** -图7-3说明了对原子性的需求:如果在事务过程中发生错误,邮箱和未读计数器的内容可能会失去同步。在原子事务中,如果对计数器的更新失败,事务将被中止,并且插入的电子邮件将被回滚。 +[图7-3](img/fig7-3.png)说明了对原子性的需求:如果在事务过程中发生错误,邮箱和未读计数器的内容可能会失去同步。在原子事务中,如果对计数器的更新失败,事务将被中止,并且插入的电子邮件将被回滚。 ![](img/fig7-3.png) @@ -177,15 +177,15 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true 这些问题会令人难以置信的困惑,因此存储引擎几乎普遍的目标是在一个节点上的单个对象(例如键值对)上提供原子性和隔离性。原子性可以通过使用日志来实现崩溃恢复(请参阅第82页的“使B树可靠”),并且可以使用每个对象的锁来实现隔离(每次只允许一个线程访问对象) )。 -一些数据库也提供更复杂的原子操作,例如增量操作,这样就不需要像图7-1那样的读 - 修改 - 写循环。同样流行的是**比较和设置(CAS, compare-and-set)**操作,只有当其他人没有同时更改该值时才允许进行写操作(请参见“比较和设置”一节第245页)。 +一些数据库也提供更复杂的原子操作,例如增量操作,这样就不需要像[图7-1](img/fig7-1.png)那样的读取-修改-写入序列。同样流行的是**[比较和设置(CAS, compare-and-set)](#比较并设置(CAS))**操作,只有当其他人没有同时更改该值时才允许进行写操作。 -这些单对象操作很有用,因为它们可以防止在多个客户端尝试同时写入同一个对象时丢失更新(请参阅“防止丢失的更新”第221页)。但是,它们不是通常意义上的事务。CAS以及其他单一对象操作被称为“轻量级事务”,甚至出于营销目的被称为“ACID”[20,21,22],但是这个术语是误导性的。事务通常被理解为,将多个对象上的多个操作分组为一个执行单元的机制。[^iv] +这些单对象操作很有用,因为它们可以防止在多个客户端尝试同时写入同一个对象时丢失更新(参阅“[防止丢失更新](#防止丢失更新)”)。但是,它们不是通常意义上的事务。CAS以及其他单一对象操作被称为“轻量级事务”,甚至出于营销目的被称为“ACID”【20,21,22】,但是这个术语是误导性的。事务通常被理解为,将多个对象上的多个操作分组为一个执行单元的机制。[^iv] [^iv]: 严格地说,**原子自增(atomic increment)**这个术语在多线程编程的意义上使用了原子这个词。 在ACID的情况下,它实际上应该被称为**孤立(isolated)**的或**可序列化(serializable)**的增量。 但这就太吹毛求疵了。 #### 多对象事务的需求 -许多分布式数据存储已经放弃了多对象事务,因为它们很难跨分区实现,而且在需要高可用性或高性能的情况下,它们可能会碍事挡路。但就算是分布式系统,也并没有什么根本上能拒绝事务的原因,我们将在第9章讨论分布式事务的实现。 +许多分布式数据存储已经放弃了多对象事务,因为它们很难跨分区实现,而且在需要高可用性或高性能的情况下,它们可能会碍事挡路。但就算是分布式系统,也并没有什么根本上能拒绝事务的原因,我们将在[第9章](ch9.md)讨论分布式事务的实现。 但是我们是否需要多对象事务?是否有可能只用键值数据模型和单对象操作来实现任何应用程序? @@ -195,7 +195,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true * 在文档数据模型中,需要一起更新的字段通常在同一个文档中,这被视为单个对象 - 更新单个文档时不需要多对象事务。但是,缺乏连接功能的文档数据库也会鼓励非规范化(请参阅第38页上的“[与文档数据库相关的对比]()”)。当需要更新非规范化的信息时,如图7-2所示,您需要一次更新多个文档。事务在这种情况下非常有用,可以防止非规范化的数据不同步。 * 在具有二级索引的数据库中(几乎除了纯粹的键值存储以外几乎都有),每次更改值时都需要更新索引。从事务角度来看,这些索引是不同的数据库对象:例如,如果没有事务隔离,记录可能出现在一个索引中,但没有出现在另一个索引中,因为第二个索引的更新还没有发生。 -这些应用程序仍然可以在没有事务的情况下实现。然而,没有原子性,错误处理就变得复杂得多,缺乏隔离性,就会导致并发问题。我们将在第233页的“[弱隔离级别]()”中讨论这些问题,并在[第12章]()中探讨其他方法。 +这些应用程序仍然可以在没有事务的情况下实现。然而,没有原子性,错误处理就变得复杂得多,缺乏隔离性,就会导致并发问题。我们将在“[弱隔离级别](#弱隔离级别)”中讨论这些问题,并在[第12章]()中探讨其他方法。 #### 处理错误和中止 @@ -210,11 +210,13 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true - 如果事务实际上成功了,但是在服务器试图确认成功提交给客户端(所以客户端认为失败)时网络发生故障,那么重试事务会导致它被执行两次,除非你有一个额外的应用级除重机制。 - 如果错误是由于负载过大造成的,则重试事务将使问题变得更糟,而不是更好。为了避免这种正反馈循环,您可以限制重试次数,使用指数退避算法,并单独处理与过载相关的错误(如果允许)。 - 仅在临时性错误(例如,由于死锁,异常情况,临时性网络中断和故障转移)之后才值得重试。在发生永久性错误(例如,违反约束)之后重试是毫无意义的。 -- 如果事务在数据库之外也有副作用,即使事务被中止,也可能发生这些副作用。例如,如果您正在发送电子邮件,则每次重试事务时都重新发送电子邮件肯定不是你想要的。如果您想确保几个不同的系统提交或放弃在一起,**二阶段提交(2PC, two-phase commit)**可以提供帮助(我们将在第354页的“[原子提交和两阶段提交(2PC)]()”中讨论这个问题)。 +- 如果事务在数据库之外也有副作用,即使事务被中止,也可能发生这些副作用。例如,如果您正在发送电子邮件,则每次重试事务时都重新发送电子邮件肯定不是你想要的。如果您想确保几个不同的系统提交或放弃在一起,**二阶段提交(2PC, two-phase commit)**可以提供帮助(将在“[原子提交和两阶段提交(2PC)](ch9.md#原子提交与二阶段提交(2PC))”中讨论这个问题)。 - 如果客户端进程在重试时失败,则任何试图写入数据库的数据都将丢失。 + + ## 弱隔离级别 如果两个事务不触及相同的数据,它们可以安全地**并行(parallel)**运行,因为两者都不依赖于另一个。当一个事务读取由另一个事务同时修改的数据时,或者当两个事务试图同时修改相同的数据时,并发问题(竞争条件)才会出现。 @@ -223,13 +225,13 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true 出于这个原因,数据库一直试图通过提供**事务隔离(transaction isolation)**来隐藏应用程序开发者的并发问题。从理论上讲,隔离可以通过假装没有并发发生,让你的生活更加轻松:**可序列化(serializable)**的隔离登记意味着数据库保证事务的效果与连续运行(即一次一个,没有任何并发)是一样的。 -实际上不幸的是:隔离并没有那么简单。**可序列化**会有性能损失,许多数据库不愿意付这个代价[8]。因此,系统通常使用较弱的隔离级别来防止一些并发问题,但不是全部。这些隔离级别难以理解,并且会导致微妙的错误,但是它们仍然在实践中被使用[23]。 +实际上不幸的是:隔离并没有那么简单。**可序列化**会有性能损失,许多数据库不愿意付这个代价[8]。因此,系统通常使用较弱的隔离级别来防止一些并发问题,但不是全部。这些隔离级别难以理解,并且会导致微妙的错误,但是它们仍然在实践中被使用【23】。 -并发性错误导致的并发性错误不仅仅是一个理论问题。他们造成了很多的资金损失[24,25],耗费了财务审计人员的调查[26],并导致客户数据被破坏[27]。关于这类问题的一个流行的评论是“如果你正在处理财务数据,请使用ACID数据库!” ——但是这一点没有提到。即使是很多流行的关系型数据库系统(通常被认为是“ACID”)也使用弱隔离级别,所以它们也不一定能防止这些错误的发生。 +并发性错误导致的并发性错误不仅仅是一个理论问题。他们造成了很多的资金损失【24,25】,耗费了财务审计人员的调查【26】,并导致客户数据被破坏【27】。关于这类问题的一个流行的评论是“如果你正在处理财务数据,请使用ACID数据库!” ——但是这一点没有提到。即使是很多流行的关系型数据库系统(通常被认为是“ACID”)也使用弱隔离级别,所以它们也不一定能防止这些错误的发生。 比起盲目地依赖工具,我们应该对存在的并发问题的种类,以及如何防止这些问题有深入的理解。然后就可以使用我们所掌握的工具来构建可靠和正确的应用程序。 -在本节中,我们将看几个在实践中使用的弱(**不可串行化(nonserializable)**)隔离级别,并详细讨论哪种竞争条件可能发生也可能不发生,以便您可以决定什么级别适合您的应用程序。一旦我们完成了这个工作,我们将详细讨论可串行性(请参阅“[可序列化]()”第259页)。我们讨论的隔离级别将是非正式的,使用示例。如果你需要严格的定义和分析它们的属性,你可以在学术文献中找到它们[28,29,30]。 +在本节中,我们将看几个在实践中使用的弱(**不可串行化(nonserializable)**)隔离级别,并详细讨论哪种竞争条件可能发生也可能不发生,以便您可以决定什么级别适合您的应用程序。一旦我们完成了这个工作,我们将详细讨论可串行性(请参阅“[可序列化](#可序列化)”)。我们讨论的隔离级别将是非正式的,使用示例。如果你需要严格的定义和分析它们的属性,你可以在学术文献中找到它们[28,29,30]。 ### 读已提交 @@ -265,8 +267,8 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true 通过防止脏写,这个隔离级别避免了一些并发问题: -- 如果事务更新多个对象,脏写会导致不好的结果。例如,考虑图7-5,图7-5以一个二手车销售网站为例,Alice和Bob两个人同时试图购买同一辆车。购买汽车需要两次数据库写入:网站上的商品列表需要更新,以反映买家的购买,销售发票需要发送给买家。在图7-5的情况下,销售是属于Bob的(因为他成功更新了商品列表),但发票却寄送给了爱丽丝(因为她成功更新了发票表)。读已提交会阻止这样这样的事故。 -- 但是,提交读取并不能防止[图7-1]()中两个计数器增量之间的竞争状态。在这种情况下,第二次写入发生在第一个事务提交后,所以它不是一个脏写。这仍然是不正确的,但是出于不同的原因,在第242页的“防止更新的丢失”中将讨论如何使这种计数器增量安全。 +- 如果事务更新多个对象,脏写会导致不好的结果。例如,考虑[图7-5](img/fig7-5.png),[图7-5](img/fig7-5.png)以一个二手车销售网站为例,Alice和Bob两个人同时试图购买同一辆车。购买汽车需要两次数据库写入:网站上的商品列表需要更新,以反映买家的购买,销售发票需要发送给买家。在[图7-5](img/fig7-5.png)的情况下,销售是属于Bob的(因为他成功更新了商品列表),但发票却寄送给了爱丽丝(因为她成功更新了发票表)。读已提交会阻止这样这样的事故。 +- 但是,提交读取并不能防止[图7-1]()中两个计数器增量之间的竞争状态。在这种情况下,第二次写入发生在第一个事务提交后,所以它不是一个脏写。这仍然是不正确的,但是出于不同的原因,在“[防止更新丢失](#防止丢失更新)”中将讨论如何使这种计数器增量安全。 ![](img/fig7-5.png) @@ -274,7 +276,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true #### 实现读已提交 -读提交是一个非常流行的隔离级别。这是Oracle 11g,PostgreSQL,SQL Server 2012,MemSQL和其他许多数据库的默认设置[8]。 +读已提交是一个非常流行的隔离级别。这是Oracle 11g,PostgreSQL,SQL Server 2012,MemSQL和其他许多数据库的默认设置【8】。 最常见的情况是,数据库通过使用**行锁(row-level lock)**来防止脏写:当事务想要修改特定对象(行或文档)时,它必须首先获得该对象的锁。然后必须持有该锁直到事务被提交或中止。一次只有一个事务可持有任何给定对象的锁;如果另一个事务要写入同一个对象,则必须等到第一个事务提交或中止后才能获取该锁并继续。这种锁定是读已提交模式(或更强的隔离级别)的数据库自动完成的。 @@ -316,7 +318,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true 快照隔离对长时间运行的只读查询(如备份和分析)非常有用。如果查询的数据在查询执行的同时发生变化,则很难理解查询的含义。当一个事务可以看到数据库在某个特定时间点冻结时的一致快照,理解起来就很容易了。 -快照隔离是一个流行的功能:PostgreSQL,使用InnoDB引擎的MySQL,Oracle,SQL Server等都支持[23,31,32]。 +快照隔离是一个流行的功能:PostgreSQL,使用InnoDB引擎的MySQL,Oracle,SQL Server等都支持【23,31,32】。 #### 实现快照隔离 @@ -368,17 +370,17 @@ UPDATE操作在内部翻译为DELETE和INSERT。例如,在[图7-7]()中,事 #### 可重复读与命名混淆 -快照隔离是一个有用的隔离级别,特别对于只读事务而言。但是,许多数据库实现了它,却用不同的名字来称呼。在Oracle中称为**可序列化(Serializable)**的,在PostgreSQL和MySQL中称为**可重复读(repeatable read)**[23]。 +快照隔离是一个有用的隔离级别,特别对于只读事务而言。但是,许多数据库实现了它,却用不同的名字来称呼。在Oracle中称为**可序列化(Serializable)**的,在PostgreSQL和MySQL中称为**可重复读(repeatable read)**【23】。 -这种命名混淆的原因是SQL标准没有快照隔离的概念,因为标准是基于System R 1975年定义的隔离级别[2],那时候**快照隔离**尚未发明。相反,它定义了**可重复读**,表面上看起来与快照隔离很相似。 PostgreSQL和MySQL称其**快照隔离**级别为**可重复读(repeatable read)**,因为它符合标准要求,所以它们可以称自己“兼容标准”。 +这种命名混淆的原因是SQL标准没有快照隔离的概念,因为标准是基于System R 1975年定义的隔离级别【2】,那时候**快照隔离**尚未发明。相反,它定义了**可重复读**,表面上看起来与快照隔离很相似。 PostgreSQL和MySQL称其**快照隔离**级别为**可重复读(repeatable read)**,因为它符合标准要求,所以它们可以称自己“兼容标准”。 -不幸的是,SQL标准对隔离级别的定义是有缺陷的——模糊,不精确,并不像标准应该的那样独立于实现[28]。有几个数据库实现了可重复读,但它们实际提供的保证存在很大的差异,尽管表面上是标准化的[23]。在研究文献[29,30]中已经有了可重复阅读的正式定义,但是大多数的实现并不能满足这个正式的定义。最后,IBM DB2使用“可重复读”来引用可串行化[8]。 +不幸的是,SQL标准对隔离级别的定义是有缺陷的——模糊,不精确,并不像标准应该的那样独立于实现【28】。有几个数据库实现了可重复读,但它们实际提供的保证存在很大的差异,尽管表面上是标准化的【23】。在研究文献【29,30】中已经有了可重复阅读的正式定义,但是大多数的实现并不能满足这个正式的定义。最后,IBM DB2使用“可重复读”来引用可串行化【8】。 结果,没有人真正知道**可重复读**的意思。 ### 防止丢失更新 -到目前为止,我们讨论的读已提交和快照隔离级别主要是:**保证了只读事务在并发写入时可以看到什么**。却忽略了两个事务并发写入的问题——我们只讨论了脏写(参阅“脏写”),一种特定类型的写-写冲突是可能出现的。 +到目前为止,我们讨论的读已提交和快照隔离级别主要是:**保证了只读事务在并发写入时可以看到什么**。却忽略了两个事务并发写入的问题——我们只讨论了[脏写](#脏写),一种特定类型的写-写冲突是可能出现的。 并发的写入事务之间还有其他几种有趣的冲突。其中最着名的是**丢失更新(lost update)**问题,如[图7-1]()所示,以两个并发计数器增量为例。 @@ -402,7 +404,7 @@ UPDATE counters SET value = value + 1 WHERE key = 'foo'; [^viii]: 将文本文档的编辑表示为原子的变化流是可能的,尽管相当复杂。参阅第174页上的“自动冲突解决”。 -原子操作通常通过在读取对象时,获取其上的排它锁来实现。以便更新完成之前没有其他事务可以读取它。这种技术有时被称为**游标稳定性(cursor stability)**[36,37]。另一个选择是简单地强制所有的原子操作在单一线程上执行。 +原子操作通常通过在读取对象时,获取其上的排它锁来实现。以便更新完成之前没有其他事务可以读取它。这种技术有时被称为**游标稳定性(cursor stability)**【36,37】。另一个选择是简单地强制所有的原子操作在单一线程上执行。 不幸的是,ORM框架很容易意外地执行不安全的读取-修改-写入循环,而不是使用数据库提供的原子操作[38]。如果你知道自己在做什么那当然不是问题,但它经常产生那种很难测出来的微妙Bug。 @@ -433,7 +435,7 @@ COMMIT; 原子操作和锁是通过强制**读取-修改-写入序列**按顺序发生,来防止丢失更新的方法。另一种方法是允许它们并行执行,如果事务管理器检测到丢失更新,则中止事务并强制它们重试其**读取-修改-写入序列**。 -这种方法的一个优点是,数据库可以结合快照隔离高效地执行此检查。事实上,PostgreSQL的可重复读,Oracle的可串行化和SQL Server的快照隔离级别,都会自动检测到丢失更新,并中止惹麻烦的事务。但是,MySQL/InnoDB的可重复读并不会检测**丢失更新**[23]。一些作者[28,30]认为,数据库必须能防止丢失更新才称得上是提供了**快照隔离**,所以在这个定义下,MySQL下不提供快照隔离。 +这种方法的一个优点是,数据库可以结合快照隔离高效地执行此检查。事实上,PostgreSQL的可重复读,Oracle的可串行化和SQL Server的快照隔离级别,都会自动检测到丢失更新,并中止惹麻烦的事务。但是,MySQL/InnoDB的可重复读并不会检测**丢失更新**【23】。一些作者【28,30】认为,数据库必须能防止丢失更新才称得上是提供了**快照隔离**,所以在这个定义下,MySQL下不提供快照隔离。 丢失更新检测是一个很好的功能,因为它不需要应用代码使用任何特殊的数据库功能,你可能会忘记使用锁或原子操作,从而引入一个错误;但丢失更新的检测是自动发生的,因此不太容易出错。 @@ -453,15 +455,15 @@ UPDATE wiki_pages SET content = '新内容' #### 冲突解决和复制 -在复制数据库中(参见第5章),防止丢失的更新需要考虑另一个维度:由于在多个节点上存在数据副本,并且在不同节点上的数据可能被并发地修改,因此需要采取一些额外的步骤来防止丢失更新。 +在复制数据库中(参见[第5章](ch5.md)),防止丢失的更新需要考虑另一个维度:由于在多个节点上存在数据副本,并且在不同节点上的数据可能被并发地修改,因此需要采取一些额外的步骤来防止丢失更新。 -锁和CAS操作假定有一个最新的数据副本。但是多主或无主复制的数据库通常允许多个写入并发执行,并异步复制到副本上,因此无法保证有一份数据的最新副本。所以基于锁或CAS操作的技术不适用于这种情况。 (我们将在“[线性化]()”中更详细地讨论这个问题。) +锁和CAS操作假定有一个最新的数据副本。但是多主或无主复制的数据库通常允许多个写入并发执行,并异步复制到副本上,因此无法保证有一份数据的最新副本。所以基于锁或CAS操作的技术不适用于这种情况。 (我们将在“[线性化](ch9.md#线性化)”中更详细地讨论这个问题。) -相反,如“[检测并发写入]()”一节所述,这种复制数据库中的一种常见方法是允许并发写入创建多个冲突版本的值(也称为兄弟),并使用应用代码或特殊数据结构在事实发生之后解决和合并这些版本。 +相反,如“[ch5.md#检测并发写入](#检测并发写入)”一节所述,这种复制数据库中的一种常见方法是允许并发写入创建多个冲突版本的值(也称为兄弟),并使用应用代码或特殊数据结构在事实发生之后解决和合并这些版本。 -原子操作可以在复制的上下文中很好地工作,尤其当它们具有可交换性时(即,可以在不同的副本上以不同的顺序应用它们,且仍然可以得到相同的结果)。例如,递增计数器或向集合添加元素是可交换的操作。这是Riak 2.0数据类型背后的思想,它可以防止复制副本丢失更新。当不同的客户端同时更新一个值时,Riak自动将更新合并在一起,以免丢失更新[39]。 +原子操作可以在复制的上下文中很好地工作,尤其当它们具有可交换性时(即,可以在不同的副本上以不同的顺序应用它们,且仍然可以得到相同的结果)。例如,递增计数器或向集合添加元素是可交换的操作。这是Riak 2.0数据类型背后的思想,它可以防止复制副本丢失更新。当不同的客户端同时更新一个值时,Riak自动将更新合并在一起,以免丢失更新【39】。 -另一方面,最后写入为准(LWW)的冲突解决方法很容易丢失更新,如“[最后写入为准(放弃并发写入)]()”中所述。不幸的是,LWW是许多复制数据库中的默认值。 +另一方面,最后写入为准(LWW)的冲突解决方法很容易丢失更新,如“[最后写入为准(丢弃并发写入)](ch5.md#最后写入为准(丢弃并发写入))”中所述。不幸的是,LWW是许多复制数据库中的默认值。 #### 写入偏差与幻读 @@ -469,7 +471,7 @@ UPDATE wiki_pages SET content = '新内容' 但是,并发写入间可能发生的竞争条件还没有完。在本节中,我们将看到一些更微妙的冲突例子。 -首先,想象一下这个例子:你正在为医院写一个医生轮班管理程序。医院通常会同时要求几位医生待命,但底线是至少有一位医生在待命。医生可以放弃他们的班次(例如,如果他们自己生病了),只要至少有一个同事在这一班中继续工作[40,41]。 +首先,想象一下这个例子:你正在为医院写一个医生轮班管理程序。医院通常会同时要求几位医生待命,但底线是至少有一位医生在待命。医生可以放弃他们的班次(例如,如果他们自己生病了),只要至少有一个同事在这一班中继续工作【40,41】。 现在想象一下,Alice和Bob是两位值班医生。两人都感到不适,所以他们都决定请假。不幸的是,他们恰好在同一时间点击按钮关闭电话。图7-8说明了接下来的事情。 @@ -481,15 +483,15 @@ UPDATE wiki_pages SET content = '新内容' #### 写入偏差的特征 -这种异常称为**写入偏差**[28]。它既不是**脏写**,也不是**丢失更新**,因为这两个事务正在更新两个不同的对象(Alice和Bob各自的待命记录)。在这里发生的冲突并不是那么明显,但是这显然是一个竞争条件:如果两个事务一个接一个地运行,那么第二个医生就不能翘班了。异常行为只有在事务并发进行时才有可能。 +这种异常称为**写入偏差**【28】。它既不是**脏写**,也不是**丢失更新**,因为这两个事务正在更新两个不同的对象(Alice和Bob各自的待命记录)。在这里发生的冲突并不是那么明显,但是这显然是一个竞争条件:如果两个事务一个接一个地运行,那么第二个医生就不能翘班了。异常行为只有在事务并发进行时才有可能。 可以将写入偏差视为丢失更新问题的一般化。如果两个事务读取相同的对象,然后更新其中一些对象(不同的事务可能更新不同的对象),则可能发生写入偏差。在多个事务更新同一个对象的特殊情况下,就会发生脏写或丢失更新(取决于时机)。 我们看到,有各种不同的方法来防止丢失的更新。随着写歪斜,我们的选择更受限制: * 由于涉及多个对象,单对象的原子操作不起作用。 -* 不幸的是,在一些快照隔离的实现中,自动检测丢失更新对此并没有帮助。在PostgreSQL的可重复读,MySQL/InnoDB的可重复读,Oracle可序列化或SQL Server的快照隔离级别中,都不会自动检测写入偏差[23]。自动防止写入偏差需要真正的可序列化隔离(请参见“[可序列化]()”(第217页))。 -* 某些数据库允许配置约束,然后由数据库强制执行(例如,唯一性,外键约束或特定值限制)。但是为了指定至少有一名医生必须在线,需要一个涉及多个对象的约束。大多数数据库没有内置对这种约束的支持,但是你可以使用触发器,或者物化视图来实现它们,这取决于不同的数据库[42]。 +* 不幸的是,在一些快照隔离的实现中,自动检测丢失更新对此并没有帮助。在PostgreSQL的可重复读,MySQL/InnoDB的可重复读,Oracle可序列化或SQL Server的快照隔离级别中,都不会自动检测写入偏差【23】。自动防止写入偏差需要真正的可序列化隔离(请参见“[可序列化](#可序列化)”)。 +* 某些数据库允许配置约束,然后由数据库强制执行(例如,唯一性,外键约束或特定值限制)。但是为了指定至少有一名医生必须在线,需要一个涉及多个对象的约束。大多数数据库没有内置对这种约束的支持,但是你可以使用触发器,或者物化视图来实现它们,这取决于不同的数据库【42】。 * 如果无法使用可序列化的隔离级别,则此情况下的次优选项可能是显式锁定事务所依赖的行。在例子中,你可以写下如下的代码: ```sql @@ -514,7 +516,7 @@ COMMIT; ***会议室预订系统*** -假设你想强制执行,同一时间不能同时在两个会议室预订[43]。当有人想要预订时,首先检查是否存在相互冲突的预订(即预订时间范围重叠的同一房间),如果没有找到,则创建会议(请参见示例7-2)[^ix]。 +假设你想强制执行,同一时间不能同时在两个会议室预订【43】。当有人想要预订时,首先检查是否存在相互冲突的预订(即预订时间范围重叠的同一房间),如果没有找到,则创建会议(请参见示例7-2)[^ix]。 [^ix]: 在PostgreSQL中,您可以使用范围类型优雅地执行此操作,但在其他数据库中并未得到广泛支持。 @@ -539,7 +541,7 @@ COMMIT; ***多人游戏*** -在[例7-1]()中,我们使用一个锁来防止丢失更新(也就是确保两个玩家不能同时移动同一个棋子)。但是锁定并不妨碍玩家将两个不同的棋子移动到棋盘上的相同位置,或者采取其他违反游戏规则的行为。按照您正在执行的规则类型,也许可以使用唯一约束,否则您很容易发生写入偏差。 +在例7-1中,我们使用一个锁来防止丢失更新(也就是确保两个玩家不能同时移动同一个棋子)。但是锁定并不妨碍玩家将两个不同的棋子移动到棋盘上的相同位置,或者采取其他违反游戏规则的行为。按照您正在执行的规则类型,也许可以使用唯一约束,否则您很容易发生写入偏差。 ***抢注用户名*** @@ -547,7 +549,7 @@ COMMIT; ***防止双重开支*** -允许用户花钱或积分的服务,需要检查用户的支付数额不超过其余额。可以通过在用户的帐户中插入一个试探性的消费项目来实现这一点,列出帐户中的所有项目,并检查总和是否为正值[44]。有了写入偏差,可能会发生两个支出项目同时插入,一起导致余额变为负值,但这两个事务都不会注意到另一个。 +允许用户花钱或积分的服务,需要检查用户的支付数额不超过其余额。可以通过在用户的帐户中插入一个试探性的消费项目来实现这一点,列出帐户中的所有项目,并检查总和是否为正值【44】。有了写入偏差,可能会发生两个支出项目同时插入,一起导致余额变为负值,但这两个事务都不会注意到另一个。 #### 导致写入偏差的幻读 @@ -565,7 +567,7 @@ COMMIT; 在医生值班的例子中,在步骤3中修改的行,是步骤1中返回的行之一,所以我们可以通过锁定步骤1中的行(`SELECT FOR UPDATE`)来使事务安全并避免写入偏差。但是其他四个例子是不同的:它们检查是否**不存在**某些满足条件的行,写入会**添加**一个匹配相同条件的行。如果步骤1中的查询没有返回任何行,则`SELECT FOR UPDATE`锁不了任何东西。 -这种效应:一个事务中的写入改变另一个事务的搜索查询的结果,被称为**幻读**[3]。快照隔离避免了只读查询中的幻影,但是在像我们讨论的例子那样的读写事务中,幻影会导致特别棘手的写歪斜情况。 +这种效应:一个事务中的写入改变另一个事务的搜索查询的结果,被称为**幻读**【3】。快照隔离避免了只读查询中幻读,但是在像我们讨论的例子那样的读写事务中,幻影会导致特别棘手的写歪斜情况。 #### 物化冲突 @@ -575,42 +577,42 @@ COMMIT; 现在,要创建预订的事务可以锁定(`SELECT FOR UPDATE`)表中与所需房间和时间段对应的行。在获得锁定之后,它可以检查重叠的预订并像以前一样插入新的预订。请注意,这个表并不是用来存储预订相关的信息——它完全就是一组锁,用于防止同时修改同一房间和时间范围内的预订。 -这种方法被称为**物化冲突(materializing conflicts)**,因为它将幻读变为数据库中一组具体行上的锁冲突[11]。不幸的是,弄清楚如何物化冲突可能很难,也很容易出错,而让并发控制机制泄漏到应用数据模型是很丑陋的做法。出于这些原因,如果没有其他办法可以实现,物化冲突应被视为最后的手段。在大多数情况下。**可序列化(Serializable)**的隔离级别是更可取的。 +这种方法被称为**物化冲突(materializing conflicts)**,因为它将幻读变为数据库中一组具体行上的锁冲突【11】。不幸的是,弄清楚如何物化冲突可能很难,也很容易出错,而让并发控制机制泄漏到应用数据模型是很丑陋的做法。出于这些原因,如果没有其他办法可以实现,物化冲突应被视为最后的手段。在大多数情况下。**可序列化(Serializable)**的隔离级别是更可取的。 -## 可序列化(Serializability) +## 可序列化 在本章中,已经看到了几个易于出现竞争条件的事务例子。**读已提交**和**快照隔离**级别会阻止某些竞争条件,但不会阻止另一些。我们遇到了一些特别棘手的例子,**写入偏差**和**幻读**。这是一个可悲的情况: - 隔离级别难以理解,并且在不同的数据库中实现的不一致(例如,“可重复读”的含义天差地别)。 - 光检查应用代码很难判断在特定的隔离级别运行是否安全。 特别是在大型应用程序中,您可能并不知道并发发生的所有事情。 -- 没有检测竞争条件的好工具。原则上来说,静态分析可能会有帮助[26],但研究中的技术还没法实际应用。并发问题的测试是很难的,因为它们通常是非确定性的——只有在倒霉的时机下才会出现问题。 +- 没有检测竞争条件的好工具。原则上来说,静态分析可能会有帮助【26】,但研究中的技术还没法实际应用。并发问题的测试是很难的,因为它们通常是非确定性的——只有在倒霉的时机下才会出现问题。 -这不是一个新问题,从20世纪70年代以来就一直是这样了,当时首先引入了较弱的隔离级别[2]。一直以来,研究人员的答案都很简单:使用**可序列化(serializable)**的隔离级别! +这不是一个新问题,从20世纪70年代以来就一直是这样了,当时首先引入了较弱的隔离级别【2】。一直以来,研究人员的答案都很简单:使用**可序列化(serializable)**的隔离级别! **可序列化(Serializability)**隔离通常被认为是最强的隔离级别。它保证即使事务可以并行执行,最终的结果也是一样的,就好像它们没有任何并发性,连续挨个执行一样。因此数据库保证,如果事务在单独运行时正常运行,则它们在并发运行时继续保持正确——换句话说,数据库可以防止**所有**可能的竞争条件。 但如果可序列化隔离级别比弱隔离级别的烂摊子要好得多,那为什么没有人见人爱?为了回答这个问题,我们需要看看实现可序列化的选项,以及它们如何执行。目前大多数提供可序列化的数据库都使用了三种技术之一,本章的剩余部分将会介绍这些技术。 -- 字面意义上地串行顺序执行事务(参见“[真的串行执行]()”) -- **两相锁定(2PL, two-phase locking)**,几十年来唯一可行的选择。(参见“[两相锁定(2PL)]()”) -- 乐观并发控制技术,例如**可序列化的快照隔离(serializable snapshot isolation)**(参阅“[可序列化的快照隔离(SSI)]()” +- 字面意义上地串行顺序执行事务(参见“[真的串行执行](#真的串行执行)”) +- **两相锁定(2PL, two-phase locking)**,几十年来唯一可行的选择。(参见“[两相锁定(2PL)](#两阶段锁定(2PL))”) +- 乐观并发控制技术,例如**可序列化的快照隔离(serializable snapshot isolation)**(参阅“[可序列化的快照隔离(SSI)](#序列化快照隔离(SSI))” -现在将主要在单节点数据库的背景下讨论这些技术;在第9章中,我们将研究如何将它们推广到涉及分布式系统中多个节点的事务。 +现在将主要在单节点数据库的背景下讨论这些技术;在[第9章](ch9.md)中,我们将研究如何将它们推广到涉及分布式系统中多个节点的事务。 #### 真的串行执行 避免并发问题的最简单方法就是完全不要并发:在单个线程上按顺序一次只执行一个事务。这样做就完全绕开了检测/防止事务间冲突的问题,由此产生的隔离,正是可序列化的定义。 -尽管这似乎是一个明显的主意,但数据库设计人员只是在2007年左右才决定,单线程循环执行事务是可行的[45]。如果多线程并发在过去的30年中被认为是获得良好性能的关键所在,那么究竟是什么改变致使单线程执行变为可能呢? +尽管这似乎是一个明显的主意,但数据库设计人员只是在2007年左右才决定,单线程循环执行事务是可行的【45】。如果多线程并发在过去的30年中被认为是获得良好性能的关键所在,那么究竟是什么改变致使单线程执行变为可能呢? 两个进展引起了这个反思: -- RAM足够便宜了,许多场景现在都可以将完整的活跃数据集保存在内存中。(参阅“[将所有内容保留在内存中]()”)。当事务需要访问的所有数据都在内存中时,事务处理的执行速度要比等待数据从磁盘加载时快得多。 -- 数据库设计人员意识到OLTP事务通常很短,而且只进行少量的读写操作(参阅“[事务处理或分析?]()”)。相比之下,长时间运行的分析查询通常是只读的,因此它们可以在串行执行循环之外的一致快照(使用快照隔离)上运行。 +- RAM足够便宜了,许多场景现在都可以将完整的活跃数据集保存在内存中。(参阅“[在内存中存储一切](ch3.md#在内存中存储一切)”)。当事务需要访问的所有数据都在内存中时,事务处理的执行速度要比等待数据从磁盘加载时快得多。 +- 数据库设计人员意识到OLTP事务通常很短,而且只进行少量的读写操作(参阅“[事务处理或分析?](ch3.md#事务处理还是分析?)”)。相比之下,长时间运行的分析查询通常是只读的,因此它们可以在串行执行循环之外的一致快照(使用快照隔离)上运行。 -串行执行事务的方法在VoltDB / H-Store,Redis和Datomic中实现[46,47,48]。设计用于单线程执行的系统有时可以比支持并发的系统更好,因为它可以避免锁的协调开销。但是其吞吐量仅限于单个CPU核的吞吐量。为了充分利用单一线程,需要与传统形式不同的结构的事务。 +串行执行事务的方法在VoltDB / H-Store,Redis和Datomic中实现【46,47,48】。设计用于单线程执行的系统有时可以比支持并发的系统更好,因为它可以避免锁的协调开销。但是其吞吐量仅限于单个CPU核的吞吐量。为了充分利用单一线程,需要与传统形式不同的结构的事务。 #### 在存储过程中封装事务 @@ -638,7 +640,7 @@ COMMIT; 但是这些问题都是可以克服的。现代的存储过程实现放弃了PL/SQL,而是使用现有的通用编程语言:VoltDB使用Java或Groovy,Datomic使用Java或Clojure,而Redis使用Lua。 -存储过程与内存数据存储,使得在单个线程上执行所有事务变得可行。由于它们不需要等待I / O,并且避免了其他并发控制机制的开销,它们可以在单个线程上实现相当好的吞吐量。 +存储过程与内存数据存储,使得在单个线程上执行所有事务变得可行。由于它们不需要等待I/O,并且避免了其他并发控制机制的开销,它们可以在单个线程上实现相当好的吞吐量。 VoltDB还使用存储过程进行复制:但不是将事务的写入结果从一个节点复制到另一个节点,而是在每个节点上执行相同的存储过程。因此VoltDB要求存储过程是**确定性的**(当在不同的节点上运行时,它们必须产生相同的结果)。举个例子,如果事务需要使用当前的日期和时间,则必须通过特殊的确定性API来实现。 @@ -646,13 +648,13 @@ VoltDB还使用存储过程进行复制:但不是将事务的写入结果从 顺序执行所有事务使并发控制简单多了,但是数据库的事务吞吐量被限制在了单个机器上单个CPU核的速度。只读事务可以使用快照隔离在其他地方执行,但对于写入吞吐量较高的应用程序,单线程事务处理器可能成为一个严重的瓶颈。 -为了扩展到多个CPU核心和多个节点,您可以对您的数据进行分区(参见第6章),在VoltDB中这是支持的。如果您可以找到一种对数据集进行分区的方法,以便每个事务只需要在单个分区中读写数据,那么每个分区就可以拥有自己独立运行的事务处理线程。在这种情况下可以为每个分区指派一个独立的CPU核,事务吞吐量就可以与CPU核数保持线性扩展[47]。 +为了扩展到多个CPU核心和多个节点,您可以对您的数据进行分区(参见[第6章](ch6.md)),在VoltDB中这是支持的。如果您可以找到一种对数据集进行分区的方法,以便每个事务只需要在单个分区中读写数据,那么每个分区就可以拥有自己独立运行的事务处理线程。在这种情况下可以为每个分区指派一个独立的CPU核,事务吞吐量就可以与CPU核数保持线性扩展【47】。 但是,对于需要访问多个分区的任何事务,数据库必须在触及的所有分区之间协调事务。存储过程需要跨越所有分区锁定执行,以确保整个系统的可串行性。 -由于跨分区事务具有额外的协调开销,所以它们比单分区事务慢得多。 VoltDB报告的吞吐量大约是每秒1000个跨分区写入,比单分区吞吐量低几个数量级,并且不能通过增加更多的机器来增加[49]。 +由于跨分区事务具有额外的协调开销,所以它们比单分区事务慢得多。 VoltDB报告的吞吐量大约是每秒1000个跨分区写入,比单分区吞吐量低几个数量级,并且不能通过增加更多的机器来增加【49】。 -事务是否可以是划分至单个分区很大程度上取决于应用数据的结构。简单的键值数据通常可以非常容易地进行分区,但是有多个二级索引的数据可能需要大量的跨分区协调(请参阅第206页的“分区和二级索引”)。 +事务是否可以是划分至单个分区很大程度上取决于应用数据的结构。简单的键值数据通常可以非常容易地进行分区,但是有多个二级索引的数据可能需要大量的跨分区协调(参阅“[分片与次级索引](ch6.md#分片与次级索引)”)。 #### 串行执行小结 @@ -686,7 +688,7 @@ VoltDB还使用存储过程进行复制:但不是将事务的写入结果从 #### 实现两阶段锁 -2PL用于MySQL(InnoDB)和SQL Server中的可序列化隔离级别,以及DB2中的可重复读隔离级别[23,36]。 +2PL用于MySQL(InnoDB)和SQL Server中的可序列化隔离级别,以及DB2中的可重复读隔离级别【23,36】。 [^xi]: 有时被称为强有力的严格的两阶段锁定(SS2PL),以区别于2PL的其他变种。 @@ -717,7 +719,7 @@ VoltDB还使用存储过程进行复制:但不是将事务的写入结果从 在会议室预订的例子中,这意味着如果一个事务在某个时间窗口内搜索了一个房间的现有预订(见例7-2),则另一个事务不能同时插入或更新同一时间窗口与同一房间的另一个预订 (可以同时插入其他房间的预订,或在不影响另一个预定的条件下预定同一房间的其他时间段)。 -如何实现这一点?从概念上讲,我们需要一个**谓词锁(predicate lock)**[3]。它类似于前面描述的共享/排它锁,但不属于特定的对象(例如,表中的一行),它属于所有符合某些搜索条件的对象,如: +如何实现这一点?从概念上讲,我们需要一个**谓词锁(predicate lock)**【3】。它类似于前面描述的共享/排它锁,但不属于特定的对象(例如,表中的一行),它属于所有符合某些搜索条件的对象,如: ```sql SELECT * FROM bookings @@ -735,7 +737,7 @@ WHERE room_id = 123 AND #### 索引范围锁 -不幸的是谓词锁性能不佳:**如果活跃事务持有很多锁,检查匹配的锁会非常耗时。**因此,大多数使用2PL的数据库实际上实现了索引范围锁(也称为**间隙锁(next-key locking)**),这是一个简化的近似版谓词锁[41,50]。 +不幸的是谓词锁性能不佳:**如果活跃事务持有很多锁,检查匹配的锁会非常耗时。**因此,大多数使用2PL的数据库实际上实现了索引范围锁(也称为**间隙锁(next-key locking)**),这是一个简化的近似版谓词锁【41,50】。 通过使谓词匹配到一个更大的集合来简化谓词锁是安全的。例如,如果你有在中午和下午1点之间预订123号房间的谓词锁,则锁定123号房间的所有时间段,或者锁定12:00~13:00时间段的所有房间(不只是123号房间)是一个安全的近似,因为任何满足原始谓词的写入也一定会满足这种更松散的近似。 @@ -756,7 +758,7 @@ WHERE room_id = 123 AND 本章描绘了数据库中并发控制的黯淡画面。一方面,我们实现了性能不好(2PL)或者扩展性不好(串行执行)的可序列化隔离级别。另一方面,我们有性能良好的弱隔离级别,但容易出现各种竞争条件(丢失更新,写入偏差,幻读等)。序列化的隔离级别和高性能是从根本上相互矛盾的吗? -也许不是:一个称为**可序列化快照隔离(SSI, serializable snapshot isolation)**的算法是非常有前途的。它提供了完整的可序列化隔离级别,但与快照隔离相比只有只有很小的性能损失。 SSI是相当新的:它在2008年首次被描述[40],并且是Michael Cahill的博士论文[51]的主题。 +也许不是:一个称为**可序列化快照隔离(SSI, serializable snapshot isolation)**的算法是非常有前途的。它提供了完整的可序列化隔离级别,但与快照隔离相比只有只有很小的性能损失。 SSI是相当新的:它在2008年首次被描述【40】,并且是Michael Cahill的博士论文【51】的主题。 今天,SSI既用于单节点数据库(9.1版以来PostgreSQL中的可序列化隔离级别)和分布式数据库(FoundationDB使用类似的算法)。由于SSI与其他并发控制机制相比还很年轻,还处于在实践中证明自己表现的阶段。但它有可能因为足够快而在未来成为新的默认选项。 @@ -768,15 +770,15 @@ WHERE room_id = 123 AND 相比之下,**序列化快照隔离**是一种**乐观(optimistic)**的并发控制技术。在这种情况下,乐观意味着,如果存在潜在的危险也不阻止事务,而是继续执行事务,希望一切都会好起来。当一个事务想要提交时,数据库检查是否有什么不好的事情发生(即隔离是否被违反);如果是的话,事务将被中止,并且必须重试。只有可序列化的事务才被允许提交。 -乐观并发控制是一个古老的想法[52],其优点和缺点已经争论了很长时间[53]。如果存在很多**争用(contention)**(很多事务试图访问相同的对象),则表现不佳,因为这会导致很大一部分事务需要中止。如果系统已经接近最大吞吐量,来自重试事务的额外负载可能会使性能变差。 +乐观并发控制是一个古老的想法【52】,其优点和缺点已经争论了很长时间【53】。如果存在很多**争用(contention)**(很多事务试图访问相同的对象),则表现不佳,因为这会导致很大一部分事务需要中止。如果系统已经接近最大吞吐量,来自重试事务的额外负载可能会使性能变差。 但是,如果有足够的备用容量,并且事务之间的争用不是太高,乐观的并发控制技术往往比悲观的要好。可交换的原子操作可以减少争用:例如,如果多个事务同时要增加一个计数器,那么应用增量的顺序(只要计数器不在同一个事务中读取)就无关紧要了,所以并发增量可以全部应用且无需冲突。 -顾名思义,SSI基于快照隔离——也就是说,事务中的所有读取都是来自数据库的一致性快照(参见“[快照隔离和可重复读取]()”)。与早期的乐观并发控制技术相比这是主要的区别。在快照隔离的基础上,SSI添加了一种算法来检测写入之间的序列化冲突,并确定要中止哪些事务。 +顾名思义,SSI基于快照隔离——也就是说,事务中的所有读取都是来自数据库的一致性快照(参见“[快照隔离和可重复读取](#快照隔离和可重复读)”)。与早期的乐观并发控制技术相比这是主要的区别。在快照隔离的基础上,SSI添加了一种算法来检测写入之间的序列化冲突,并确定要中止哪些事务。 #### 基于过时前提的决策 -先前讨论了快照隔离中的写入偏差(请参阅“写入歪斜和幻像”第221页)时,我们观察到一个循环模式:事务从数据库读取一些数据,检查查询的结果,并根据它看到的结果决定采取一些操作(写入数据库)。但是,在快照隔离的情况下,原始查询的结果在事务提交时可能不再是最新的,因为数据可能在同一时间被修改。 +先前讨论了快照隔离中的写入偏差(参阅“[写入偏差和幻像](#写入偏差与幻读)”)时,我们观察到一个循环模式:事务从数据库读取一些数据,检查查询的结果,并根据它看到的结果决定采取一些操作(写入数据库)。但是,在快照隔离的情况下,原始查询的结果在事务提交时可能不再是最新的,因为数据可能在同一时间被修改。 换句话说,事务基于一个**前提(premise)**采取行动(事务开始时候的事实,例如:“目前有两名医生正在通话”)。之后当事务要提交时,原始数据可能已经改变——前提可能不再成立。 @@ -819,11 +821,11 @@ WHERE room_id = 123 AND 与往常一样,许多工程细节会影响算法的实际表现。例如一个权衡是跟踪事务的读取和写入的**粒度(granularity)**。如果数据库详细地跟踪每个事务的活动(细粒度),那么可以准确地确定哪些事务需要中止,但是簿记开销可能变得很显著。简略的跟踪速度更快(粗粒度),但可能会导致更多不必要的事务中止。 -在某些情况下,事务可以读取被另一个事务覆盖的信息:这取决于发生了什么,有时可以证明执行结果无论如何都是可序列化的。 PostgreSQL使用这个理论来减少不必要的中止次数[11,41]。 +在某些情况下,事务可以读取被另一个事务覆盖的信息:这取决于发生了什么,有时可以证明执行结果无论如何都是可序列化的。 PostgreSQL使用这个理论来减少不必要的中止次数【11,41】。 与两阶段锁定相比,可序列化快照隔离的最大优点是一个事务不需要阻塞等待另一个事务所持有的锁。就像在快照隔离下一样,写不会阻塞读,反之亦然。这种设计原则使得查询延迟更可预测,变量更少。特别是,只读查询可以运行在一致的快照上,而不需要任何锁定,这对于读取繁重的工作负载非常有吸引力。 -与串行执行相比,可序列化快照隔离并不局限于单个CPU核的吞吐量:FoundationDB将检测到的序列化冲突分布在多台机器上,允许扩展到很高的吞吐量。即使数据可能跨多台机器进行分区,事务也可以在保证可序列化隔离等级的同时读写多个分区中的数据[54]。 +与串行执行相比,可序列化快照隔离并不局限于单个CPU核的吞吐量:FoundationDB将检测到的序列化冲突分布在多台机器上,允许扩展到很高的吞吐量。即使数据可能跨多台机器进行分区,事务也可以在保证可序列化隔离等级的同时读写多个分区中的数据【54】。 中止率显着影响SSI的整体表现。例如,长时间读取和写入数据的事务很可能会发生冲突并中止,因此SSI要求同时读写的事务尽量短(长时间运行的只读事务可能没问题)。对于慢事务,SSI可能比两阶段锁定或串行执行更不敏感。 @@ -845,35 +847,39 @@ WHERE room_id = 123 AND ***脏写*** -​ 一个客户端会覆盖另一个客户端已经写入但尚未提交的数据。几乎所有的事务实现都可以防止脏写入。 +​ 一个客户端会覆盖另一个客户端已经写入但尚未提交的数据。几乎所有的事务实现都可以防止脏写。 ***读取偏斜(不可重复读)*** + ​ 客户在不同的时间点看到数据库的不同部分。快照隔离最常遇到这个问题,它允许事务在一个时间点从一致的快照中读取数据。它通常使用多版本并发控制(MVCC)来实现。 ***丢失的更新*** -​ 两个客户端同时执行读 - 修改 - 写周期。一个覆盖另一个的写入而不包含其变化,所以数据丢失。快照隔离的一些实现可以自动防止这种异常,而另一些实现则需要手动锁定(SELECT FOR UPDATE)。 +​ 两个客户端同时执行读取 修改 - 写周期。一个覆盖另一个的写入而不包含其变化,所以数据丢失。快照隔离的一些实现可以自动防止这种异常,而另一些实现则需要手动锁定(`SELECT FOR UPDATE`)。 ***写歪斜*** + ​ 一个事务读取一些东西,根据它所看到的值作出决定,并将决定写入数据库。但是,写作的时候,决定的前提不再是真实的。只有可序列化的隔离才能防止这种异常。 ***幻读*** + ​ 事务读取符合某些搜索条件的对象。另一个客户端进行写入,影响搜索结果。快照隔离可以防止直接的幻像读取,但是写入歪斜环境中的幻影需要特殊处理,例如索引范围锁定。 弱隔离级别可以防止这些异常情况,但是让应用程序开发人员手动处理其他应用程序(例如,使用显式锁定)。只有可序列化的隔离才能防范所有这些问题。我们讨论了实现可序列化事务的三种不同方法: ***按照连续顺序从字面上执行事务*** + ​ 如果您可以使每个事务的执行速度非常快,并且事务吞吐量足够低以在单个CPU内核上处理,这是一个简单而有效的选择。 ***两相锁定*** -​ 数十年来,这一直是实现可串行化的标准方式,但是许多应用程序由于其性能特征而避免使用它。 + +​ 数十年来,这一直是实现可串行化的标准方式,但是许多应用程序由于其性能特性而避免使用它。 + ***可串行化快照隔离(SSI)*** ​ 一个相当新的算法,避免了以前方法的大部分缺点。它使用乐观的方法,允许事务进行而不会阻塞。当一个事务想要提交时,它会被检查,如果执行不可序列化,它将被中止。 -本章中的示例使用关系数据模型。但是,正如在讨论中 - -无论使用哪种数据模型,第231页上的“多对象事务的需要”事务都是有价值的数据库功能。 +本章中的示例使用关系数据模型。但是,正如在讨论中,无论使用哪种数据模型,第231页上的“多对象事务的需要”事务都是有价值的数据库功能。 在本章中,我们主要探讨了在一台机器上运行数据库的情况下的想法和算法。分布式数据库中的事务开启了一系列新的困难挑战,我们将在接下来的两章中讨论。 @@ -881,158 +887,69 @@ WHERE room_id = 123 AND ## 参考文献 -1. Donald D. Chamberlin, Morton M. Astrahan, Michael W. Blasgen, et al.: - “[A History and Evaluation of System R](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.84.348&rep=rep1&type=pdf),” *Communications of the ACM*, - volume 24, number 10, pages 632–646, October 1981. +1. Donald D. Chamberlin, Morton M. Astrahan, Michael W. Blasgen, et al.: “[A History and Evaluation of System R](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.84.348&rep=rep1&type=pdf),” *Communications of the ACM*, volume 24, number 10, pages 632–646, October 1981. [doi:10.1145/358769.358784](http://dx.doi.org/10.1145/358769.358784) -2. Jim N. Gray, Raymond A. Lorie, Gianfranco R. Putzolu, and Irving L. Traiger: - “[Granularity of Locks and Degrees of Consistency in a Shared Data Base](http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.92.8248&rep=rep1&type=pdf),” in *Modelling in Data - Base Management Systems: Proceedings of the IFIP Working Conference on Modelling in Data Base - Management Systems*, edited by G. M. Nijssen, pages - 364–394, Elsevier/North Holland Publishing, 1976. Also in *Readings in Database Systems*, 4th edition, edited by Joseph M. - Hellerstein and Michael Stonebraker, MIT Press, 2005. ISBN: 978-0-262-69314-1 -3. Kapali P. Eswaran, Jim N. Gray, Raymond A. Lorie, and Irving L. Traiger: - “[The Notions of Consistency and Predicate Locks in a Database System](http://research.microsoft.com/en-us/um/people/gray/papers/On%20the%20Notions%20of%20Consistency%20and%20Predicate%20Locks%20in%20a%20Database%20System%20CACM.pdf),” *Communications of the - ACM*, volume 19, number 11, pages 624–633, November 1976. +2. Jim N. Gray, Raymond A. Lorie, Gianfranco R. Putzolu, and Irving L. Traiger: “[Granularity of Locks and Degrees of Consistency in a Shared Data Base](http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.92.8248&rep=rep1&type=pdf),” in *Modelling in Data Base Management Systems: Proceedings of the IFIP Working Conference on Modelling in Data Base Management Systems*, edited by G. M. Nijssen, pages 364–394, Elsevier/North Holland Publishing, 1976. Also in *Readings in Database Systems*, 4th edition, edited by Joseph M. Hellerstein and Michael Stonebraker, MIT Press, 2005. ISBN: 978-0-262-69314-1 +3. Kapali P. Eswaran, Jim N. Gray, Raymond A. Lorie, and Irving L. Traiger: “[The Notions of Consistency and Predicate Locks in a Database System](http://research.microsoft.com/en-us/um/people/gray/papers/On%20the%20Notions%20of%20Consistency%20and%20Predicate%20Locks%20in%20a%20Database%20System%20CACM.pdf),” *Communications of the ACM*, volume 19, number 11, pages 624–633, November 1976. 4. “[ACID Transactions Are Incredibly Helpful](http://web.archive.org/web/20150320053809/https://foundationdb.com/acid-claims),” FoundationDB, LLC, 2013. 5. John D. Cook: “[ACID Versus BASE for Database Transactions](http://www.johndcook.com/blog/2009/07/06/brewer-cap-theorem-base/),” *johndcook.com*, July 6, 2009. 6. Gavin Clarke: “[NoSQL's CAP Theorem Busters: We Don't Drop ACID](http://www.theregister.co.uk/2012/11/22/foundationdb_fear_of_cap_theorem/),” *theregister.co.uk*, November 22, 2012. -7. Theo Härder and Andreas Reuter: - “[Principles of Transaction-Oriented Database Recovery](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.87.2812&rep=rep1&type=pdf),” *ACM Computing Surveys*, - volume 15, number 4, pages 287–317, December 1983. - [doi:10.1145/289.291](http://dx.doi.org/10.1145/289.291) -8. Peter Bailis, Alan Fekete, Ali Ghodsi, et al.: - “[HAT, not CAP: Towards Highly Available Transactions](http://www.bailis.org/papers/hat-hotos2013.pdf),” +7. Theo Härder and Andreas Reuter: “[Principles of Transaction-Oriented Database Recovery](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.87.2812&rep=rep1&type=pdf),” *ACM Computing Surveys*, volume 15, number 4, pages 287–317, December 1983. [doi:10.1145/289.291](http://dx.doi.org/10.1145/289.291) +8. Peter Bailis, Alan Fekete, Ali Ghodsi, et al.: “[HAT, not CAP: Towards Highly Available Transactions](http://www.bailis.org/papers/hat-hotos2013.pdf),” at *14th USENIX Workshop on Hot Topics in Operating Systems* (HotOS), May 2013. -9. Armando Fox, Steven D. Gribble, Yatin Chawathe, et al.: - “[Cluster-Based Scalable Network Services](http://www.cs.berkeley.edu/~brewer/cs262b/TACC.pdf),” at +9. Armando Fox, Steven D. Gribble, Yatin Chawathe, et al.: “[Cluster-Based Scalable Network Services](http://www.cs.berkeley.edu/~brewer/cs262b/TACC.pdf),” at *16th ACM Symposium on Operating Systems Principles* (SOSP), October 1997. -10. Philip A. Bernstein, Vassos Hadzilacos, and Nathan Goodman: - *Concurrency - Control and Recovery in Database Systems*. 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. +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. -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. -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), +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. +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 +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. -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. +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. +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. -23. Martin Kleppmann: - “[Hermitage: Testing the 'I' in ACID](http://martin.kleppmann.com/2014/11/25/hermitage-testing-the-i-in-acid.html),” *martin.kleppmann.com*, November 25, 2014. -24. Tristan D'Agosta: - “[BTC Stolen from Poloniex](https://bitcointalk.org/index.php?topic=499580),” - *bitcointalk.org*, March 4, 2014. -25. bitcointhief2: - “[How I Stole Roughly 100 BTC from an Exchange and How I Could Have Stolen More!](http://www.reddit.com/r/Bitcoin/comments/1wtbiu/how_i_stole_roughly_100_btc_from_an_exchange_and/),” *reddit.com*, - February 2, 2014. -26. 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. -27. Michael Melanson: - “[Transactions: The Limits of Isolation](http://www.michaelmelanson.net/2014/03/20/transactions/),” *michaelmelanson.net*, March 20, 2014. -28. Hal Berenson, Philip A. Bernstein, Jim N. Gray, et al.: - “[A Critique of ANSI SQL Isolation Levels](http://research.microsoft.com/pubs/69541/tr-95-51.pdf),” +23. Martin Kleppmann: “[Hermitage: Testing the 'I' in ACID](http://martin.kleppmann.com/2014/11/25/hermitage-testing-the-i-in-acid.html),” *martin.kleppmann.com*, November 25, 2014. +24. Tristan D'Agosta: “[BTC Stolen from Poloniex](https://bitcointalk.org/index.php?topic=499580),” *bitcointalk.org*, March 4, 2014. +25. bitcointhief2: “[How I Stole Roughly 100 BTC from an Exchange and How I Could Have Stolen More!](http://www.reddit.com/r/Bitcoin/comments/1wtbiu/how_i_stole_roughly_100_btc_from_an_exchange_and/),” *reddit.com*, February 2, 2014. +26. 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. +27. Michael Melanson: “[Transactions: The Limits of Isolation](http://www.michaelmelanson.net/2014/03/20/transactions/),” *michaelmelanson.net*, March 20, 2014. +28. Hal Berenson, Philip A. Bernstein, Jim N. Gray, et al.: “[A Critique of ANSI SQL Isolation Levels](http://research.microsoft.com/pubs/69541/tr-95-51.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), May 1995. -29. Atul Adya: “[Weak Consistency: A Generalized Theory and Optimistic Implementations for Distributed Transactions](http://pmg.csail.mit.edu/papers/adya-phd.pdf),” - PhD Thesis, Massachusetts Institute of Technology, March 1999. -30. Peter Bailis, Aaron Davidson, Alan Fekete, et al.: - “[Highly Available Transactions: Virtues and Limitations (Extended Version)](http://arxiv.org/pdf/1302.0309.pdf),” at *40th International Conference on Very Large Data Bases* - (VLDB), September 2014. -31. Bruce Momjian: - “[MVCC Unmasked](http://momjian.us/main/presentations/internals.html#mvcc),” *momjian.us*, - July 2014. -32. Annamalai Gurusami: - “[Repeatable Read Isolation Level in InnoDB – How Consistent Read View Works](https://blogs.oracle.com/mysqlinnodb/entry/repeatable_read_isolation_level_in),” *blogs.oracle.com*, - January 15, 2013. -33. Nikita Prokopov: - “[Unofficial Guide to Datomic Internals](http://tonsky.me/blog/unofficial-guide-to-datomic-internals/),” *tonsky.me*, May 6, 2014. -34. 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. -35. J. Chris Anderson, Jan Lehnardt, and Noah Slater: - *CouchDB: The Definitive Guide*. O'Reilly Media, 2010. - ISBN: 978-0-596-15589-6 -36. Rikdeb Mukherjee: - “[Isolation in DB2 (Repeatable Read, Read Stability, Cursor Stability, Uncommitted Read) with Examples](http://mframes.blogspot.co.uk/2013/07/isolation-in-cursor.html),” - *mframes.blogspot.co.uk*, July 4, 2013. -37. Steve Hilker: - “[Cursor Stability (CS) – IBM DB2 Community](http://www.toadworld.com/platforms/ibmdb2/w/wiki/6661.cursor-stability-cs.aspx),” *toadworld.com*, March 14, 2013. -38. Nate Wiger: - “[An Atomic Rant](http://www.nateware.com/an-atomic-rant.html),” *nateware.com*, - February 18, 2010. -39. Joel Jacobson: - “[Riak 2.0: Data Types](http://blog.joeljacobson.com/riak-2-0-data-types/),” - *blog.joeljacobson.com*, March 23, 2014. -40. 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) -41. 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. -42. 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. -43. 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) -44. Gary Fredericks: - “[Postgres Serializability Bug](https://github.com/gfredericks/pg-serializability-bug),” *github.com*, September 2015. -45. 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. -46. 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. -47. 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. -48. Rich Hickey: - “[The Architecture of Datomic](http://www.infoq.com/articles/Architecture-Datomic),” *infoq.com*, November 2, 2012. -49. John Hugg: - “[Debunking Myths About the VoltDB In-Memory Database](http://voltdb.com/blog/debunking-myths-about-voltdb-memory-database),” *voltdb.com*, May 12, 2014. -50. Joseph M. Hellerstein, Michael Stonebraker, and James Hamilton: - “[Architecture of a Database System](http://db.cs.berkeley.edu/papers/fntdb07-architecture.pdf),” +29. Atul Adya: “[Weak Consistency: A Generalized Theory and Optimistic Implementations for Distributed Transactions](http://pmg.csail.mit.edu/papers/adya-phd.pdf),” PhD Thesis, Massachusetts Institute of Technology, March 1999. +30. Peter Bailis, Aaron Davidson, Alan Fekete, et al.: “[Highly Available Transactions: Virtues and Limitations (Extended Version)](http://arxiv.org/pdf/1302.0309.pdf),” at *40th International Conference on Very Large Data Bases* (VLDB), September 2014. +31. Bruce Momjian: “[MVCC Unmasked](http://momjian.us/main/presentations/internals.html#mvcc),” *momjian.us*, July 2014. +32. Annamalai Gurusami: “[Repeatable Read Isolation Level in InnoDB – How Consistent Read View Works](https://blogs.oracle.com/mysqlinnodb/entry/repeatable_read_isolation_level_in),” *blogs.oracle.com*, January 15, 2013. +33. Nikita Prokopov: “[Unofficial Guide to Datomic Internals](http://tonsky.me/blog/unofficial-guide-to-datomic-internals/),” *tonsky.me*, May 6, 2014. +34. 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. +35. J. Chris Anderson, Jan Lehnardt, and Noah Slater: *CouchDB: The Definitive Guide*. O'Reilly Media, 2010. + ISBN: 978-0-596-15589-6 Rikdeb Mukherjee: “[Isolation in DB2 (Repeatable Read, Read Stability, Cursor Stability, Uncommitted Read) with Examples](http://mframes.blogspot.co.uk/2013/07/isolation-in-cursor.html),” *mframes.blogspot.co.uk*, July 4, 2013. +36. Steve Hilker: “[Cursor Stability (CS) – IBM DB2 Community](http://www.toadworld.com/platforms/ibmdb2/w/wiki/6661.cursor-stability-cs.aspx),” *toadworld.com*, March 14, 2013. +37. Nate Wiger: “[An Atomic Rant](http://www.nateware.com/an-atomic-rant.html),” *nateware.com*, February 18, 2010. +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. +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. +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. +47. Rich Hickey: “[The Architecture of Datomic](http://www.infoq.com/articles/Architecture-Datomic),” *infoq.com*, November 2, 2012. +48. John Hugg: “[Debunking Myths About the VoltDB In-Memory Database](http://voltdb.com/blog/debunking-myths-about-voltdb-memory-database),” *voltdb.com*, May 12, 2014. +49. Joseph M. Hellerstein, Michael Stonebraker, and James Hamilton: “[Architecture of a Database System](http://db.cs.berkeley.edu/papers/fntdb07-architecture.pdf),” *Foundations and Trends in Databases*, volume 1, number 2, pages 141–259, November 2007. [doi:10.1561/1900000002](http://dx.doi.org/10.1561/1900000002) -51. Michael J. Cahill: - “[Serializable Isolation for Snapshot Databases](http://cahill.net.au/wp-content/uploads/2010/02/cahill-thesis.pdf),” PhD Thesis, University of Sydney, July 2009. -52. D. Z. Badal: - “[Correctness of Concurrency Control and Implications in Distributed Databases](http://ieeexplore.ieee.org/abstract/document/762563/),” at *3rd International IEEE Computer Software and - Applications Conference* (COMPSAC), November 1979. -53. Rakesh Agrawal, Michael J. Carey, and Miron Livny: - “[Concurrency Control Performance Modeling: Alternatives and Implications](http://www.eecs.berkeley.edu/~brewer/cs262/ConcControl.pdf),” *ACM Transactions on Database - Systems* (TODS), volume 12, number 4, pages 609–654, December 1987. - [doi:10.1145/32204.32220](http://dx.doi.org/10.1145/32204.32220) -54. Dave Rosenthal: - “[Databases at 14.4MHz](http://web.archive.org/web/20150427041746/http://blog.foundationdb.com/databases-at-14.4mhz),” - *blog.foundationdb.com*, December 10, 2014. +50. Michael J. Cahill: “[Serializable Isolation for Snapshot Databases](http://cahill.net.au/wp-content/uploads/2010/02/cahill-thesis.pdf),” PhD Thesis, University of Sydney, July 2009. +51. D. Z. Badal: “[Correctness of Concurrency Control and Implications in Distributed Databases](http://ieeexplore.ieee.org/abstract/document/762563/),” at *3rd International IEEE Computer Software and Applications Conference* (COMPSAC), November 1979. +52. Rakesh Agrawal, Michael J. Carey, and Miron Livny: “[Concurrency Control Performance Modeling: Alternatives and Implications](http://www.eecs.berkeley.edu/~brewer/cs262/ConcControl.pdf),” *ACM Transactions on Database Systems* (TODS), volume 12, number 4, pages 609–654, December 1987. [doi:10.1145/32204.32220](http://dx.doi.org/10.1145/32204.32220) +53. Dave Rosenthal: “[Databases at 14.4MHz](http://web.archive.org/web/20150427041746/http://blog.foundationdb.com/databases-at-14.4mhz),” *blog.foundationdb.com*, December 10, 2014. ------ diff --git a/ddia/ch9.md b/ddia/ch9.md index 7406d92771340000b9dcc02ba39fb1d44b6e6934..4b18fd46a86963adc1fb1f60829bbd4a4f33fa0d 100644 --- a/ddia/ch9.md +++ b/ddia/ch9.md @@ -217,13 +217,13 @@ 一些共识算法,我们将在本章后面讨论,与单引导者复制相似。然而,共识协议包含措施,以防止裂脑和陈旧的复制品。由于这些细节,协调算法可以安全地实现线性化存储。例如,Zoo-Keeper [21]和etcd [22]就是这样工作的。 -***多领导复制(不可线性化)*** +***多主复制(不可线性化)*** 具有多引导程序复制的系统通常不是线性化的,因为它们同时在多个节点上处理写入,并将其异步复制到其他节点。由于这个原因,它们可能会产生冲突的写入,需要解析(请参阅第171页的“处理写入冲突”)。这种冲突是缺乏单一数据副本的人为因素。 -***无领导复制(可能不是线性化的)*** +***无主复制(可能不是线性化的)*** -对于无领导者复制的系统(Dynamo风格;请参阅第177页的“无领导者复制”),有时候人们会声称通过要求法定读写(w + r> n)可以获得“强一致性”。根据法定人数的确切配置,取决于您如何界定强一致性,这是不正确的。 +对于无领导者复制的系统(Dynamo风格;请参阅第177页的“无主复制”),有时候人们会声称通过要求法定读写(w + r> n)可以获得“强一致性”。根据法定人数的确切配置,取决于您如何界定强一致性,这是不正确的。 基于时钟(例如,在Cassandra中;参见第291页上的“依赖于同步时钟”)的“最后写入胜利”冲突解决方法几乎是非线性的,因为时钟时间戳不能保证与时间戳一致由于时钟歪斜而导致的实际事件排序。不规范的法定人数(第183页的“马虎法定人数和暗示交接”)也破坏了线性化的可能性。即使是严格的法定人数,非线性行为也是可能的,如下一节所示。 @@ -280,7 +280,9 @@ CAP最初是作为一个经验法则提出的,没有准确的定义,目的 > ### CAP定理没有帮助 > -> CAP有时表现为一致性,可用性和分区容忍度:从3中挑选出2个。不幸的是,这样做是误导的[32],因为网络分区是一种错误,所以它们不是你所拥有的一个选择:他们会发生,不管你喜欢还是不喜欢[38]。 +> CAP有时以这种面目出现:一致性,可用性和分区容忍:三者只能择其二。 +> +> 个中只能选择从3中挑选出2个。不幸的是,这样做是误导的[32],因为网络分区是一种错误,所以它们不是你所拥有的一个选择:他们会发生,不管你喜欢还是不喜欢[38]。 > > 在网络正常工作的时候,系统可以提供一致性(线性化)和总体可用性。发生网络故障时,您必须选择线性或总可用性。因此,一个更好的表达CAP的方法可以是一致的,或者在分区时可用[39]。一个更可靠的网络需要减少这个选择,但是在某些时候选择是不可避免的。 > @@ -953,9 +955,7 @@ ZooKeeper和朋友们可以看作是成员服务研究的悠久历史的一部 “[Eventual Consistency Today: Limitations, Extensions, and Beyond](http://queue.acm.org/detail.cfm?id=2462076),” *ACM Queue*, volume 11, number 3, pages 55-63, March 2013. [doi:10.1145/2460276.2462076](http://dx.doi.org/10.1145/2460276.2462076) -1. Prince Mahajan, Lorenzo Alvisi, and Mike Dahlin: - “[Consistency, Availability, and Convergence](http://apps.cs.utexas.edu/tech_reports/reports/tr/TR-2036.pdf),” University of Texas at Austin, Department of Computer - Science, Tech Report UTCS TR-11-22, May 2011. +1. Prince Mahajan, Lorenzo Alvisi, and Mike Dahlin: “[Consistency, Availability, and Convergence](http://apps.cs.utexas.edu/tech_reports/reports/tr/TR-2036.pdf),” University of Texas at Austin, Department of Computer Science, Tech Report UTCS TR-11-22, May 2011. 1. Alex Scotti: “[Adventures in Building Your Own Database](http://www.slideshare.net/AlexScotti1/allyourbase-55212398),” at *All Your Base*, November 2015. @@ -1108,9 +1108,7 @@ ZooKeeper和朋友们可以看作是成员服务研究的悠久历史的一部 [doi:10.1145/72981.72982](http://dx.doi.org/10.1145/72981.72982) 1. Hagit Attiya, Faith Ellen, and Adam Morrison: - “[Limitations of Highly-Available Eventually-Consistent Data Stores](http://www.cs.technion.ac.il/people/mad/online-publications/podc2015-replds.pdf),” at *ACM Symposium on Principles of - Distributed Computing* (PODC), July 2015. - [doi:10.1145/2767386.2767419](http://dx.doi.org/10.1145/2767386.2767419) + “[Limitations of Highly-Available Eventually-Consistent Data Stores](http://www.cs.technion.ac.il/people/mad/online-publications/podc2015-replds.pdf),” at *ACM Symposium on Principles of Distributed Computing* (PODC), July 2015. [doi:10.1145/2767386.2767419](http://dx.doi.org/10.1145/2767386.2767419) 1. Peter Sewell, Susmit Sarkar, Scott Owens, et al.: