分区(partitioning)

把一大堆数据分成一群小数据集放在不同的节点。

好处:扩展性,一次查询的负载分散在不同的节点上:每个节点分担一些子查询。

分区和副本

分区和副本通常是一起出现的。通常来说,一个分区的数据通常有多个副本。

有一种集群架构如下图所示:

键-值数据的分区

分区的基本原则:把数据均匀地分布在不同的节点上。

数据倾斜: 某些分区的数据量或者查询量比其他分区多得多。

数据量或者查询量高得不正常的分区叫做过热点

依据键的范围来分区

一种分区方式是:每个分区存放一堆连续范围的键,就像百科全书一样。

这种方式的重点:连续键的边界

分区的边界可以由管理人员手动选择,也可以由数据库自动选择。

在每个分区内,所有的键可以按序存储:范围查询变得容易。缺点是:负载不均衡。

依据键的哈希值来分区

如图所示:

好处:实在是很均匀

分区的边界可以是均匀分割的,也可以是随机决定的(这种情况就是哈希一致性)。

有一个槽点:

坏处:范围查询不好做

在 MongoDB 中,如果开启了 哈希分片模式,那么任何的范围查询将会被发送到所有的分区
在 Cassandra 中,一种中和的方式:
每个表中声明一种有多列组合成的 Compound primary key,其中第一列用于被哈希以决定数据被放在哪个分区,其他列用于构建排序的索引。因此,不能通过第一列来做范围查询,但是当知道第一列的值之后,再在其他列上做范围查询将非常有效率。

负载倾斜和热点缓和

哈希分区也有极端情况:当读写都是对同一个 Key 时,仍然有负载倾斜的情况。

这种情况也不是那么不常见: 比如 Twitter 的名人。这些名人对应的读写量非常大。而对这些 User 的 ID 的哈希没有负载均衡的意义。

一种解决办法是: 在写的时候,在这些热点 ID 前随机加上一个数字,这可以把写操作分散到 10 个节点上。但带来的问题是:读操作变得麻烦,因为需要读 10 个节点然后把数据集合到一起。

考虑到性能和数据量,决定权在你手上。

分区和二级索引

二级索引 通常作为一种搜索某个特定值的出现的方法。二级索引是关系型数据库的基础,在 NoSQL 数据库中也很常见。但是,在 Key-Value 型的数据库中不是很受欢迎,因为带来了复杂度。

另外,二级索引是搜索引擎的存在的原因。

二级索引的问题是它们不能整齐地映射到分区
使用二级索引对数据库进行分区有两种主要方法:基于文档的分区和基于术语的分区

基于文档的二级索引分区

这种分区方法中,每个分区只维护属于自己的文档的索引。所有的写操作只在本分区进行,所以也称作 本地索引

但是读操作就比较麻烦,每个读操作都需要应用在每个分区 —> 先找,然后合并。

这种查询方式被称作 分散/聚集(scatter/gather)。虽然这种方式的读操作很昂贵,但大多数数据库都是采用的这种方式。

基于术语的二级索引分区

相对应的,构建一个包含所有分区数据的二级索引被称作 全局索引。但是全局索引也必须要分区存放在不同的节点上,不然就会成为瓶颈。

如图所示,字母以 a ~ r 开头的颜色被放在第 0 个分区,s ~ z 的被放在 1 分区。

这种对二级索引的分区方式称作 基于术语分区。在图中,color:red就是术语。

这种方式的好处是对与读操作而言效率变高了:只需要读一个分区就好了。
但是对写操作变复杂了,有可能只改变了一个文档的情况下会更改多个索引分区。

另外,全局索引的同步机制是异步的:文档的更新不会实时反应在索引上,数据的一致性也一样。

增加分区

出于对性能和容量的考虑,分区扩展是常有的是。

几点要求:

  • 分区增加后,各方面的负载(存储,读,写)都应该均匀。
  • 增加的过程中不能停服;
  • 在增加分区的过程中只移动需要移动的数据。

为了以后扩展考虑,不要用 hash mod N 的方式来分区,因为当 N 增加的时候,数据的转移非常昂贵。

分区数量固定

如图所示: 定义分区数量远远大于节点数量

很骚的操作。

这种方式下,分区数量一般来说是固定的。选择好的分区数量是个技术活,你懂的,不多说.

动态分区

当一个分区里数据量过大时,这个分区自动变成了两个;相反的,当一个分区的数据量太少了,会自动合并。
这个行为和 B 树 很类似。

HBase 这种 key range-partitioned 数据库就是使用动态分区。

固定数量分区 类似,每个节点有多个分区,当一个分区变成两个时,其中一个会跑到另一个节点上,从而均衡负载。在 HBase 中,这是通过 HDFS 来实现的。

动态分区好处多多,不用赘述。

按比例分配节点

还有一种分区方式:每个节点的分区数量相同

当有新节点加入集群时,随机挑选固定个数的分区做分裂,然后分给新的节点。

随机二字不简单。。。

请求路由

又被称作 服务发现

有几种方式可以解决这个问题:

关键问题:节点或者数据有更新该怎么办

有很多分布式数据系统都依赖一个单独的协作服务,比如 ZooKeeper 来跟踪数据集群的元信息。

routing tier 可以向 ZK 订阅相关的信息,当 ZK 中相关的信息有改动时会告知它。

另外也有 gossip protocol的实现,就像上面图中的方式一样,这种方式在节点中增加了复杂度,但是不必依赖第三方的服务。

并行查询执行

大规模并行处理 MPP(massively parallel processing)关系型数据库通常会处理非常复杂的查询。MPP 查询优化器会把这种大的复杂查询分成好几个阶段和分区,然后把它们并行地放在多个节点上执行。

第六章就到这儿啦,啦啦啦啦啦啦