DDIA读书笔记 | 第六章:分区

文章目录

  • 前言
      • 为什么分区:
      • 简要概括
  • 一、分区与复制
  • 二、键值数据的分区
    • 2.1 根据键的范围分区(Key Range 分区)
    • 2.2 根据键的散列分区
    • 2.3 负载偏斜与热点消除
  • 三、分区与次级索引
    • 3.1基于文档的次级索引进行分区
    • 3.2基于关键词的次级索引进行分区
  • 四、分区再平衡
    • 4.1再平衡策略
  • 五、请求路由
  • 本章小结


前言

对于非常大的数据集,或非常高的吞吐量,仅仅进行复制是不够的:我们需要将数据进行 分区(partitions),也称为 分片(sharding)

分区将大型数据库分解成小型数据库

为什么分区:

主要是提高可伸缩性。不同的分区可以放在不共享集群中的不同节点上。因此,大数据集可以分布在多个磁盘上,并且查询负载可以分布在多个处理器上。

简要概括

在本章中,我们将首先介绍分割大型数据集的不同方法,并观察索引如何与分区配合。然后我们将讨论 分区再平衡(rebalancing),如果想要添加或删除集群中的节点,则必须进行再平衡。最后,我们将概述数据库如何将请求路由到正确的分区并执行查询。


一、分区与复制

一般是先复制再分区,复制与分区结合使用。

