关于分布式分片,你该知道的事儿

news2024/12/23 6:47:22

关于分布式分片,你该知道的事儿

  • 前言
    • 一、关于分片方式的那些事儿
      • 1.1 按照Hash划分
      • 1.2 按照区间范围划分
      • 1.3 按照数据量划分
      • 1.4 来些例子
        • 1.4.1 Redis的分片划分
        • 1.4.2 Mongo的分片划分
    • 二、关于分区再平衡的那些事儿
      • 2.1 基于固定分片数量
      • 2.2 基于动态分片数量
      • 2.3 基于节点比例进行分片划分
      • 2.4 来些例子
        • 2.4.1 Redis中的再平衡
        • 2.4.2 Mongo中的再平衡
    • 三、关于请求路由的那些事儿
      • 3.1 举些例子
        • 3.1.1 Redis中的路由方式
        • 3.1.2 Mongo中的路由方式
  • 后记
  • 参考

前言

在这里插入图片描述

复制技术讨论的是将同一份数据拷贝到多个节点,每个节点保存相同的数据内容,以冗余来提高可靠性。而数据库分片是和水平分表(将一个表里的行拆分成不同的表)有着一定关系的一种数据拆分方式,它讨论的是将数据进行划分(也有点类似于水平划分),划分的小块叫做逻辑分片(logical shards),然后分配到不同的节点(也做叫物理分片,physical shards)上去,这样每个节点负责处理一部分数据,可以达到提高读写性能的目的。

为了避免混淆,下文中把逻辑分片称作分片,物理分片叫做物理节点。

这篇博客就来讨论讨论分布式中关于分片的那些事儿。 为了避免”总结不举例,就像吹空气“的情况,每块内容会结合例子介绍在实际中分片是如何应用的。


一、关于分片方式的那些事儿

首先要讨论的就是数据是如何分片的,或者说数据是按照什么方式来进行划分的,按照什么方式聚集成为一堆的。

1.1 按照Hash划分

Hash分片主要是根据数据的某些特征进行Hash计算,然后将计算得来的Hash值与集群中节点进行映射(现在大多数系统通过一个虚拟的shard(不同的数据库系统叫法不一样,有的叫做chunk,有的叫做slot等等)层, 数据分配到shard中,然后多个shard分配到节点中, 如下图所示),从而将不同hash值的数据分不到不同的节点上。
在这里插入图片描述
这种方式比较常见。如果选用的hash函数的散列特性比较好,这种方式可以较为均匀的将数据分配到不同的节点中。

但是Hash分片也有两个问题:

  • 数据倾斜。如果整个数据集在某些特征值上有大量的数据,则可能大量的数据会被划分到同一个节点上,造成“数据倾斜”,进而可能引起访问热点。比如说要存储一个群组成员,以群成员所属的groupId作为Hash的key, 因此一个群的所有群成员将被映射到同一个节点中。 当某个群中有了十万,一百万,乃至上千万的群成员,用一个节点来存储和计算估计就顶不住了。
  • 无法进行良好的区间范围查询。key相邻的两个关键字,经过hash之后的值基本不会相邻,因此在存储时很大程度上也不会“紧挨着”。 因此区间查询会发送到多个分片之中。

1.2 按照区间范围划分

在这里插入图片描述

另一种常见的数据划分是按照数据的区间范围进行划分,即将数据按照特征值的值域划分为不同的区间,然后为每个节点分配对应区间范围内的数据。比如说有3个节点N1,N2,N3, 要存储一个图书馆的所有书籍,则可以用书名首字母作为特征值,N1负责[a, g), N2负责[g,t), N3负责[t,z]。这样,每个节点可以处理自己区间范围内的数据,相互之间不会干扰。

对于按照哈希划分来说,整个系统只需要保存哈希函数和节点的个数。 但是按照区间范围划分,需要记录每个节点负责数据范围的对应关系,这部分元信息一般使用专门的服务器来维护。

按照区间范围进行划分最大的一个好处是有利于区间范围查询。 比如说上面的例子,如果要查询【a,c]开头的书名则只需要将请求发送到N1即可。

但是如果系统写入的顺序也是按照key的递增顺序进行写入,会存在针对单个节点的热点写入问题。一些常见的递增key有时间戳、自增序列、计数器等。

Hash划分和范围划分刚好是两个相对互补的分片方式,前者可以达到较为均匀的数据均衡(当然具体还是要根据上层的应用选择的key够不够理想),但是无法进行较好的范围查询;后者可以进行良好的范围查询,但是有可能数据分散的不够合理。

