improve words in ch09.md

This commit is contained in:
qtmuniao 2023-11-09 15:16:30 +00:00 committed by GitHub
parent 617ae9eac7
commit dfadb6998d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

18
ch09.md
View File

@ -42,7 +42,7 @@
分布式系统中的**一致性模型的强弱**和第七章讲的事物的**隔离级别层次**有一些共通之处,比如在性能和隔离性/一致性间做取舍。但他们是相对独立的抽象: 分布式系统中的**一致性模型的强弱**和第七章讲的事物的**隔离级别层次**有一些共通之处,比如在性能和隔离性/一致性间做取舍。但他们是相对独立的抽象:
1. **事务隔离级别**是为了解决并发所引起的竞态条件 1. **事务隔离级别**是为了解决并发所引起的数据竞态条件
2. **分布式一致性**是处理由于多副本间延迟和故障所引入的数据同步问题 2. **分布式一致性**是处理由于多副本间延迟和故障所引入的数据同步问题
本章涉及到很多主题,乍看起来很宽泛,但其内里是互相勾连的: 本章涉及到很多主题,乍看起来很宽泛,但其内里是互相勾连的:
@ -77,8 +77,8 @@
在本例中,寄存器支持两种类型的操作: 在本例中,寄存器支持两种类型的操作:
1. _read_(_x_) ⇒ _v:_ 客户端请求读寄存器 x 的值,数据库会返回值 v 1. `read_(x) ⇒ v`: 客户端请求读寄存器 x 的值,数据库会返回值 v
2. _write_(_x_, _v_) ⇒ _r_ 客户端请求将寄存器 x 的值设置为 v数据返回结果 r可能是成功或者失败 2. `write(x, v) ⇒ r` 客户端请求将寄存器 x 的值设置为 v数据返回结果 r可能是成功或者失败
在上图中x 初始值为 0客户端 C 发出一个写请求将其置为 1。在此期间客户端 A 和 B 不断地向数据库请求 x 的最新值。试问 A 和 B 的每个读请求都会读到什么值? 在上图中x 初始值为 0客户端 C 发出一个写请求将其置为 1。在此期间客户端 A 和 B 不断地向数据库请求 x 的最新值。试问 A 和 B 的每个读请求都会读到什么值?
@ -121,17 +121,17 @@
> **线性一致性和可串行化** > **线性一致性和可串行化**
> **线性一致性**Linearizability很容易和**可串行化**serializability相混淆因为他们看起来都意味着:可以进行顺序化组织。但他们是不同维度的约束,我们很有必要对其进行区分: > **线性一致性**Linearizability很容易和**可串行化**serializability相混淆因为他们看起来都像是:可以进行拓扑化组织。但他们是不同维度的约束,我们很有必要对其进行区分:
> **可串行化**Serializability。可串行化是事务的一种隔离级别。每个事务可能会涉及多个数据对象行、文档、记录的读写[之前](https://ddia.qtmuniao.com/#/ch07?id=%e5%8d%95%e5%af%b9%e8%b1%a1%e5%92%8c%e5%a4%9a%e5%af%b9%e8%b1%a1%e6%93%8d%e4%bd%9c)有讨论过单对象和多对象。可串行化可以保证所有事务好像按**某种顺序依次执行**(后一个事务在前一个事务结束后才开始)。需要注意的是,如果某种串行顺序和实际执行顺序不一致也没事,只要是串行执行就行。举个例子,如果 A、B、C 三个事务并发执行,真实顺序是 A、B、C但如果对应用层表现为 CAB 的执行顺序(可能由于多机时间戳不同步),也可以叫可串行化,但 CAB 的执行顺序在某个对象上可能不满足线性一致性。 > **可串行化**Serializability。可串行化是事务的一种隔离级别。每个事务可能会涉及**多个数据对象**(行、文档、记录)的读写,[之前](https://ddia.qtmuniao.com/#/ch07?id=%e5%8d%95%e5%af%b9%e8%b1%a1%e5%92%8c%e5%a4%9a%e5%af%b9%e8%b1%a1%e6%93%8d%e4%bd%9c)有讨论过单对象和多对象。可串行化可以保证所有事务好像按**某种顺序依次执行**(后一个事务在前一个事务结束后才开始)。需要注意的是,如果某种串行顺序和实际执行顺序不一致也没事,只要是串行执行就行。举个例子,如果 A、B、C 三个事务并发执行,真实顺序是 A->B->C但如果对应用层表现为 C->A->B 的执行顺序(可能由于多机时间戳不同步),也可以叫可串行化,但 C->A->B 的执行顺序在**某个**对象上可能不满足线性一致性。
> **线性一致性**Linearizability。线性一致性是一种针对寄存器register单个数据对象的读写新鲜度保证。它不会将多个操作打包成事务因此不能避免像之前提到的[写偏序](https://ddia.qtmuniao.com/#/ch07?id=%e5%86%99%e5%81%8f%e5%ba%8f%e5%92%8c%e5%b9%bb%e8%af%bb)等问题,除非使用某些辅助手段,如[物化冲突](https://ddia.qtmuniao.com/#/ch07?id=%e7%89%a9%e5%8c%96%e5%86%b2%e7%aa%81)。 > **线性一致性**Linearizability。线性一致性是一种针对寄存器register**单个数据对象**)的读写新鲜度保证。它不会将多个操作打包成事务,因此不能避免像之前提到的[写偏序](https://ddia.qtmuniao.com/#/ch07?id=%e5%86%99%e5%81%8f%e5%ba%8f%e5%92%8c%e5%b9%bb%e8%af%bb)等问题,除非使用某些辅助手段,如[物化冲突](https://ddia.qtmuniao.com/#/ch07?id=%e7%89%a9%e5%8c%96%e5%86%b2%e7%aa%81)。
> 一个数据库可以同时提供可串行化和线性一致性保证,我们称之为**严格可串行化**_strict serializability_或者**单副本可串行化**strong one-copy serializability。使用两阶段锁或者真正串行化执行实现的可串行化通常都是线性一致的。 > 一个数据库可以同时提供可串行化和线性一致性保证,我们称之为**严格可串行化**_strict serializability_或者**单副本可串行化**strong one-copy serializability。使用两阶段锁或者真正串行化执行实现的可串行化通常都是线性一致的。
> 然而,基于快照隔离的串行化**通常**不是线性一致的。为了避免读写互相阻塞,所有的读取都会基于某个一致性的快照,则该快照之后的写入不会反映到读请求上,因此,快照读不满足线性一致性。 > 然而,基于快照隔离的串行化**通常**不是线性一致的。为了避免读写互相阻塞,所有的读取都会基于某个一致性的快照,则该快照之后的写入不会反映到读请求上,因此,快照读不满足线性一致性。
## 依赖线性一致性 ## 应用线性一致性
在什么场景下我们会用到线性一致性?之前提到的获取体育赛事决赛比分不是一个能凸显其重要性的例子:在该情况下延迟几分钟得到比赛结果可能并不会引起什么实质性损害。然而,在某些领域里,线性一致性是系统能够正常工作的**重要依赖**。 在什么场景下我们会用到线性一致性?之前提到的获取体育赛事决赛比分不是一个能凸显其重要性的例子:在该情况下延迟几分钟得到比赛结果可能并不会引起什么实质性损害。然而,在某些领域里,线性一致性是系统能够正常工作的**重要依赖**。
@ -187,7 +187,7 @@
- **单主模型**Single-leader replicationpotentially linearizable - **单主模型**Single-leader replicationpotentially linearizable
在一个单主模型的系统中,主副本服务于写请求,其他副本负责备份。如果我们让读取也走主副本,或者使用同步更新从副本的策略,则该系统**有可能**满足线性一致性。但是,并不是所有单主模型的数据都提供线性一致性,有时候是故意的(比如提供快照隔离),有时候是由于并发 bug。 在一个单主模型的系统中,主副本服务于写请求,其他副本负责备份。如果我们让读取也走主副本,或者使用同步更新从副本的策略,则该系统**有可能**满足线性一致性。但是,并不是所有单主模型的数据都提供线性一致性,有时候是故意的(比如提供快照隔离),有时候是由于并发 bug。
想让主副本也负责读请求,首先我们得确切知道哪一个是主副本。就像我们在“[真相由多数节点定义](https://www.notion.so/Chapter-9-Consistency-and-Consensus-f80d66bdfb7b4d1281722914239a563a)”一节中提到的,很有可能某个节点认为他是主节点,但其事实上不是。如果这个**自以为是的主节点**delusional leader继续提供服务则系统很可能会违反线性一致性。如果使用异步同步策略节点宕机可能甚至会丢数据从而不仅违反线性一致性也违反了可持久性。 想让主副本也负责读请求,首先我们得确切知道哪一个是主副本。就像我们在“[真相由多数节点定义](https://ddia.qtmuniao.com/#/ch08?id=%e7%9c%9f%e7%9b%b8%e7%94%b1%e5%a4%9a%e6%95%b0%e6%b4%be%e5%ae%9a%e4%b9%89)”一节中提到的,很有可能某个节点认为他是主节点,但其事实上不是。如果这个**自以为是的主节点**delusional leader继续提供服务则系统很可能会违反线性一致性。如果使用异步同步策略节点宕机可能甚至会丢数据从而不仅违反线性一致性也违反了可持久性。
- **共识算法**Consensus algorithmslinearizable - **共识算法**Consensus algorithmslinearizable
我们本章稍后会讨论到有一些共识算法看起来与单主模型类似。但这些共识协议有一些阻止脑裂和过期副本的手段。由于这些额外细节共识算法可以实现安全的线性一致性存储。Zookeeper 和 etcd 就是用的这种手段。 我们本章稍后会讨论到有一些共识算法看起来与单主模型类似。但这些共识协议有一些阻止脑裂和过期副本的手段。由于这些额外细节共识算法可以实现安全的线性一致性存储。Zookeeper 和 etcd 就是用的这种手段。
- **多主模型**Multi-leader replicationnot linearizable - **多主模型**Multi-leader replicationnot linearizable
@ -244,7 +244,7 @@ Quorum 的配置是严格满足 w+r>n 的,然而这个读写序列却不是线
总而言之,**如果系统不提供线性一致性,就可以对网络故障更加鲁棒**。该洞见常被称为 **CAP 定理**,于 2000 年被 Eric Brewer 提出。不过,类似的取舍考量从 1970 年代就为分布式数据的设计人员所熟知了。 总而言之,**如果系统不提供线性一致性,就可以对网络故障更加鲁棒**。该洞见常被称为 **CAP 定理**,于 2000 年被 Eric Brewer 提出。不过,类似的取舍考量从 1970 年代就为分布式数据的设计人员所熟知了。
CAP 最初被提出只是一个为了激发数据库取舍讨论的模糊的取舍参考而非被精确定义的定理Martin 还专门写过一篇[文章](https://www.notion.so/Chapter-9-Consistency-and-Consensus-f80d66bdfb7b4d1281722914239a563a)来探讨这件事。在当时很多分布式数据库还在着眼于基于共享存储的一组机器上提供线性一致性语义。CAP 的提出,鼓励工程师们在 share-nothing 等更广阔的设计领域进行架构探索,以找出更加适合大规模可扩展 web 服务架构。在新世纪的最初十年里CAP 的提出见证并推动了当时数据库设计思潮从强一致系统转向弱一致系统(也被称为 NoSQL 架构)。 CAP 最初被提出只是一个为了激发数据库取舍讨论的模糊的取舍参考而非被精确定义的定理Martin 还专门写过一篇[文章](https://www.qtmuniao.com/2020/02/16/not-cp-or-ap/)来探讨这件事。在当时很多分布式数据库还在着眼于基于共享存储的一组机器上提供线性一致性语义。CAP 的提出,鼓励工程师们在 share-nothing 等更广阔的设计领域进行架构探索,以找出更加适合大规模可扩展 web 服务架构。在新世纪的最初十年里CAP 的提出见证并推动了当时数据库设计思潮从强一致系统转向弱一致系统(也被称为 NoSQL 架构)。
**CAP 定理**的形式化定义适用范围很窄:仅包含一种一致性模型(即线性一致性)和一种故障类型(网络分区,或者说节点存活,但互不连通)。它没有进一步说明任何关于网络延迟、宕机节点、以及其他的一些取舍考量。因此,尽管 CAP 在历史上很有影响力,但他在设计系统时缺乏实际有效指导力。 **CAP 定理**的形式化定义适用范围很窄:仅包含一种一致性模型(即线性一致性)和一种故障类型(网络分区,或者说节点存活,但互不连通)。它没有进一步说明任何关于网络延迟、宕机节点、以及其他的一些取舍考量。因此,尽管 CAP 在历史上很有影响力,但他在设计系统时缺乏实际有效指导力。