Skip to content

Commit

Permalink
remove nested parentheses
Browse files Browse the repository at this point in the history
  • Loading branch information
yingang committed Dec 13, 2021
1 parent 07c0c84 commit 2014894
Show file tree
Hide file tree
Showing 24 changed files with 90 additions and 90 deletions.
4 changes: 2 additions & 2 deletions ch1.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@

直到最近,硬件冗余对于大多数应用来说已经足够了,它使单台机器完全失效变得相当罕见。只要你能快速地把备份恢复到新机器上,故障停机时间对大多数应用而言都算不上灾难性的。只有少量高可用性至关重要的应用才会要求有多套硬件冗余。

但是随着数据量和应用计算需求的增加,越来越多的应用开始大量使用机器,这会相应地增加硬件故障率。此外在一些云平台(**如亚马逊网络服务(AWS, Amazon Web Services)**)中,虚拟机实例不可用却没有任何警告也是很常见的【7】,因为云平台的设计就是优先考虑**灵活性(flexibility)****弹性(elasticity)**[^i],而不是单机可靠性。
但是随着数据量和应用计算需求的增加,越来越多的应用开始大量使用机器,这会相应地增加硬件故障率。此外,在类似亚马逊AWS(Amazon Web Services)的一些云服务平台上,虚拟机实例不可用却没有任何警告也是很常见的【7】,因为云平台的设计就是优先考虑**灵活性(flexibility)****弹性(elasticity)**[^i],而不是单机可靠性。

如果在硬件冗余的基础上进一步引入软件容错机制,那么系统在容忍整个(单台)机器故障的道路上就更进一步了。这样的系统也有运维上的便利,例如:如果需要重启机器(例如应用操作系统安全补丁),单服务器系统就需要计划停机。而允许机器失效的系统则可以一次修复一个节点,无需整个系统停机。

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

为了弄清异常值有多糟糕,可以看看更高的百分位点,例如第959999.9百分位点(缩写为p95,p99和p999)。它们意味着95%,99%或99.9%的请求响应时间要比该阈值快,例如:如果第95百分位点响应时间是1.5秒,则意味着100个请求中的95个响应时间快于1.5秒,而100个请求中的5个响应时间超过1.5秒。如[图1-4](img/fig1-4.png)所示。

响应时间的高百分位点(也称为**尾部延迟tail latencies**)非常重要,因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时以99.9百分位点为准,即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户,也可以说是最有价值的客户 —— 因为他们掏钱了【19】。保证网站响应迅速对于保持客户的满意度非常重要,亚马逊观察到:响应时间增加100毫秒,销售量就减少1%【20】;而另一些报告说:慢 1 秒钟会让客户满意度指标减少16%【2122】。
响应时间的高百分位点(也称为**尾部延迟**,即**tail latencies**)非常重要,因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时以99.9百分位点为准,即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户,也可以说是最有价值的客户 —— 因为他们掏钱了【19】。保证网站响应迅速对于保持客户的满意度非常重要,亚马逊观察到:响应时间增加100毫秒,销售量就减少1%【20】;而另一些报告说:慢 1 秒钟会让客户满意度指标减少16%【2122】。

另一方面,优化第99.99百分位点(一万个请求中最慢的一个)被认为太昂贵了,不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难,因为它很容易受到随机事件的影响,这超出了控制范围,而且效益也很小。

Expand Down
8 changes: 4 additions & 4 deletions ch10.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ MapReduce与Unix命令管道的主要区别在于,MapReduce可以在多台机

在大多数情况下,应该在Mapper任务中运行的应用代码在将要运行它的机器上还不存在,所以MapReduce框架首先将代码(例如Java程序中的JAR文件)复制到适当的机器。然后启动Map任务并开始读取输入文件,一次将一条记录传入Mapper回调函数。Mapper的输出由键值对组成。