有些情况为了避免某一种分片的缺点,我们把分片键进行改造。比如说为了缓解hash分片的数据倾斜的问题可以考虑在key后加一个随机数,应用层进行打散;为了缓解区间范围分片的问题,则可以在关键字前面加上另外一个属性(比如说传感器以时间为key进行范围分片会有热点写入的问题,则可以在key前加一个传感器的名称,也可以将数据打散)。

1.3 按照数据量划分

还有一种方式是按照数据量进行划分,把整个数据集看作是一个顺序增长的文件,然后将这个数据集划分为固定大小的分片块,每个节点负责一个或多个分片块。

但是这种方式由于是以追加的方式添加数据,因此可能会造成热点写入。对于读取来说,如果写入的顺序是按照某个区间范围,则查询时性能较好。但是对于一般情况,则必须把查询路由到所有的节点。

1.4 来些例子

1.4.1 Redis的分片划分

redis分片集群将整个数据集(具体来说是一个数据库)事先划分了16384(0-16383, 关于为什么要设置成16384个槽位,作者也是有考虑的,具体请参见【12】)个槽(类似于上文中说的shard), 然后将数据库中的所有key分配到这些槽中。具体采用的方式是Hash分片,通过计算key的hash值(使用的hash函数是CRC16(key))得到的结果 与16384做取余运算, 最终计算出key所属的slot, 如下所示。

slot = CRC16(key) % 16384

在redis中使用下述命令可以确定key所属的slot位置。

CLUSTER KEYSLOT key

如下图所示,为“test_key1”,“test_key2”,"test_key3"这三个key所属的slot位置。
在这里插入图片描述

以上只完成了将数据key分配到了“虚拟的”slot, 最后一步过程是将slot分配给不同的节点。一般来说,这一步默认是按照平均分配的原则。如果redis采用3分片节点,则每个分片分到的slot是5041(16384/3)个槽。如下图所示为腾讯云上申请的3节点集群。

在这里插入图片描述
如果采用3分片节点集群,上述示例3个key, “test_key1”,“test_key2”,"test_key3"将分别由3个分片节点进行管理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当然也可以根据具体的情况,手动分配每个节点所管理的slot数量。

1.4.2 Mongo的分片划分

mongo 支持两种分片划分方式。
一种是Hash划分。如下图所示,mongo将数据的key进行hash然后映射到不同的chunk中。一个chunk是一段与key有一定联系的范围值,在Hash分片中它是一段hash(key)后的hash值,即一段相邻的hash值将会存放在同一个chunk中(有点类似于上文说到的Redis中的槽位,但是每个槽位是按照hash(key)值取余的方式得出来的)。
在这里插入图片描述

另一种是范围划分。mongo分片集群支持按照shard key的范围进行分片,相邻范围内的key 将会被分配到同一个chunk, 如下图所示。
在这里插入图片描述

不明白?

上述两种方式中的chunk都是一段key space空间,只不过hash分片划分是以hash(shardKey) 之后的值作为标准,而范围分片是直接以shardKey作为标准。

二、关于分区再平衡的那些事儿

这主要是讨论数据的迁移问题。 当我们用多个节点联合起来组成一个完成的数据库系统时,一个不可避免的问题就是节点的失效退出,和扩容新增。由于节点的数量变量,因此这个时候会涉及到数据的迁移。 这个过程无法避免,但是理所应当我们希望迁移的数据越少越好或者迁移后数据分布的越平衡越好。

使用直接取余的方式将shard映射到node中一般是不太行的,因为这样当扩充或者减少节点时会有大量的数据迁移。 一般常用的主要有以下几种分片的方式:

2.1 基于固定分片数量

基于固定的分片数量是事先创建远大于节点数的分片数shardNum ( shardNum >> NodeNom ) ,然后为每个节点分配一批分片。当增加节点时,从每个节点平均允一些分片到新的节点中(由于shard是逻辑概念,因此迁移的主要是shard中具体的数据集),直到整个集群达到平衡状态(或者相对平衡状态)。如下图所示。
在这里插入图片描述

2.2 基于动态分片数量

还有一种分片方式是不定数量的动态分片。对于一些按照区间范围分片的分布式系统来说,数据库无法知道一个shard区间范围内有多少数据,因此无法更均衡的以事先分配的方式直接将某个shard指派给具体的节点。
因此,这种数据库往往采用动态分片的方式,当某个shard的数据达到设定的阈值之后,将shard进行拆分,一个shard平均拆分为两个shard(一般以数据量来衡量)。然后将拆分的shard分到不同的节点中去,以此来达到负载均衡的目的。 如下图所示。

在这里插入图片描述

2.3 基于节点比例进行分片划分

