一致性hash

一、什么是一致性hash

普通的hash算法 (hashcode % size ),如果size发生变化,几乎所有的历史数据都需要重hash、移动,代价非常大,常见的java中的hashmap就是如此。

那如果在hash表扩容或者收缩的时候size能够保持不变,即历史数据在hash表中的位置不变,这样就解决了hash表阔缩时的大量数据移动问题。

一致性hash可以理解,就是hash函数(hashcode%size)的size保持不变,从而保证了hash函数的前后一致性。

二、一致性hash算法介绍

一致性hash算法主要应用在分布式缓存系统中,在增加或者删除服务器节点时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系,也就是系统中的大多数历史缓存的存储服务器节点可以不变,解决了普通hash算法带来的动态伸缩性问题。

如上图一致性hash定义了一个 0 ~ 2^32-1 hash环,hash函数并不是按照服务器节点的数量取模,而是按照 2^32 取模(hashcode % 2^32),这样请求的数据就会落在环上某个固定的位置

服务器节点按照IP或域名进行hash,分配到hash环上,如图分配了4个服务器节点,分别在hash环的 10000、20000、30000、40000 位置(为了方便演示)。

插入数据

请求数据先通过hash函数(hashcode % 2^32)确定了在环上的位置,再沿着环顺时针查找,遇到的第一个节点就是命中的服务器节点。

新增、删除节点

新增节点E (25000),按照一致性hash算法,只有B ~ E 之间的历史数据会受到影响,(之前是路由到C的,现在路由到 E ),即只有C的一部分数据需要迁移到E。

删除节点B,那么 A~ B 之间的历史数据丢失,并且新增数据会被插入到 C,其他的节点都不会受到影响。

可以看到一致性hash算法,节点的增删都只会影响了系统中的一小部分数据,容错性非常好。

但是上面的模型还是有个问题,如果服务器节点太少或者出现热点数据,就会导致服务器节点上之间的数据分布不均匀;并且还可能出现缓存雪崩的问题。

如果每个服务器在环上只有一个节点,那么当服务器宕机,它原本所负责的缓存数据将全部交由顺时针方向的下一个服务器节点处理。例如,当 B 退出时,它原本所负责的缓存将全部交给 C 处理。这就意味着 C 的访问压力会瞬间增大。设想一下,如果 C 因为压力过大而崩溃,那么更大的压力又会向 D 压过去,最终服务压力就像滚雪球一样越滚越大,最终导致缓存雪崩

缓存雪崩:虚拟节点处理

一致性hash 通过引入虚拟节点解决了这个问题,每个实际节点映射多个虚拟节点,数据按照规则找到虚拟节点后,再储存到映射的实际节点上;因为虚拟节点可以在hash环上均匀分布,这意味着当一个真实节点失效退出后,它原来所承载的压力将会均匀地分散到其他节点上去,解决缓存雪崩问题 

虚拟节点 

三、Redis Cluster的一致性hash算法

redis cluster没有采用基于hash环的一致性算法,而是引入了哈希槽 ( slots ) 的概念,所有的数据都是存在slot中,redis cluster 一共有 2^14(16384)个slot,每个master节点负责一部分slot。

3.1 节点路由

当客户端连接向某个 master 节点发送请求时,接收到命令的节点首先会通过 CRC-16(key)%16384 计算出当前key所属的slot,如果该slot由自己负责,直接处理响应客户端的请求,如果不是,则向客户端返回MOVED重定向请求,并将该slot对应的服务器节点的ip和port一并返回,客户端拿着这些数据重新访问。

3.2 集群扩容

集群新增 master 节点后,需要通过reshard来将slot重新分配,假设我们需要向集群中加入一个D节点,而此时集群内已经有A、B、C三个节点了。此时redis-trib会向A、B、C三个节点发送迁移出slot的请求,同时向D节点发送准备导入slot的请求,做好准备之后A、B、C这三个源节点就开始执行迁移,将对应的slot所对应的键值对迁移至目标节点D。

3.3 节点宕机

针对A节点,某一个节点认为A宕机了,那么此时是主观宕机。而如果集群内超过半数的节点认为A挂了, 那么此时A就会被标记为客观宕机

一旦节点A被标记为了客观宕机,集群就会开始执行故障转移。其余正常运行的 master 节点会进行投票选举,从A节点的 slave 节点中选举出一个,将其切换成新的master对外提供服务。当某个slave获得了超过半数的master节点投票,就会成功当选,成功之后停止复制A节点,使自己成为master。然后将A节点所负责处理的slot,全部转移给自己,然后就会向集群发PONG消息来广播自己的最新状态。

