一致性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;

    @Override
    public 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_id
              precise-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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

React-editor-js not showing up in a function component

React-editor-js not showing up in a function component react-editor-js 在react 函数组件中显示不出来 真的&#xff0c;我马上就想放弃它了。但是看它周下载量还挺多&#xff0c;我不信别人没遇到过。于是我继续在网络上挖呀挖。只是我一开始的方向错了。我一直以为我的写…

学习Rust第14天:HashMaps

今天我们来看看Rust中的hashmaps&#xff0c;在 std::collections crate中可用&#xff0c;是存储键值对的有效数据结构。本文介绍了创建、插入、访问、更新和迭代散列表等基本操作。通过一个计算单词出现次数的实际例子&#xff0c;我们展示了它们在现实世界中的实用性。Hashm…

安居水站:四大学习法:成为学霸的有效途径

摘要&#xff1a; 本文详细探讨了全球公认的四种高效学习方法——费曼学习法、西蒙学习法、思维导图法和SQ3R阅读法&#xff0c;通过引入相关数据、名人名言以及名人故事&#xff0c;深入分析了这些方法的核心理念、实施步骤及其在学习过程中的关键作用。 一、引言 学习是人…

《QT实用小工具·三十八》QT炫酷的菜单控件

1、概述 源码放在文章末尾 非常飘逸的 Qt 菜单控件&#xff0c;带有各种动画效果&#xff0c;用起来也十分方便。 无限层级&#xff0c;响应键盘、鼠标单独操作&#xff0c;支持单快捷键。 允许添加自定义 widget、layout&#xff0c;当做特殊的 QDialog 使用。 项目demo演示…

如何理解自然语言处理中的位置编码(Positional Encoding)

在自然语言处理和特别是在使用Transformer模型中,位置编码(Positional Encoding)是一个关键的概念。它们的作用是为模型提供序列中各个元素的位置信息。由于Transformer架构本身并不像循环神经网络(RNN)那样具有处理序列的固有能力,位置编码因此显得尤为重要。 为什么需…

MongoDB数据恢复—拷贝MongoDB数据库文件后无法启动服务的数据恢复案例

服务器数据恢复环境&#xff1a; 一台Windows Server操作系统服务器&#xff0c;服务器上部署MongoDB数据库。 MongoDB数据库故障&检测&#xff1a; 工作人员在未关闭MongoDB数据库服务的情况下&#xff0c;将数据库文件拷贝到其他分区。拷贝完成后将原MongoDB数据库所在分…

CCS项目持续集成

​ 因工作需要&#xff0c;用户提出希望可以做ccs项目的持续集成&#xff0c;及代码提交后能够自动编译并提交到svn。调研过jenkins之后发现重新手写更有性价比&#xff0c;所以肝了几晚终于搞出来了&#xff0c;现在分享出来。 ​ 先交代背景&#xff1a; 1. 代码分两部分&am…

Android Studio开发之路(八)Spinner样式设置

一、需求 白色背景显示下拉框按钮 问题&#xff1a; 设置Spinner的背景可以通过设置background&#xff1a; android:background"color/white",但是一旦设置了这个值&#xff0c;右侧的下拉按钮就会消失 方法一、自定义一个style&#xff08;不成功&#xff09; …

大模型推理框架Vllm和TensorRT-LLM在ChatGLM2-6B模型的推理速度对比

目录 一、框架的特点简介 1、vllm pagedAttention Continuous batching 2、TensorRT-LLM WOQ——W4A16、W8A16 SQ——SmoothQuant AWQ——Activation-aware Weight Quantization 二、web推理服务 vllm_service tensortllm_service 三、推理速度对比 1、非业务数据 …

第48期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

游戏陪玩系统app