一个节点可能存储多个分区。如果使用主从复制模型,则分区和复制的组合如 图 6-1 所示。每个分区领导者(主)被分配给一个节点,追随者(从)被分配给其他节点。 每个节点可能是某些分区的领导者,同时是其他分区的追随者。

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、键值数据的分区

  • 如何决定在哪些节点上存储哪些记录的问题
  • 分区目标是将数据和查询负载均匀分布在各个节点上。
  • 不均衡导致的高负载的分区被称为 热点(hot spot)。
  • 分区是不公平的,一些分区比其他分区有更多的数据或查询,我们称之为 偏斜
  • 避免热点最简单的方法是将记录随机分配给节点。这将在所有节点上平均分配数据,但是它有一个很大的缺点:当你试图读取特定值时,你无法知道它在哪个节点上,所以你必须并行地查询所有的节点。

    2.1 根据键的范围分区(Key Range 分区)

  • 为每个分区指定一块连续的键范围(从最小值到最大值) 键的范围不一定均匀分布,因为数据也很可能不均匀分布。
  • 缺点:
    是某些特定的访问模式会导致热点

    2.2 根据键的散列分区

    一旦你有一个合适的键散列函数,你可以为每个分区分配一个散列范围(而不是键的范围),每个通过哈希散列落在分区范围内的键将被存储在该分区中。

    一致性哈希:
    分区边界可以是均匀间隔的,也可以是伪随机选择的。
    这种特殊的方法对于数据库实际上并不是很好,所以在实际中很少使用。

    缺点:
    不能高效执行范围查询。
    曾经相邻的键现在分散在所有分区中,所以它们之间的顺序就丢失了。

    补救:
    组合索引方法为一对多关系提供了一个优雅的数据模型。

    2.3 负载偏斜与热点消除

    如今,大多数数据系统无法自动补偿这种高度偏斜的负载,因此应用程序有责任减少偏斜。
    例如,如果一个主键被认为是非常火爆的,一个简单的方法是在主键的开始或结尾添加一个随机数。只要一个两位数的十进制随机数就可以将主键分散为 100 种不同的主键,从而存储在不同的分区中。然而,将主键进行分割之后,任何读取都必须要做额外的工作,因为他们必须从所有 100 个主键分布中读取数据并将其合并。

    总结:
    只需要对少量热点附加随机数;对于写入吞吐量低的绝大多数主键来说是不必要的开销。因此,你还需要一些方法来跟踪哪些键需要被分割。

    三、分区与次级索引

    两种用次级索引对数据库分区方法:

  • 基于文档的分区(document-based)
  • 基于关键词(term-based)的分区。
  • 次级索引缺点:
    它们不能整齐地映射到分区

    3.1基于文档的次级索引进行分区

    文档分区索引 也被称为 本地索引(而不是将在下一节中描述的 全局索引)。
    每个分区维护自己的次级索引,仅覆盖该分区中的文档。它不关心存储在其他分区的数据

    分散 / 聚集(scatter/gather):
    红色汽车出现在分区 0 和分区 1 中。因此,如果要搜索红色汽车,则需要将查询发送到所有分区,并合并所有返回的结果。

    3.2基于关键词的次级索引进行分区

    构建一个覆盖所有分区数据的 全局索引,而不是给每个分区创建自己的次级索引(本地索引)。
    全局索引也必须进行分区(否则会违背分区的目的),但可以采用与主键不同的分区方式。

    关键词分区全局索引优于文档分区索引的方面是:
    读取更有效率:不需要 分散 / 收集 所有分区,客户端只需要向包含关键词的分区发出请求。

    全局索引的缺点:

  • 写入速度较慢且较为复杂,因为写入单个文档现在可能会影响索引的多个分区(文档中的每个关键词可能位于不同的分区或者不同的节点上) 。
  • 对全局次级索引的更新通常是异步的,会有一定的延迟。
  • 四、分区再平衡

    再平衡:
    将负载从集群中的一个节点向另一个节点移动的过程。

    随着时间的推移,数据库会有各种变化:

  • 查询吞吐量增加,所以你想要添加更多的 CPU 来处理负载。
  • 数据集大小增加,所以你想添加更多的磁盘和 RAM 来存储它。
  • 机器出现故障,其他机器需要接管故障机器的责任。
  • 无论使用哪种分区方案,再平衡通常都要满足一些最低要求:

  • 再平衡之后,负载(数据存储,读取和写入请求)应该在集群中的节点之间**公平地共享**。
  • 再平衡发生时,数据库应该继续接受读取和写入。
  • 节点之间只移动必须的数据,以便快速再平衡,并减少网络和磁盘 I/O 负载。
  • 4.1再平衡策略

    ① 取模哈希(反面教材)
    如果节点数量 N 发生变化,大多数键将需要从一个节点移动到另一个节点。
    但是我们需要一种只移动必需数据的方法。

    ② 固定数量的分区

    只有分区在节点之间的移动。分区的数量不会改变,键所指定的分区也不会改变。

    改进:
    使分区数大于节点数,并为每个节点分配多个分区。
    例如,运行在 10 个节点的集群上的数据库可能会从一开始就被拆分为 1,000 个分区,因此大约有 100 个分区被分配给每个节点。
    现在,如果一个节点被添加到集群中,新节点可以从当前每个节点中 窃取 一些分区,直到分区再次公平分配。如果从集群中删除一个节点,则会发生相反的情况

    总结:
    在这种配置中,分区的数量通常在数据库第一次建立时确定,之后不会改变。虽然原则上可以分割和合并分区,但固定数量的分区在操作上更简单,因此许多固定分区数据库选择不实施分区分割。因此,一开始配置的分区数就是你可以拥有的最大节点数量,所以你需要选择足够多的分区以适应未来的增长。但是,每个分区也有管理开销,所以选择太大的数字会适得其反。

    ③ 动态分区
    对于使用键范围分区的数据库具有固定边界的固定数量的分区将非常不便:如果出现边界错误,则可能会导致一个分区中的所有数据或者其他分区中的所有数据为空。手动重新配置分区边界将非常繁琐。

    所以按键的范围进行分区的数据库会动态创建分区,动态分裂和合并,与 B 树顶层发生的过程类似

    要求:
    每个分区分配给一个节点,每个节点可以处理多个分区,就像固定数量的分区一样。

    优点是:
    分区数量适应总数据量。如果只有少量的数据,少量的分区就足够了,所以开销很小;如果有大量的数据,每个分区的大小被限制在一个可配置的最大值

    注意:
    空数据库从一个分区开始,因为没有关于在哪里绘制分区边界的先验信息。
    数据集开始时很小,直到达到第一个分区的分割点,所有写入操作都必须由单个节点处理,而其他节点则处于空闲状态
    有些数据库会预分割(配置一组初始分区),在键范围分区的情况中,预分割需要提前知道键是如何进行分配的。

    ④ 按照节点比例分区

  • 动态分区,分区的数量与数据集的大小成正比,因为拆分和合并过程将每个分区的大小保持在固定的最小值和最大值之间。
  • 固定数量的分区,每个分区的大小与数据集的大小成正比。
  • 按节点比例进行分区:使分区数与节点数成正比,节点不变时,每个分区的大小与数据集大小成比例地增长。增加节点数时,分区将再次变小。使每个分区的大小较为稳定
  • 机制:
    当一个新节点加入集群时,它随机选择固定数量的现有分区进行拆分,然后占有这些拆分分区中每个分区的一半,同时将每个分区的另一半留在原地。随机化可能会产生不公平的分割,但是平均在更大数量的分区上时(在 Cassandra 中,默认情况下,每个节点有 256 个分区),新节点最终从现有节点获得公平的负载份额。 Cassandra 3.0 引入了另一种再平衡的算法来避免不公平的分割。

    手动再平衡还是自动再平衡?
    自动故障检测可能十分危险。例如,假设一个节点过载,并且对请求的响应暂时很慢。其他节点得出结论:过载的节点已经死亡,并自动重新平衡集群,使负载离开它。这会对已经超负荷的节点,其他节点和网络造成额外的负载,从而使情况变得更糟,并可能导致级联失败。

    因此再平衡的过程中有人参与是一件好事。这比完全自动的过程慢,但可以帮助防止运维意外

    五、请求路由

    当客户想要发出请求时,如何知道要连接哪个节点?

    三种方案:

    1. 允许客户联系任何节点(例如,通过 循环策略的负载均衡,即 Round-Robin Load Balancer)。如果该节点恰巧拥有请求的分区,则它可以直接处理该请求;否则,它将请求转发到适当的节点,接收回复并传递给客户端。

    2. 首先将所有来自客户端的请求发送到路由层,它决定了应该处理请求的节点,并相应地转发。此路由层本身不处理任何请求;它仅负责分区的负载均衡。

    **3. 要求客户端知道分区和节点的分配。**在这种情况下,客户端可以直接连接到适当的节点,而不需要任何中介。

    集群管理:
    每个节点在 ZooKeeper 中注册自己,ZooKeeper 维护分区到节点的可靠映射。其他参与者(如路由层或分区感知客户端)可以在 ZooKeeper 中订阅此信息。只要分区分配发生了改变,或者集群中添加或删除了一个节点,ZooKeeper 就会通知路由层使路由信息保持最新状态。

    执行并行查询:
    目前为止,我们只关注读取或写入单个键的非常简单的查询(加上基于文档分区的次级索引场景下的分散 / 聚集查询)。
    一个典型的数据仓库查询包含多个连接,过滤,分组和聚合操作。 大规模并行处理(MPP) 查询优化器将这个复杂的查询分解成许多执行阶段和分区,其中许多可以在数据库集群的不同节点上并行执行。涉及扫描大规模数据集的查询特别受益于这种并行执行。

    数据仓库查询的快速并行执行是一个专门的话题,由于分析有很重要的商业意义,可以带来很多利益。我们将在 第十章 讨论并行查询执行的一些技巧。


    本章小结

    数据量非常大的时候,在单台机器上存储和处理不可行,需要分区。
    分区的目标是是在多台机器上均匀分布数据和查询负载,避免出现热点(负载不成比例的节点)。选择合适的分区方案 并 在将节点添加到集群或从集群删除时进行分区再平衡

    分区方案:

  • 键范围分区
  • 其中键是有序的,并且分区拥有从某个最小值到某个最大值的所有键。排序的优势在于可以进行有效的范围查询,但是如果应用程序经常访问相邻的键,则存在热点的风险。

    在这种方法中,当分区变得太大时,通常将分区分成两个子分区,动态地再平衡分区。

  • 散列分区
  • 散列函数应用于每个键,分区拥有一定范围的散列。这种方法**破坏了键的排序**,使得范围查询效率低下,但可以**更均匀地分配负载**。

    通过散列进行分区时,通常先提前创建固定数量的分区,为每个节点分配多个分区,并在添加或删除节点时将整个分区从一个节点移动到另一个节点。也可以使用动态分区。

    也可以使用两种方法搭配:例如使用复合主键:使用键的一部分来标识分区,而使用另一部分作为排序顺序。

    还讨论了分区和次级索引之间的相互作用,次级索引也需要分区,有两种方法:

  • 基于文档分区(本地索引)
  • 其中次级索引存储在与主键和值相同的分区中。这意味着只有一个分区需要在写入时更新,但是读取次级索引需要在所有分区之间进行分散 / 收集。
  • 基于关键词分区(全局索引)
  • 其中次级索引存在不同的分区中。次级索引中的条目可以包括来自主键的所有分区的记录。当文档写入时,需要更新多个分区中的次级索引;但是可以从单个分区中进行读取。

    最后,我们讨论了将查询路由到适当的分区的技术,从简单的分区负载平衡到复杂的并行查询执行引擎。

    多数情况下每个分区是独立运行的 — 这就是分区数据库可以伸缩到多台机器的原因。但是,需要写入多个分区的操作结果可能难以预料:例如,如果写入一个分区成功,但另一个分区失败,会发生什么情况?我们将在下面的章节中讨论这个问题。

你可能感兴趣的:(DDIA读书总结,数据库,database)