还有一种方式是根据节点来进行分片划分,也就是每个节点的分片数量是固定的(注意上面固定分片是整个系统的分片数量是固定的)。如果每个节点的分片数量是N,则多一个节点,整个系统的分片数量要增加N。当新增节点时,以某种方式选择(比如说采用随机或者轮询)一个原有节点,将其中一半的数据分配给新节点。如下图所示。
在这里插入图片描述

2.4 来些例子

2.4.1 Redis中的再平衡

对于Redis来说,其分区再平衡采用的是基于固定分片数量的方式,如上所述,redis集群架构事先规划了16384个slot(相当于上文中说的shard)。当新增一个节点A时,会平均从其他所有的slot中迁移数据(以slot为单位进行迁移)到新节点A.

如下图所示是Redis从3个节点扩充到5个节点的Slot示意图。
在这里插入图片描述
扩充后:
在这里插入图片描述在这里插入图片描述
可以看到,新扩充的节点从以前的3个节点中都允走了部分slot(因此新的节点所管理的slot范围变得不连续了)。

2.4.2 Mongo中的再平衡

mongo集群是采用上述的基于动态分片数量的再平衡方式。 如上所述,mongo是采用chunk,也就是一定范围的shard key空间来组织数据的。程序开始的时候,mongo初始化一个chunk(如果是采用的hash分片方式,则默认每个节点会初始化2个chunk, 具体初始化的chunk数量可以根据numInitialChunks 这个参数进行配置),覆盖整个range空间。

当chunk达到设定的阈值(一般是64M,最新版本的mongo应该是128M)之后,会平均分裂为两个chunk(按照数据量大小进行划分)。

同时, mongo内部会有一个balancer 来异步迁移各个节点之间的数据。当发现节点之间unbalanced 的时候,就会在节点之间进行迁移,具体的做法是数据多的节点迁移chunk到数据少的节点中。 如下图所示。
在这里插入图片描述
这里要注意两个问题:
(1)在5.0版本之前,balance的条件是以节点上chunk的数量来计算的(针对所有启用分片的集合,如果 「拥有最多数量 chunk 的 节点」 与 「拥有最少数量 chunk 的 节点」 的差值超过某个阈值,就会触发 chunk 迁移;)。而在5.0之后的版本中,balance的条件变成了以节点上collection中的数据量进行计算。如下图所示。
在这里插入图片描述

在这里插入图片描述

(2)迁移的单位是chunk. 即每次迁移最少一个chunk的量。

从Redis何Mongo的再平衡方式可以看出,Redis在迁移的时候强调整个集群数据分布的平均(确切的说是slot分布的平均),而Mongo更注重每次迁移较少的数据量(当然,也会达到一定程度的数据均衡)

三、关于请求路由的那些事儿

请求路由解决的问题是我们把数据集划分成了多个分片,当请求到来时,我们怎么知道该请求哪个分片呢?更确切的说,客户发起一个"get test_key” 请求, 这个请求会发往那个节点,哪台机器呢?
在这里插入图片描述
一般来说,常见的路由的方式有以下三种:

  1. 数据和节点的关系是确定的(如果是data-shard-node的方式, 那就是shard和node的关系是确定的),可以通过某种方式计算出来。这样,客户端可以直接根据请求的key计算出目标节点,然后直接将请求发往目标节点。

    一般来说,数据和节点的关系如果是确定的,那么如果增减节点,则映射关系就不存在了。但是有一种算法依旧可以计算出映射关系,那就是一致性hash算法。在实际中,也有一些数据库系统采用这种方式来进行路由原则(如Dynamo和Cassandra)。

  2. 数据和节点的关系保存在集群的各个节点中。这种情况集群中的每个节点都维护了数据和节点(或者shard和节点)的映射关系。 对于这种方式,客户端可以随意请求集群中的节点A,如果请求的节点正好是目标节点B,则目标节点B直接处理请求并返回结果。如果客户端请求的节点A‘不是目标节点,则A’会将请求转发给目标节点(因为集群中的节点知道对应key的目标节点在哪儿),得到结果后再将结果转发给客户端。 还有一种方式是请求的节点A’ 将目标节点的地址信息返回给客户端,有客户端再去请求 对应的目标节点。

    这种方式依赖节点之间通过协议来维护同一份数据和节点的映射关系,增加了数据库的复杂度,但是没有外部协调节点的依赖。

  3. 数据和节点的映射关系保存在集群单独的节点。 这种方式集群中一般有专门的节点来负责维护数据和节点的映射关系,然后还有一个路由节点来订阅这份路由映射表(有些集群可能路由节点自己维护了这份映射关系),所有的请求也是先发送给这个路由节点,然后由路由节点再将请求转发给后端的数据节点。

    有不少数据库集群或者Mq等系统使用开源的外部协调节点(如ZooKeeper)来维护数据和节点的映射关系。所有的数据节点(存储具体数据,负责处理具体请求的节点)的变化情况会向协调节点通知,而客户端或者路由节点则向协调节点订阅映射关系,这种方式虽然简单,但是会带来外部系统的依赖性。