可以看到对于Redis Cluster,CRC-16(key)%16384 保证了某个key只会落固定的一个slot上,并不需要关心它最终要去到哪个服务器节点。

四、分库分表中一致性hash实践

1.基于hash环一致性hash算法的分库分表

一个简单的没有虚拟节点的一致性hash算法

public class ConsistentHashAlgorithm {private SortedMap<Long, String> virtualNodes = new TreeMap<>();public ConsistentHashAlgorithm(Collection<String> tableNodes) {initNodesToHashLoop(tableNodes);}public void initNodesToHashLoop(Collection<String> tableNodes) {SortedMap<Long, String> virtualTableNodes = new TreeMap<>();for (String node : tableNodes) {long hash = getHash(node);virtualTableNodes.put(hash, node);}for (Map.Entry<Long, String> entry : virtualTableNodes.entrySet()) {log.info("节点[" + entry.getValue() + "]被添加, hash值为" + entry.getKey());}this.virtualNodes = virtualTableNodes;}public String getTableNode(String key) {SortedMap<Long, String> subMap = virtualNodes.tailMap(getHash(key));if (subMap.isEmpty()) {return virtualNodes.get(virtualNodes.firstKey());}return subMap.get(subMap.firstKey());}/*** 使用FNV1_32_HASH算法计算key的Hash值** @param key* @return*/public long getHash(String key) {final int p = 16777619;int hash = (int) 2166136261L;for (int i = 0; i < key.length(); i++) {hash = (hash ^ key.charAt(i)) * p;}hash += hash << 13;hash ^= hash >> 7;hash += hash << 3;hash ^= hash >> 17;hash += hash << 5;// 如果算出来的值为负数则取其绝对值if (hash < 0) {hash = Math.abs(hash);}return hash;}
}

sharding-jdbc 自定义分片策略

public class ConsistentShardingAlgorithm implements PreciseShardingAlgorithm<Long> {private ConsistentHashAlgorithm consistentHashAlgorithm;@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {if (consistentHashAlgorithm == null) {consistentHashAlgorithm = new ConsistentHashAlgorithm(collection);}return consistentHashAlgorithm.getTableNode(String.valueOf(preciseShardingValue.getValue()));}
}

sharding-jdbc 配置

        # 一致性hash 分表测试pick_task:actual-data-nodes: ds0.pick_task_$->{0..4}tableStrategy:standard:shardingColumn: place_idprecise-algorithm-class-name: com.simplezero.coding.sharding.repository.sharding.ConsistentShardingAlgorithm

可以看到:

节点[pick_task_1]被添加, hash值为1045769218
节点[pick_task_3]被添加, hash值为1077989284
节点[pick_task_0]被添加, hash值为1225972537
节点[pick_task_2]被添加, hash值为1999305687

如果增加节点 pick_task_4,此时根据 一致性hash算法,只有 pick_task_2 表数据需要迁移

节点[pick_task_1]被添加, hash值为1045769218
节点[pick_task_3]被添加, hash值为1077989284
节点[pick_task_0]被添加, hash值为1225972537
节点[pick_task_4]被添加, hash值为1959358845
节点[pick_task_2]被添加, hash值为1999305687

2.美团的一致性hash方案

直接分32库32表,通过userId后四位 % 32 分库 userId后四位 / 32 %32 分表,共计分为1024张表。线上部署情况为8个集群(主从),每个集群4个库。

场景一:数据库性能达到瓶颈

将逻辑数据库升级到 --> 物理数据库--> 数据库集群 ,分库分表规则不变,最多可以直接扩展到32个数据库集群

如果32个集群也无法满足需求,那么将分库分表规则调整为(32*2^n)*(32/2^n),可以达到最多1024个集群,即每张表一个集群。

场景二:单表容量达到瓶颈(或者1024已经无法满足你)

如果1024张表都不能满足你了,这时,可以保持分库规则不变,单库里的表再进行裂变

tb_(userId后四位 div 32 mod 32)_(userId后四位 div 32 mod 32 mod 8) ==> db_4.tb_3_7

userId后四位 div 32 mod 32在目前订单这种规则下(用userId后四位 mod)还是有极限的,因为只有四位,所以最多拆8192个表。

不过这个时候就不能一劳永逸,需要进行库内的表数据迁移了。

3.平均分布方案

直接32库32表1024张表,固然可以一步到位,但是对于小公司来说前期根本用不上,浪费机器且增加系统复杂度,所以还是循序渐进,按照一致性hash算法的思想,先确定总的节点数为32

32 = 节点count * 数据库数量
database_index = key % 32 / count  * count//分2库 count = 32 / 2 = 16
database_index = key % 32 / 16  * 16 ==> db_0(0-15) db_16(16-31)//分4库 count = 32 / 4 = 8
database_index = key % 32 / 8  * 8 ==> db_0(0-7) db_8(8-15) db_16(16-23) db_24(24-31)

可以看到如果从2库扩展到4库,需要从 db_0 移动一半的数据到 db_8,db_16 也同样要移动。

db_0(0-15)==> db_0(0-7)+ db_8(8-15)

可以看到当需要进行扩容一倍时需要迁移一半的数据量,如果量比较大,影响还是比较大。

这个方案还可以进行优化,一致性hash的算法不变,每次增加1个节点,而不是每次增加2^n个节点,这样可以尽量平滑的进行扩容。

//分2库 count = 32 / 2 = 16
database_index = key % 32 / 16  * 16 ==> db_0 db_16//分3库 count = 32 / 4 = 8
database_index = key % 32 / 8  * 8 ==> db_0(0-7) db_8(8-15) db_16(16-31)

此时就需要改分片路由的规则了

Integer dbValue = userId  % 32 / 8 * 8 ;
if(dbValue >= 16){return 16;
} else {return dbValue;
}

 

