fix markdown and punctuations

This commit is contained in:
keming 2020-11-09 00:05:15 +08:00
parent 1bf5a38bdc
commit bdd492deb3
3 changed files with 15 additions and 15 deletions

26
ch6.md
View File

@ -11,13 +11,13 @@
[TOC]
在[第5章](ch5.md)中,我们讨论了复制——即数据在不同节点上的副本,对于非常大的数据集,或非常高的吞吐量,仅仅进行复制是不够的:我们需要将数据进行**分区partitions**,也称为**分片sharding**[^i]
在[第5章](ch5.md)中,我们讨论了复制——即数据在不同节点上的副本,对于非常大的数据集,或非常高的吞吐量,仅仅进行复制是不够的:我们需要将数据进行**分区partitions**,也称为**分片sharding**[^i]
[^i]: 正如本章所讨论的,分区是一种有意将大型数据库分解成小型数据库的方式。它与**网络分区net splits**无关,这是节点之间网络中的一种故障类型。我们将在[第8章](ch8.md)讨论这些错误。
[^i]: 正如本章所讨论的,分区是一种有意将大型数据库分解成小型数据库的方式。它与 **网络分区net splits** 无关,这是节点之间网络中的一种故障类型。我们将在[第8章](ch8.md)讨论这些错误。
> ##### 术语澄清
>
> 上文中的**分区(partition)**,在MongoDB,Elasticsearch和Solr Cloud中被称为**分片(shard)**,在HBase中称之为**区域(Region)**Bigtable中则是 **表块tablet**Cassandra和Riak中是**虚节点vnode)**, Couchbase中叫做**虚桶(vBucket)**.但是**分区(partition)** 是约定俗成的叫法。
> 上文中的**分区(partition)**在MongoDBElasticsearch和Solr Cloud中被称为**分片(shard)**在HBase中称之为**区域(Region)**Bigtable中则是 **表块tablet**Cassandra和Riak中是**虚节点vnode)**Couchbase中叫做**虚桶(vBucket)**。但是**分区(partition)** 是约定俗成的叫法。
>
通常情况下,每条数据(每条记录,每行或每个文档)属于且仅属于一个分区。有很多方法可以实现这一点,本章将进行深入讨论。实际上,每个分区都是自己的小型数据库,尽管数据库可能支持同时进行多个分区的操作。
@ -43,7 +43,7 @@
## 键值数据的分区
假设你有大量数据并且想要分区,如何决定在哪些节点上存储哪些记录呢?
假设你有大量数据并且想要分区如何决定在哪些节点上存储哪些记录呢?
分区目标是将数据和查询负载均匀分布在各个节点上。如果每个节点公平分享数据和负载那么理论上10个节点应该能够处理10倍的数据量和10倍的单个节点的读写吞吐量暂时忽略复制
@ -51,7 +51,7 @@
避免热点最简单的方法是将记录随机分配给节点。这将在所有节点上平均分配数据,但是它有一个很大的缺点:当你试图读取一个特定的值时,你无法知道它在哪个节点上,所以你必须并行地查询所有的节点。
我们可以做得更好。现在假设您有一个简单的键值数据模型,其中您总是通过其主键访问记录。例如,在一本老式的纸质百科全书中,你可以通过标题来查找一个条目;由于所有条目按字母顺序排序,因此您可以快速找到您要查找的条目。
我们可以做得更好。现在假设您有一个简单的键值数据模型,其中您总是通过其主键访问记录。例如,在一本老式的纸质百科全书中,你可以通过标题来查找一个条目由于所有条目按字母顺序排序,因此您可以快速找到您要查找的条目。
### 根据键的范围分区
@ -75,7 +75,7 @@
由于偏斜和热点的风险,许多分布式数据存储使用散列函数来确定给定键的分区。
一个好的散列函数可以将将偏斜的数据均匀分布。假设你有一个32位散列函数,无论何时给定一个新的字符串输入它将返回一个0到$2^{32}$ -1之间的"随机"数。即使输入的字符串非常相似,它们的散列也会均匀分布在这个数字范围内。
一个好的散列函数可以将将偏斜的数据均匀分布。假设你有一个32位散列函数,无论何时给定一个新的字符串输入它将返回一个0到$2^{32}$ -1之间的“随机”数。即使输入的字符串非常相似,它们的散列也会均匀分布在这个数字范围内。
出于分区的目的散列函数不需要多么强壮的加密算法例如Cassandra和MongoDB使用MD5Voldemort使用Fowler-Noll-Vo函数。许多编程语言都有内置的简单哈希函数它们用于哈希表但是它们可能不适合分区例如在Java的`Object.hashCode()`和Ruby的`Object#hash`同一个键可能在不同的进程中有不同的哈希值【6】。
@ -89,9 +89,9 @@
> #### 一致性哈希
>
> 一致性哈希由Karger等人定义。【7】 用于跨互联网级别的缓存系统例如CDN中是一种能均匀分配负载的方法。它使用随机选择的**分区边界partition boundaries**来避免中央控制或分布式一致性的需要。 请注意这里的一致性与复制一致性请参阅第5章或ACID一致性参阅[第7章](ch7.md))无关,而是描述了重新平衡的特定方法。
> 一致性哈希由Karger等人定义。【7】 用于跨互联网级别的缓存系统例如CDN中是一种能均匀分配负载的方法。它使用随机选择的 **分区边界partition boundaries** 来避免中央控制或分布式一致性的需要。 请注意这里的一致性与复制一致性请参阅第5章或ACID一致性参阅[第7章](ch7.md))无关,而是描述了重新平衡的特定方法。
>
> 正如我们将在“[重新平衡分区](#重新平衡分区)”中所看到的,这种特殊的方法对于数据库实际上并不是很好,所以在实际中很少使用(某些数据库的文档仍然指的是一致性哈希,但是它 往往是不准确的)。 因为有可能产生混淆,所以最好避免使用一致性哈希这个术语,而只是把它称为**散列分区hash partitioning**。
> 正如我们将在“[重新平衡分区](#重新平衡分区)”中所看到的,这种特殊的方法对于数据库实际上并不是很好,所以在实际中很少使用(某些数据库的文档仍然指的是一致性哈希,但是它往往是不准确的)。 因为有可能产生混淆,所以最好避免使用一致性哈希这个术语,而只是把它称为**散列分区hash partitioning**。
不幸的是通过使用Key散列进行分区我们失去了键范围分区的一个很好的属性高效执行范围查询的能力。曾经相邻的密钥现在分散在所有分区中所以它们之间的顺序就丢失了。在MongoDB中如果您使用了基于散列的分区模式则任何范围查询都必须发送到所有分区【4】。Riak 【9】Couchbase 【10】或Voldemort不支持主键上的范围查询。
@ -107,7 +107,7 @@
如今大多数数据系统无法自动补偿这种高度偏斜的负载因此应用程序有责任减少偏斜。例如如果一个主键被认为是非常火爆的一个简单的方法是在主键的开始或结尾添加一个随机数。只要一个两位数的十进制随机数就可以将主键分散为100种不同的主键,从而存储在不同的分区中。
然而将主键进行分割之后任何读取都必须要做额外的工作因为他们必须从所有100个主键分布中读取数据并将其合并。此技术还需要额外的记录只需要对少量热点附加随机数;对于写入吞吐量低的绝大多数主键来说是不必要的开销。因此,您还需要一些方法来跟踪哪些键需要被分割。
然而将主键进行分割之后任何读取都必须要做额外的工作因为他们必须从所有100个主键分布中读取数据并将其合并。此技术还需要额外的记录只需要对少量热点附加随机数对于写入吞吐量低的绝大多数主键来说是不必要的开销。因此,您还需要一些方法来跟踪哪些键需要被分割。
也许在将来,数据系统将能够自动检测和补偿偏斜的工作负载;但现在,您需要自己来权衡。
@ -155,7 +155,7 @@
我们将这种索引称为**关键词分区term-partitioned**,因为我们寻找的关键词决定了索引的分区方式。例如,一个关键词可能是:`颜色:红色`。**关键词(Term)** 来源于来自全文搜索索引(一种特殊的次级索引),指文档中出现的所有单词。
和之前一样,我们可以通过**关键词**本身或者它的散列进行索引分区。根据它本身分区对于范围扫描非常有用(例如对于数字,像汽车的报价),而对关键词的哈希分区提供了负载均衡的能力。
和之前一样,我们可以通过**关键词**本身或者它的散列进行索引分区。根据它本身分区对于范围扫描非常有用(例如对于数字像汽车的报价),而对关键词的哈希分区提供了负载均衡的能力。
关键词分区的全局索引优于文档分区索引的地方点是它可以使读取更有效率:不需要**分散/收集**所有分区,客户端只需要向包含关键词的分区发出请求。全局索引的缺点在于写入速度较慢且较为复杂,因为写入单个文档现在可能会影响索引的多个分区(文档中的每个关键词可能位于不同的分区或者不同的节点上) 。
@ -190,7 +190,7 @@
我们在前面说过([图6-3](img/fig6-3.png)),最好将可能的散列分成不同的范围,并将每个范围分配给一个分区(例如,如果$0≤hash(key)<b_0$则将键分配给分区0如果$b_0 hash(key) <b_1$则分配给分区1
也许你想知道为什么我们不使用***mod***(许多编程语言中的%运算符)。例如,`hash(key) mod 10`会返回一个介于0和9之间的数字如果我们将散列写为十进制数散列模10将是最后一个数字。如果我们有10个节点编号为0到9这似乎是将每个键分配给一个节点的简单方法。
也许你想知道为什么我们不使用 ***mod***(许多编程语言中的%运算符)。例如,`hash(key) mod 10`会返回一个介于0和9之间的数字如果我们将散列写为十进制数散列模10将是最后一个数字。如果我们有10个节点编号为0到9这似乎是将每个键分配给一个节点的简单方法。
模$N$方法的问题是如果节点数量N发生变化大多数密钥将需要从一个节点移动到另一个节点。例如假设$hash(key)=123456$。如果最初有10个节点那么这个键一开始放在节点6上因为$123456\ mod\ 10 = 6$。当您增长到11个节点时密钥需要移动到节点3$123456\ mod\ 11 = 3$当您增长到12个节点时需要移动到节点0$123456\ mod\ 12 = 0$)。这种频繁的举动使得重新平衡过于昂贵。
@ -222,7 +222,7 @@
每个分区分配给一个节点每个节点可以处理多个分区就像固定数量的分区一样。大型分区拆分后可以将其中的一半转移到另一个节点以平衡负载。在HBase中分区文件的传输通过HDFS底层分布式文件系统来实现【3】。
动态分区的一个优点是分区数量适应总数据量。如果只有少量的数据,少量的分区就足够了,所以开销很小;如果有大量的数据每个分区的大小被限制在一个可配置的最大值【23】。
动态分区的一个优点是分区数量适应总数据量。如果只有少量的数据,少量的分区就足够了,所以开销很小如果有大量的数据每个分区的大小被限制在一个可配置的最大值【23】。
需要注意的是一个空的数据库从一个分区开始因为没有关于在哪里绘制分区边界的先验信息。数据集开始时很小直到达到第一个分区的分割点所有写入操作都必须由单个节点处理而其他节点则处于空闲状态。为了解决这个问题HBase和MongoDB允许在一个空的数据库上配置一组初始分区这被称为**预分割pre-splitting**。在键范围分区的情况中预分割需要提前知道键是如何进行分配的【4,26】。
@ -258,7 +258,7 @@
概括来说这个问题有几种不同的方案如图6-7所示:
1. 允许客户联系任何节点(例如,通过**循环策略的负载均衡Round-Robin Load Balancer**)。如果该节点恰巧拥有请求的分区,则它可以直接处理该请求;否则,它将请求转发到适当的节点,接收回复并传递给客户端。
1. 允许客户联系任何节点(例如,通过**循环策略的负载均衡Round-Robin Load Balancer**)。如果该节点恰巧拥有请求的分区,则它可以直接处理该请求否则,它将请求转发到适当的节点,接收回复并传递给客户端。
2. 首先将所有来自客户端的请求发送到路由层,它决定了应该处理请求的节点,并相应地转发。此路由层本身不处理任何请求;它仅负责分区的负载均衡。
3. 要求客户端知道分区和节点的分配。在这种情况下,客户端可以直接连接到适当的节点,而不需要任何中介。

View File

@ -25,7 +25,7 @@
### 伸缩至更高的载荷
如果你需要的只是伸缩至更高的**载荷load**,最简单的方法就是购买更强大的机器(有时称为**垂直伸缩vertical scaling**或**向上伸缩scale up**)。许多处理器,内存和磁盘可以在同一个操作系统下相互连接,快速的相互连接允许任意处理器访问内存或磁盘的任意部分。在这种**共享内存架构shared-memory architecture**中,所有的组件都可以看作一台单独的机器。
如果你需要的只是伸缩至更高的**载荷load**,最简单的方法就是购买更强大的机器(有时称为**垂直伸缩vertical scaling**或**向上伸缩scale up**)。许多处理器,内存和磁盘可以在同一个操作系统下相互连接,快速的相互连接允许任意处理器访问内存或磁盘的任意部分。在这种 **共享内存架构shared-memory architecture** 中,所有的组件都可以看作一台单独的机器。
[^i]: 在大型机中,尽管任意处理器都可以访问内存的任意部分,但总有一些内存区域与一些处理器更接近(称为**非均匀内存访问nonuniform memory access, NUMA**【1】。 为了有效利用这种架构特性,需要对处理进行细分,以便每个处理器主要访问临近的内存,这意味着即使表面上看起来只有一台机器在运行,**分区partitioning**仍然是必要的。

View File

@ -28,7 +28,7 @@
## 章节概述
我们将从[第十章](ch10.md)开始研究例如MapReduce这样**面向批处理batch-oriented**的数据流系统。对于建设大规模数据系统,我们将看到,它们提供了优秀的工具和思想。[第十一章](ch11.md)将把这些思想应用到**流式数据data streams**中,使我们能用更低的延迟完成同样的任务。[第十二章](ch12.md)将对本书进行总结,探讨如何使用这些工具来构建可靠,可伸缩和可维护的应用。
我们将从[第十章](ch10.md)开始研究例如MapReduce这样 **面向批处理batch-oriented** 的数据流系统。对于建设大规模数据系统,我们将看到,它们提供了优秀的工具和思想。[第十一章](ch11.md)将把这些思想应用到 **流式数据data streams** 中,使我们能用更低的延迟完成同样的任务。[第十二章](ch12.md)将对本书进行总结,探讨如何使用这些工具来构建可靠,可伸缩和可维护的应用。
## 索引