具体如下图所示。
在这里插入图片描述

3.1 举些例子

3.1.1 Redis中的路由方式

Redis通过gossip协议来维护整个集群中数据和节点的映射关系(准确来说是slot和节点的映射关系,因为数据key到slot是采用CRC16 hash算出来的)。

当客户端向集群发送请求时,接收命令的节点会计算出请求的key属于哪个槽,然后根据自身存储的映射关系表判断出请求的slot是否归属自己管理,如果是归自己管理则直接执行请求的命令;否则节点会向客户端返回一个MOVED错误,而且MOVED得回包中会携带正确的节点信息;

客户端收到MOVED错误之后会根据返回的节点信息,重新发送请求到目标节点。如下图所示。

在这里插入图片描述

3.1.2 Mongo中的路由方式

对于Mongo集群,它的路由请求的方式采用的是上述说的通过一个路由节点(mongos)完成的。 而数据和节点的映射关系保存在(config server上)。客户端所有的请求都会发往Mongos节点,然后mongos再将请求转发给后端的节点(注意mongo集群中叫做shard, 指的是开头说的物理shard节点)。

在这里插入图片描述
在mongo集群中把请求主要分为两种,一种是Targeted Operations, 第二种是Broadcast Operations。 一般来说,可以将前者理解为请求中携带了shard key, 这样mongos就可以根据映射关系直接把请求转发到目标节点;

在这里插入图片描述
后者指的是请求中没有带shard key 这种的操作,mongos只要把请求转发到所有的节点,等待所有节点回复之后,汇集起来然后返回给客户端。

在这里插入图片描述
一般来说,target operation要比broadcast operation要快的多,因此客户端在调用接口时应尽量把分片键给带上,使用target operation来进行请求。

后记

数据库分片或者扩大点说分片技术,本质上是通过增加机器来水平扩展系统性能的方式。这不仅存在于数据库上、很多mq甚至应用层都需要使用分片的思想来提高系统的性能。 但是“遇事不决加机器”这种,也不一定是唯一的方式(很多时候也不一定是最优的),毕竟这增加了系统的复杂度,很多时候通过垂直扩展或者优化系统性能的也能很好的解决问题。总的来说还是马克思爷爷教我们的: “实事求是,因地制宜,合适的才是最好的”。


参考

  • 【1】《DDIA》
  • 【2】《高性能Mysql》
  • 【3】《Redis的设计与实现》
  • 【4】分布式相关技术 && 《DDIA读书笔记》
  • 【5】 为什么Redis集群有16384个槽
  • 【6】Understanding Database Sharding
  • 【7】MongoDB · 引擎特性 · sharding chunk 分裂与迁移详解
  • 【8】Sharded Cluster Balancer
  • 【9】redis集群新增结点slot迁移原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1511234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

让生活更加精致的APP?

晚上好,今天博主来介绍几款帮助你条理生活的APP,让你的生活更加精致,充满仪式感。 一.格志日记 一款以“格子”的方式记录日记的APP,非常简单明了,用户可以依据自己的喜好,来自由定义或者删除格…

初阶数据结构之---堆的应用(堆排序和topk问题)

引言 上篇博客讲到了堆是什么,以及堆的基本创建和实现,这次我们再来对堆这个数据结构更进一步的深入,将讲到的内容包括:向下调整建堆,建堆的复杂度计算,堆排序和topk问题。话不多说,开启我们今…

Python面向对象——程序架构

需求 创建图形管理器 -记录多种图形(圆形、矩形.) --提供计算总面积的方法, 要求:增加新图形,不影响图形管理器 测试: 创建图形管理器,存储多个图形对象。 通过图形管理器,调用计算总面积方法 思路 ​​​​​​​ 代码 # ------…

C# SM2加解密 ——国密SM2算法

SM2 是国家密码管理局组织制定并提出的椭圆曲线密码算法标准。 本文使用第三方密码库 BouncyCastle 实现 SM2 加解密,使用 NuGet 安装即可,包名:Portable.BouncyCastle,目前最新版本为:1.9.0。 using Org.BouncyCastl…

SpringBoot中MD5使用