 借鉴:一致性hash

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

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

相关文章

gitee / github 配置git, 实现免密码登录

文章目录 怎么配置公钥和私钥验证配置成功问题 怎么配置公钥和私钥 以下内容参考自 github ssh 配置&#xff0c;gitee的配置也是一样的&#xff1b; 粘贴以下文本&#xff0c;将示例中使用的电子邮件替换为 GitHub 电子邮件地址。 ssh-keygen -t ed25519 -C "your_emai…

线性代数 --- 矩阵的对角化以及矩阵的n次幂

矩阵的对角化以及矩阵的n次幂 &#xff08;特征向量与特征值的应用&#xff09; 前言&#xff1a; 在上一篇文章中&#xff0c;我记录了学习矩阵的特征向量和特征值的学习笔记&#xff0c;所关注的是那些矩阵A作用于向量x后&#xff0c;方向不发生改变的x(仅有尺度的缩放)。线…

VMware 15 安装centos7虚拟机

1. 安装前准备 1.1 下载centos 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 下载需要版本的centos版本 直达链接 centos7.9 &#xff1a; centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿里云 .基础使用的话安装选择这个就行了&#xff0c;大概下载几分钟 2. …

linux负载均衡 和 系统负载分析笔记

1 负载均衡 1.1 计算负载 1.1.1 PELT算法简介 从Linux3.8内核以后进程的负载计算不仅考虑权重&#xff0c;⽽且跟踪每个调度实体的历史负载情况&#xff0c;该算法称为PELT(Per-entity Load Tracking) 《奔跑吧Linux内核》卷1&#xff1a;基础架构&#xff1b;P505 相关资料…

银河麒麟V10 SP1服务器客户端定时数据同步

银河麒麟V10 SP1服务器客户端定时数据同步 0.概述 当前只测试了将数据从客户端往服务端推送&#xff0c;两个客户端分别推送不同的数据 1.环境 三台电脑均为银河麒麟V10SP1桌面操作系统 服务器IP&#xff1a;192.168.1.51 用户名&#xff1a;wlh 客户端IP&#xff1a;192…

OpenStack云计算(十一)——OpenStack网络管理,验证OpenStack网络资源模型,验证来巩固和加深对OpenStack网络资源模型的理解

项目实训一 【实训题目】 验证OpenStack网络资源模型 【实训目的】 通过验证来巩固和加深对OpenStack网络资源模型的理解。 【实训准备】 &#xff08;1&#xff09;复习Neutron网络资源模型。 &#xff08;2&#xff09;重点理解网络、子网、端口和路由器的概念。 【实…

用 LM Studio 1 分钟搭建可在本地运行大型语言模型平台替代 ChatGPT

&#x1f4cc; 简介 LM Studio是一个允许用户在本地离线运行大型语言模型&#xff08;LLMs&#xff09;的平台&#xff0c;它提供了一种便捷的方式来使用和测试这些先进的机器学习模型&#xff0c;而无需依赖于互联网连接。以下是LM Studio的一些关键特性&#xff1a; 脱机&am…

【笔记django】创建一个app

创建app 错误 raise ImproperlyConfigured( django.core.exceptions.ImproperlyConfigured: Cannot import rules. Check that dvadmin.rules.apps.RulesConfig.name is correct.原因 刚创建的rules的app被手动移动到了dvadmin目录下 而dvadmin/rules/apps.py的内容还是&…

kubeadmin搭建自建k8s集群

一、安装要求 在开始之前&#xff0c;部署Kubernetes集群的虚拟机需要满足以下几个条件&#xff1a; 操作系统 CentOS7.x-86_x64硬件配置&#xff1a;2GB或更多RAM&#xff0c;2个CPU或更多CPU&#xff0c;硬盘30GB或更多【注意master需要两核】可以访问外网&#xff0c;需要…

万界星空科技电机行业MES+商业电机行业开源MES+项目合作

要得出mes系统解决方案在机电行业的应用范围&#xff0c;我们先来看一下传统机电行业的管理难题&#xff1a; 1、 产品标准化程度较低&#xff0c;制造工艺复杂&#xff0c;生产周期较长&#xff0c;产品质量不稳定&#xff1b; 2、 自动化程度低&#xff0c;大多数工序以手工…

【linux】匿名管道|进程池

1.进程为什么要通信&#xff1f; 进程也是需要某种协同的&#xff0c;所以如何协同的前提条件(通信) 通信数据的类别&#xff1a; 1.通知就绪的 2.单纯的数据 3.控制相关的信息 2.进程如何通信&#xff1f; 进程间通信&#xff0c;成本会高一点 进程间通信的前提&#xff0c;先…

2024年最新 Windows\Linux 后台运行程序注解

一、前言 有时候需要在后台运行程序&#xff0c;查了查网上现有的程序有些运行不了&#xff0c;跑通了之后简单记录一下&#xff0c;为后面的同学躺一下雷 1、Linux 系统 Linux后台运行进程时&#xff0c;通常使用如下方法&#xff1a; &#xff08;1&#xff09;程序挂后台…

算法练习(2)——约瑟夫环和坐标公式的推导

看一下上面的牛客题。题目的意思是n个小朋友围成一个圆环&#xff0c;编号从0开始&#xff0c;数m个数时&#xff0c;让小朋友出列&#xff0c;然后出列小朋友的下一个位置为0&#xff0c;继续数m个数&#xff0c;然后小朋友出来&#xff0c;直到最后一个小朋友&#xff0c;然后…

SVN小乌龟汉化问题

1.首先确认中文语言包和SVN版本需要一致&#xff08;点击右键 选择最后一个选项即可查看&#xff09; 官网链接 点击这个官网链接可以下载对应版本的中文包 2.下载好之后直接无脑下一步安装即可 3.如果还是没有中文&#xff0c;找到这个文件夹&#xff0c;把里面的内容全部删…

图解《图搜索算法》及代码实现

关注我&#xff0c;持续分享逻辑思维&管理思维&#xff1b; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导&#xff1b; 有意找工作的同学&#xff0c;请参考博主的原创&#xff1a;《面试官心得--面试前应该如何准备》&#xff0c;《面试官心得--面试时如何进行自…

GPT与GAN结合生成图像——VQGAN原理解析

1、前言 这篇文章&#xff0c;我们讲VQ_GAN&#xff0c;这是一个将特征向量离散化的模型&#xff0c;其效果相当不错&#xff0c;搭配Transformer&#xff08;GPT&#xff09;或者CLIP使用&#xff0c;达到的效果在当时可谓是令人拍案叫绝&#xff01; 原论文&#xff1a;Tam…

【视频异常检测】Open-Vocabulary Video Anomaly Detection 论文阅读

Open-Vocabulary Video Anomaly Detection 论文阅读 AbstractMethod3.1. Overall Framework3.2. Temporal Adapter Module3.3. Semantic Knowledge Injection Module3.4. Novel Anomaly Synthesis Module3.5. Objective Functions3.5.1 Training stage without pseudo anomaly …

网盘——查看文件

本文主要讲解文件操作过程中&#xff0c;查看文件如何实现&#xff0c;实现步骤如下&#xff1a; 1、实现步骤&#xff1a; A、首先客户端发送查看请求&#xff08;包含目录信息&#xff09; B、服务器将文件名字还有文件的类型发送给客户端&#xff08;只发送文件的名字&am…

Linux2.6内核进程调度队列

目录 运行队列runqueue 活跃队列&过期队列 queue[140]&优先级&队列数组下标 bitmap[5]&O(1)调度算法 nr_active active指针和expired指针 O(1)调度算法之调度过程 本篇是Linux进程概念篇的最后一篇&#xff0c;Linux2.6内核是一个具体的/可行的/实际的存…

【Node.js】03 —— HTTP 模块探索

&#x1f31f;Node.js之HTTP模块探索✨ &#x1f31f;引言 在网络编程中&#xff0c;HTTP协议无处不在。在Node.js的世界里&#xff0c;我们可以通过内置的http模块来轻松创建HTTP服务器和客户端&#xff0c;实现数据的接收和发送。今天就让我们一起打开这扇门&#xff0c;探索…