游戏陪玩系统APP为用户提供了一个便捷的平台&#xff0c;让他们能够轻松找到合适的陪玩者&#xff0c;一同享受游戏的乐趣。以下是对您提到的功能的详细解释&#xff1a; 游戏约玩&#xff1a; 在陪玩APP上&#xff0c;用户可以浏览陪玩者的信息&#xff0c;包括他们的游戏技能…

用Excel做一个功能完备的仓库管理系统

1 基本设计思路 用到的Excel技术&#xff1a;sumif, vlookup, 表格(table)。基本思路&#xff1a;在有基础的商品、仓库等信息的情况下&#xff0c;对商品的每一个操作都有对应的单据&#xff0c;然后再汇总统计。标识&#xff1a;为了在不同的维度统计数量&#xff0c;各单据…

【七】jmeter5.5+influxdb2.0+prometheus+grafana

参考文章&#xff1a;https://blog.csdn.net/wenxingchen/article/details/126892890 https://blog.csdn.net/Zuo19960127/article/details/119726652 https://blog.csdn.net/shnu_cdk/article/details/132182858 promethus参考 由于自己下载的是infuldb2.0&#xff0c;所以按照…

Hive服务详解

Hive服务 HiveServer2、Hive Metastore 服务服务共同构成了 Hive 生态系统中的核心功能&#xff0c;分别负责管理元数据和提供数据查询服务&#xff0c;为用户提供了一个方便、高效的方式来访问和操作存储在 Hive 中的数据。 1. Hive 查询服务&#xff08;HiveServer2&#xf…

jmeter之连接MySQL数据库

jmeter连接mysql数据库 mysql官网下载地址&#xff1a;MySQL :: Download Connector/J 步骤如下&#xff1a; 1、下载mysql的jar包放入到jmeter的lib/ext下&#xff0c;然后重启jmeter 链接: https://pan.baidu.com/s/1rRrMQKnEuKz8zOUfMdMHFg?pwdawfc 提取码: awfc 2、配置…

构建NodeJS库--前端项目的打包发布

1. 前言 学习如何打包发布前端项目&#xff0c;需要学习以下相关知识&#xff1a; package.json 如何初始化配置&#xff0c;以及学习npm配置项&#xff1b; 模块类型type配置&#xff0c; 这是nodejs的package.json的配置main 入口文件的配置 webpack 是一个用于现代 JavaSc…

ElasticSearch总结二

正向索引和倒排索引&#xff1a; 正向索引&#xff1a; 比方说我这里有一张数据库表&#xff0c;那我们知道对于数据库它一般情况下都会基于i d去创建一个索引&#xff0c;然后形成一个b树。 那么你根据i d进行检索的速度&#xff0c;就会非常的快&#xff0c;那么这种方式的…

Cesium之加载GeoServer或geowebcache的WMTS服务

文章目录 Cesium加载GeoServer的WMTS关键代码WMTS服务地址获取核心参数获取 Cesium加载GeoServer的WMTS关键代码 Cesium之加载GeoServer或geowebcache的WMTS服务关键代码如下 var url2"http://localhost:8090/geowebcache/service/wmts/rest/arcgis_com/{style}/{TileMat…

在excel中,如何在一个表中删除和另一个表中相同的数据?

现在有A表&#xff0c;是活动全部人员的姓名和学号&#xff0c;B表是该活动中获得优秀人员的姓名和学号&#xff0c; 怎么提取没有获得优秀人员的名单&#xff1f; 这里提供两个使用excel基础功能的操作方法。 1.条件格式自动筛选 1.1按住Ctrl键&#xff0c;选中全表中的姓…

的记忆:pandas(实在会忘记,就看作是一个 Excel 表格,或者是 SQL 表,或者是字典的字典。)

pandas 是一个开源的 Python 数据分析库&#xff0c;它提供了快速、灵活和富有表现力的数据结构&#xff0c;旨在使“关系”或“标记”数据的“快速分析、清洗和转换”变得既简单又直观。pandas 非常适合于数据清洗和转换、数据分析和建模等任务。以下是 pandas 的基本概念和主…