SpringBoot中MD5使用 新建md5类 public final class MD5 {public static String encrypt(String strSrc) {try {char[] hexChars {0, 1, 2, 3, 4, 5, 6, 7, 8,9, a, b, c, d, e, f};byte[] bytes strSrc.getBytes();MessageDigest md MessageDigest.getInstance("MD5…

设计模式前置了解uml图

在开发前,会进行系统的设计,而数据模型的设计大多通过 UML 类图实现。为了在 UML 类图中清晰地表达类之间的关系,需要对类之间的关系有一定的认识,并且了解相关的表达符号。 类之间的关系有以下几种: 组合 聚合 关联…

IPC:管道

一、管道的概念 1.原理 在进程3G~4G的内核空间中,创建一个特殊的文件(管道),管道的数据直接保存在内存中。 2.特性 1)管道可以看成是一个特殊的文件,一般的文件存储在外存中,而管道内容是存储…

“光谱视界革新:ChatGPT在成像光谱遥感中的智能革命“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境,是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型,在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用,人工智能…

docker——启动各种服务

1.Mysql 2.Redis 3.nginx 4.ES 注意:ES7之后环境为 -e ELASTICSEARCH_HOSTS http://ip地址:9200

双场板功率型GaN HEMT中用于精确开关行为的电容建模

来源:Capacitance Modeling in Dual Field-Plate Power GaN HEMT for Accurate Switching Behavior (TED 16年) 摘要 本文提出了一种基于表面电势的紧凑模型,用于描述具有栅极和源极场板(FP)结构的AlGaN/GaN高电子迁移率晶体管(…

在OpenStack架构中,Controller节点的配置(基础)

虚拟机的安装 新建虚拟机,选择自定义 默认选择即可 操作系统的镜像稍后选择 客户及操作系统选择Linux,注意选择centos 7 64位 给虚拟机命名 处理器的配置建议1:2 内存大小选择建议为:4GB 网络连接选择为:NAT 默认即可…

Redis底层核心对象RedisObject源码分析

文章目录 1. redis底层数据结构2. 插入KV底层源码流程分析 1. redis底层数据结构 redis 6数据结构和底层数据结构的关系 String类型本质是SDS动态字符串,即redis层面的数据结构底层会有对应的数据结构实现,上面是redis 6之前的实现 redis 7数据结构和底…

如何保证消息的顺序性

先看看顺序会错乱的场景:RabbitMQ:一个 queue,多个 consumer,这不明显乱了: 解决方案:

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的番茄新鲜程度检测系统(深度学习+UI界面+训练数据集)

摘要:本研究详述了一种采用深度学习技术的番茄新鲜程度检测系统,该系统集成了最新的YOLOv8算法,并与YOLOv7、YOLOv6、YOLOv5等早期算法进行了性能评估对比。该系统能够在各种媒介——包括图像、视频文件、实时视频流及批量文件中——准确地识…

智能泵站智能运维系统

在现代化城市建设和工农业发展中,泵站作为关键的水利设施,其运行效率和稳定性至关重要。然而,传统的泵站运维方式往往依赖于人工巡检和定期维护,这种方式不仅效率低下,而且难以应对突发状况。随着物联网技术的飞速发展…

支小蜜校园防欺凌系统真的能有效遏制欺凌现象吗?

随着社会的快速发展,校园欺凌问题逐渐浮出水面,引起了广泛关注。为了应对这一问题,校园防欺凌系统应运而生,旨在通过一系列措施,有效遏制欺凌现象的发生。然而,这一系统是否真的能够如预期般发挥作用&#…

软考高项(信息系统项目管理师)备考一、介绍

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

Cassandra 安装部署

文章目录 一、概述1.官方文档2. 克隆服务器3.安装准备3.1.安装 JDK 113.2.安装 Python3.3.下载文件 二、安装部署1.配置 Cassandra2.启动 Cassandra3.关闭Cassandra4.查看状态5.客户端连接服务器6.服务运行脚本 开源中间件 # Cassandrahttps://iothub.org.cn/docs/middleware/…

干货!不懂Python的math模块和random模块操作还不赶紧来学!

1.导入math模块 import math 2.向上取整:math.ceil() num 9.12print(math.ceil(num)) # 10 3.向下取整:math.floor() num1 9.99print(math.floor(num1)) # 9 4.开平方:math.sqrt()​​​​​​​ num2 16print(math.sqrt(num…

Python程序控制

一、程序的描述方式 1.1自然语言 (1)概念:自然语言就是使用人类语言、直接描述程序 (2)比如:之前提过的Input(输入)、Process(处理)、Output(输…