计算的Reduce端也被分区。虽然Map任务的数量由输入文件块的数量决定,但Reducer的任务的数量是由作业作者配置的(它可以不同于Map任务的数量)。为了确保具有相同键的所有键值对最终落在相同的Reducer处,框架使用键的散列值来确定哪个Reduce任务应该接收到特定的键值对(请参阅“[根据键的散列分区](ch6.md#根据键的散列分区)”)
计算的Reduce端也被分区。虽然Map任务的数量由输入文件块的数量决定,但Reducer的任务的数量是由作业作者配置的(它可以不同于Map任务的数量)。为了确保具有相同键的所有键值对最终落在相同的Reducer处,框架使用键的散列值来确定哪个Reduce任务应该接收到特定的键值对(请参阅“[根据键的散列分区](ch6.md#根据键的散列分区)”)。

键值对必须进行排序,但数据集可能太大,无法在单台机器上使用常规排序算法进行排序。相反,分类是分阶段进行的。首先每个Map任务都按照Reducer对输出进行分区。每个分区都被写入Mapper程序的本地磁盘,使用的技术与我们在“[SSTables与LSM树](ch3.md#SSTables与LSM树)”中讨论的类似。

Expand Down Expand Up @@ -290,7 +290,7 @@ Hadoop的各种高级工具(如Pig 【30】,Hive 【31】,Cascading 【32

#### 示例:用户活动事件分析

[图10-2](img/fig10-2.png)给出了一个批处理作业中连接的典型例子。左侧是事件日志,描述登录用户在网站上做的事情(称为**活动事件(activity events)** **点击流数据(clickstream data)**),右侧是用户数据库。 你可以将此示例看作是星型模式的一部分(请参阅“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”):事件日志是事实表,用户数据库是其中的一个维度。
[图10-2](img/fig10-2.png)给出了一个批处理作业中连接的典型例子。左侧是事件日志,描述登录用户在网站上做的事情(称为**活动事件**,即activity events,**点击流数据**,即clickstream data),右侧是用户数据库。 你可以将此示例看作是星型模式的一部分(请参阅“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”):事件日志是事实表,用户数据库是其中的一个维度。

![](img/fig10-2.png)

Expand Down Expand Up @@ -344,7 +344,7 @@ Hadoop的各种高级工具(如Pig 【30】,Hive 【31】,Cascading 【32

如果存在与单个键关联的大量数据,则“将具有相同键的所有记录放到相同的位置”这种模式就被破坏了。例如在社交网络中,大多数用户可能会与几百人有连接,但少数名人可能有数百万的追随者。这种不成比例的活动数据库记录被称为**关键对象(linchpin object)**【38】或**热键(hot key)**

在单个Reducer中收集与某个名人相关的所有活动(例如他们发布内容的回复)可能导致严重的**偏斜**(也称为**热点(hot spot)**)—— 也就是说,一个Reducer必须比其他Reducer处理更多的记录(请参阅“[负载偏斜与热点消除](ch6.md#负载偏斜与热点消除)“)。由于MapReduce作业只有在所有Mapper和Reducer都完成时才完成,所有后续作业必须等待最慢的Reducer才能启动。
在单个Reducer中收集与某个名人相关的所有活动(例如他们发布内容的回复)可能导致严重的**偏斜**(也称为**热点**,即hot spot)—— 也就是说,一个Reducer必须比其他Reducer处理更多的记录(请参阅“[负载偏斜与热点消除](ch6.md#负载偏斜与热点消除)“)。由于MapReduce作业只有在所有Mapper和Reducer都完成时才完成,所有后续作业必须等待最慢的Reducer才能启动。

如果连接的输入存在热键,可以使用一些算法进行补偿。例如,Pig中的**偏斜连接(skewed join)** 方法首先运行一个抽样作业(Sampling Job)来确定哪些键是热键【39】。连接实际执行时,Mapper会将热键的关联记录**随机**(相对于传统MapReduce基于键散列的确定性方法)发送到几个Reducer之一。对于另外一侧的连接输入,与热键相关的记录需要被复制到**所有**处理该键的Reducer上【40】。

Expand Down Expand Up @@ -603,7 +603,7 @@ Spark,Flink和Tez避免将中间状态写入HDFS,因此它们采取了不同

> 像Spark,Flink和Tez这样的数据流引擎(请参阅“[物化中间状态](#物化中间状态)”)通常将算子作为**有向无环图(DAG)** 的一部分安排在作业中。这与图处理不一样:在数据流引擎中,**从一个算子到另一个算子的数据流**被构造成一个图,而数据本身通常由关系型元组构成。在图处理中,数据本身具有图的形式。又一个不幸的命名混乱!
许多图算法是通过一次遍历一条边来表示的,将一个顶点与近邻的顶点连接起来,以传播一些信息,并不断重复,直到满足一些条件为止 —— 例如,直到没有更多的边要跟进,或直到一些指标收敛。我们在[图2-6](img/fig2-6.png)中看到一个例子,它通过重复跟进标明地点归属关系的边,生成了数据库中北美包含的所有地点列表(这种算法被称为**传递闭包(transitive closure)**)。
许多图算法是通过一次遍历一条边来表示的,将一个顶点与近邻的顶点连接起来,以传播一些信息,并不断重复,直到满足一些条件为止 —— 例如,直到没有更多的边要跟进,或直到一些指标收敛。我们在[图2-6](img/fig2-6.png)中看到一个例子,它通过重复跟进标明地点归属关系的边,生成了数据库中北美包含的所有地点列表(这种算法被称为**传递闭包**,即transitive closure)。

可以在分布式文件系统中存储图(包含顶点和边的列表的文件),但是这种“重复至完成”的想法不能用普通的MapReduce来表示,因为它只扫过一趟数据。这种算法因此经常以**迭代**的风格实现:

Expand Down
10 changes: 5 additions & 5 deletions ch11.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@

#### 消息代理

一种广泛使用的替代方法是通过**消息代理(message broker**也称为**消息队列(message queue)**)发送消息,消息代理实质上是一种针对处理消息流而优化的数据库。它作为服务器运行,生产者和消费者作为客户端连接到服务器。生产者将消息写入代理,消费者通过从代理那里读取来接收消息。
一种广泛使用的替代方法是通过**消息代理**(message broker也称为**消息队列**,即message queue)发送消息,消息代理实质上是一种针对处理消息流而优化的数据库。它作为服务器运行,生产者和消费者作为客户端连接到服务器。生产者将消息写入代理,消费者通过从代理那里读取来接收消息。

通过将数据集中在代理上,这些系统可以更容易地容忍来来去去的客户端(连接,断开连接和崩溃),而持久性问题则转移到代理的身上。一些消息代理只将消息保存在内存中,而另一些消息代理(取决于配置)将其写入磁盘,以便在代理崩溃的情况下不会丢失。针对缓慢的消费者,它们通常会允许无上限的排队(而不是丢弃消息或背压),尽管这种选择也可能取决于配置。

Expand All @@ -98,7 +98,7 @@

* 负载均衡(load balancing)

每条消息都被传递给消费者**之一**,所以处理该主题下消息的工作能被多个消费者共享。代理可以为消费者任意分配消息。当处理消息的代价高昂,希望能并行处理消息时,此模式非常有用(在AMQP中,可以通过让多个客户端从同一个队列中消费来实现负载均衡,而在JMS中则称之为**共享订阅(shared subscription)**)。
每条消息都被传递给消费者**之一**,所以处理该主题下消息的工作能被多个消费者共享。代理可以为消费者任意分配消息。当处理消息的代价高昂,希望能并行处理消息时,此模式非常有用(在AMQP中,可以通过让多个客户端从同一个队列中消费来实现负载均衡,而在JMS中则称之为**共享订阅**,即shared subscription)。

* 扇出(fan-out)

Expand Down Expand Up @@ -276,7 +276,7 @@ LinkedIn的Databus【25】,Facebook的Wormhole【26】和Yahoo!的Sherpa【27

我们之前在“[散列索引](ch3.md#散列索引)”中关于日志结构存储引擎的上下文中讨论了日志压缩(请参阅[图3-2](img/fig3-2.png)的示例)。原理很简单:存储引擎定期在日志中查找具有相同键的记录,丢掉所有重复的内容,并只保留每个键的最新更新。这个压缩与合并过程在后台运行。

在日志结构存储引擎中,具有特殊值NULL(**墓碑(tombstone)**)的更新表示该键被删除,并会在日志压缩过程中被移除。但只要键不被覆盖或删除,它就会永远留在日志中。这种压缩日志所需的磁盘空间仅取决于数据库的当前内容,而不取决于数据库中曾经发生的写入次数。如果相同的键经常被覆盖写入,则先前的值将最终将被垃圾回收,只有最新的值会保留下来。
在日志结构存储引擎中,具有特殊值NULL(**墓碑**,即tombstone)的更新表示该键被删除,并会在日志压缩过程中被移除。但只要键不被覆盖或删除,它就会永远留在日志中。这种压缩日志所需的磁盘空间仅取决于数据库的当前内容,而不取决于数据库中曾经发生的写入次数。如果相同的键经常被覆盖写入,则先前的值将最终将被垃圾回收,只有最新的值会保留下来。

在基于日志的消息代理与变更数据捕获的上下文中也适用相同的想法。如果CDC系统被配置为,每个变更都包含一个主键,且每个键的更新都替换了该键以前的值,那么只需要保留对键的最新写入就足够了。

Expand Down Expand Up @@ -491,7 +491,7 @@ CEP的实现包括Esper【69】,IBM InfoSphere Streams【70】,Apama,TIBCO

批处理可以在几分钟内读取一年的历史事件;在大多数情况下,感兴趣的时间线是历史中的一年,而不是处理中的几分钟。而且使用事件中的时间戳,使得处理是**确定性**的:在相同的输入上再次运行相同的处理过程会得到相同的结果(请参阅“[容错](ch10.md#容错)”)。

另一方面,许多流处理框架使用处理机器上的本地系统时钟(**处理时间(processing time)**)来确定**窗口(windowing)**【79】。这种方法的优点是简单,如果事件创建与事件处理之间的延迟可以忽略不计,那也是合理的。然而,如果存在任何显著的处理延迟 —— 即,事件处理显著地晚于事件实际发生的时间,这种处理方式就失效了。
另一方面,许多流处理框架使用处理机器上的本地系统时钟(**处理时间**,即processing time)来确定**窗口(windowing)**【79】。这种方法的优点是简单,如果事件创建与事件处理之间的延迟可以忽略不计,那也是合理的。然而,如果存在任何显著的处理延迟 —— 即,事件处理显著地晚于事件实际发生的时间,这种处理方式就失效了。

#### 事件时间与处理时间

Expand Down Expand Up @@ -706,7 +706,7 @@ Storm的Trident基于类似的想法来处理状态【78】。依赖幂等性意

* 流流连接

两个输入流都由活动事件组成,而连接算子在某个时间窗口内搜索相关的事件。例如,它可能会将同一个用户30分钟内进行的两个活动联系在一起。如果你想要找出一个流内的相关事件,连接的两侧输入可能实际上都是同一个流(**自连接(self-join)**)。
两个输入流都由活动事件组成,而连接算子在某个时间窗口内搜索相关的事件。例如,它可能会将同一个用户30分钟内进行的两个活动联系在一起。如果你想要找出一个流内的相关事件,连接的两侧输入可能实际上都是同一个流(**自连接**,即self-join)。

* 流表连接

Expand Down
Loading

0 comments on commit 2014894

Please sign in to comment.