Skip to content

Commit

Permalink
update several redundant referenced headings
Browse files Browse the repository at this point in the history
  • Loading branch information
Gang Yin committed Aug 24, 2021
1 parent b258b24 commit 5e5b9a1
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 72 deletions.
10 changes: 5 additions & 5 deletions ch11.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

如果消息被缓存在队列中,那么理解队列增长会发生什么是很重要的。当队列装不进内存时系统会崩溃吗?还是将消息写入磁盘?如果是这样,磁盘访问又会如何影响消息传递系统的性能【6】?

2. **如果节点崩溃或暂时脱机,会发生什么情况? —— 是否会有消息丢失?**与数据库一样,持久性可能需要写入磁盘和/或复制的某种组合(请参阅“[复制和持久性](ch7.md#复制和持久性)”),这是有代价的。如果你能接受有时消息会丢失,则可能在同一硬件上获得更高的吞吐量和更低的延迟。
2. **如果节点崩溃或暂时脱机,会发生什么情况? —— 是否会有消息丢失?**与数据库一样,持久性可能需要写入磁盘和/或复制的某种组合(请参阅“[复制与持久性](ch7.md#复制与持久性)”),这是有代价的。如果你能接受有时消息会丢失,则可能在同一硬件上获得更高的吞吐量和更低的延迟。

​ 是否可以接受消息丢失取决于应用。例如,对于周期传输的传感器读数和指标,偶尔丢失的数据点可能并不重要,因为更新的值会在短时间内发出。但要注意,如果大量的消息被丢弃,可能无法立刻意识到指标已经不正确了【7】。如果你正在对事件计数,那么它们能够可靠送达是更重要的,因为每个丢失的消息都意味着使计数器的错误扩大。

Expand Down Expand Up @@ -231,7 +231,7 @@

​ 除非有一些额外的并发检测机制,例如我们在“[检测并发写入](ch5.md#检测并发写入)”中讨论的版本向量,否则你甚至不会意识到发生了并发写入 —— 一个值将简单地以无提示方式覆盖另一个值。

​ 双重写入的另一个问题是,其中一个写入可能会失败,而另一个成功。这是一个容错问题,而不是一个并发问题,但也会造成两个系统互相不一致的结果。确保它们要么都成功要么都失败,是原子提交问题的一个例子,解决这个问题的代价是昂贵的(请参阅“[原子提交与两阶段提交(2PC)](ch7.md#原子提交与两阶段提交(2PC))”)。
​ 双重写入的另一个问题是,其中一个写入可能会失败,而另一个成功。这是一个容错问题,而不是一个并发问题,但也会造成两个系统互相不一致的结果。确保它们要么都成功要么都失败,是原子提交问题的一个例子,解决这个问题的代价是昂贵的(请参阅“[原子提交与两阶段提交](ch7.md#原子提交与两阶段提交)”)。

​ 如果你只有一个单领导者复制的数据库,那么这个领导者决定了写入顺序,而状态机复制方法可以在数据库副本上工作。然而,在[图11-4](img/fig11-4.png)中,没有单个主库:数据库可能有一个领导者,搜索索引也可能有一个领导者,但是两者都不追随对方,所以可能会发生冲突(请参阅“[多主复制](ch5.md#多主复制)“)。

Expand Down Expand Up @@ -321,7 +321,7 @@
* 用于记录更新的CDC事件通常包含记录的**完整新版本**,因此主键的当前值完全由该主键的最近事件确定,而日志压缩可以丢弃相同主键的先前事件。
* 另一方面,事件溯源在更高层次进行建模:事件通常表示用户操作的意图,而不是因为操作而发生的状态更新机制。在这种情况下,后面的事件通常不会覆盖先前的事件,所以你需要完整的历史事件来重新构建最终状态。这里进行同样的日志压缩是不可能的。

​ 使用事件溯源的应用通常有一些机制,用于存储从事件日志中导出的当前状态快照,因此它们不需要重复处理完整的日志。然而这只是一种性能优化,用来加速读取,提高从崩溃中恢复的速度;真正的目的是系统能够永久存储所有原始事件,并在需要时重新处理完整的事件日志。我们将在“[不变性的限制](#不变性的限制)”中讨论这个假设。
​ 使用事件溯源的应用通常有一些机制,用于存储从事件日志中导出的当前状态快照,因此它们不需要重复处理完整的日志。然而这只是一种性能优化,用来加速读取,提高从崩溃中恢复的速度;真正的目的是系统能够永久存储所有原始事件,并在需要时重新处理完整的事件日志。我们将在“[不变性的局限性](#不变性的局限性)”中讨论这个假设。

#### 命令和事件

Expand Down Expand Up @@ -392,7 +392,7 @@ $$

​ 如果事件日志与应用状态以相同的方式分区(例如,处理分区3中的客户事件只需要更新分区3中的应用状态),那么直接使用单线程日志消费者就不需要写入并发控制了。它从设计上一次只处理一个事件(请参阅“[真的串行执行](ch7.md#真的串行执行)”)。日志通过在分区中定义事件的序列顺序,消除了并发性的不确定性【24】。如果一个事件触及多个状态分区,那么需要做更多的工作,我们将在[第十二章](ch12.md)讨论。

#### 不变性的限制
#### 不变性的局限性

​ 许多不使用事件溯源模型的系统也还是依赖不可变性:各种数据库在内部使用不可变的数据结构或多版本数据来支持时间点快照(请参阅“[索引和快照隔离](ch7.md#索引和快照隔离)” )。 Git,Mercurial和Fossil等版本控制系统也依靠不可变的数据来保存文件的版本历史记录。

Expand Down Expand Up @@ -633,7 +633,7 @@ GROUP BY follows.follower_id

​ 在本章的最后一节中,让我们看一看流处理是如何容错的。我们在[第十章](ch10.md)中看到,批处理框架可以很容易地容错:如果MapReduce作业中的任务失败,可以简单地在另一台机器上再次启动,并且丢弃失败任务的输出。这种透明的重试是可能的,因为输入文件是不可变的,每个任务都将其输出写入到HDFS上的独立文件中,而输出仅当任务成功完成后可见。

​ 特别是,批处理容错方法可确保批处理作业的输出与没有出错的情况相同,即使实际上某些任务失败了。看起来好像每条输入记录都被处理了恰好一次 —— 没有记录被跳过,而且没有记录被处理两次。尽管重启任务意味着实际上可能会多次处理记录,但输出中的可见效果看上去就像只处理过一次。这个原则被称为**恰好一次语义(exactly-once semantics)**,尽管**有效一次(effectively-once)** 可能会是一个更写实的术语【90】。
​ 特别是,批处理容错方法可确保批处理作业的输出与没有出错的情况相同,即使实际上某些任务失败了。看起来好像每条输入记录都被处理了恰好一次 —— 没有记录被跳过,而且没有记录被处理两次。尽管重启任务意味着实际上可能会多次处理记录,但输出中的可见效果看上去就像只处理过一次。这个原则被称为**恰好一次语义(exactly-once semantics)**,尽管**等效一次(effectively-once)** 可能会是一个更写实的术语【90】。

​ 在流处理中也出现了同样的容错问题,但是处理起来没有那么直观:等待某个任务完成之后再使其输出可见并不是一个可行选项,因为你永远无法处理完一个无限的流。

Expand Down
Loading

0 comments on commit 5e5b9a1

Please sign in to comment.