【算法】一致性哈希

news2024/9/24 7:26:17

一、引言

        在分布式系统中,数据存储和访问的均匀性、高可用性以及可扩展性一直是核心问题。一致性哈希算法(Consistent Hashing)是一种分布式算法,因其出色的分布式数据存储特性,被广泛应用于缓存、负载均衡、数据库分片等场景。主要用于解决缓存和负载均衡等问题。

二、算法原理

        一致性哈希算法的核心思想是将数据映射到一个固定范围的哈希环上,服务器节点也映射到这个哈希环上。数据根据哈希值顺时针查找距离最近的服务器节点,从而完成数据的存储和访问。

哈希环

        一致性哈希算法使用一个长度为2^32的环形哈希空间,通常使用MD5或SHA-1等哈希函数将数据映射到这个空间。

虚拟节点

        为了解决服务器节点分布不均匀的问题,一致性哈希引入了虚拟节点的概念。每个物理节点对应多个虚拟节点,数据映射到虚拟节点上,从而实现数据的均匀分布。

三、数据结构

        一致性哈希算法主要使用的数据结构为哈希环和节点映射表。哈希环用于存储虚拟节点,节点映射表用于存储虚拟节点与物理节点的对应关系。

        哈希环:通过哈希函数计算的一圈环状空间,用来分布数据节点和数据对象。

        节点:数据存储的实际位置,通过节点的哈希值在哈希环上定位。

        数据对象:需要进行负载均衡或分布式存储的实际数据。

四、使用场景

一致性哈希算法广泛应用于以下场景:

        分布式缓存:如Memcached、Redis等。

        负载均衡:如LVS、Nginx等。

        数据库分片:如MySQL分片、MongoDB分片等。

五、算法实现

基本步骤:

        初始化节点:将每个节点通过哈希函数映射到哈希环上。

        数据分配:计算数据对象的哈希值,将其分配给顺时针最近的节点。

一致性哈希算法的伪代码实现:

初始化哈希环
初始化节点映射表

哈希函数:hash(key)
{
    return MD5(key) % 2^32
}

添加物理节点:addNode(physicalNode)
{
    for (i = 0; i < 虚拟节点数; i++)
    {
        virtualNode = hash(physicalNode + i)
        哈希环[virtualNode] = physicalNode
        节点映射表[virtualNode] = physicalNode
    }
}

删除物理节点:removeNode(physicalNode)
{
    for (virtualNode in 节点映射表)
    {
        if (节点映射表[virtualNode] == physicalNode)
        {
            哈希环[virtualNode] = null
            节点映射表[virtualNode] = null
        }
    }
}

查找节点:findNode(data)
{
    dataHash = hash(data)
    while (哈希环[dataHash] == null)
    {
        dataHash = (dataHash + 1) % 2^32
    }
    return 哈希环[dataHash]
}

六、其他同类算法对比

  1. 简单哈希算法:将数据直接映射到固定数量的服务器节点,当节点数量变化时,大部分数据需要重新映射,不够灵活。
  2. 带有限负载的一致性哈希算法:在一致性哈希基础上,考虑节点负载,实现更均匀的数据分布。

七、多语言实现

  Java

// 省略部分代码,仅展示关键方法
public class ConsistentHashing {
    private SortedMap<Integer, String> circle = new TreeMap<>();

    public void addNode(String node) {
        for (int i = 0; i < VIRTUAL_NODES; i++) {
            int hash = getHash(node + "#" + i);
            circle.put(hash, node);
        }
    }

    public void removeNode(String node) {
        for (int i = 0; i < VIRTUAL_NODES; i++) {
            int hash = getHash(node + "#" + i);
            circle.remove(hash);
        }
    }

    public String findNode(String key) {
        if (circle.isEmpty()) {
            return null;
        }
        int hash = getHash(key);
        if (!circle.containsKey(hash)) {
            SortedMap<Integer, String> tailMap = circle.tailMap(hash);
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        return circle.get(hash);
    }

    private int getHash(String key) {
        // 使用MD5散列函数
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.reset();
        md5.update(key.getBytes());
        byte[] digest = md5.digest();
        BigInteger bigInt = new BigInteger(1, digest);
        return bigInt.intValue        return bigInt.intValue() & 0x7fffffff;
    }
}

  Python

class ConsistentHashing:
    def __init__(self):
        self.circle = {}
        self.virtual_nodes = 100

    def _hash(self, key):
        return hash(key)

    def add_node(self, node):
        for i in range(self.virtual_nodes):
            virtual_node = f"{node}-{i}"
            hash_value = self._hash(virtual_node)
            self.circle[hash_value] = node

    def remove_node(self, node):
        for i in range(self.virtual_nodes):
            virtual_node = f"{node}-{i}"
            hash_value = self._hash(virtual_node)
            self.circle.pop(hash_value, None)

    def get_node(self, key):
        hash_value = self._hash(key)
        nodes = sorted(self.circle.keys())
        for node in nodes:
            if hash_value < node:
                return self.circle[node]
        return self.circle[nodes[0]]

C++

#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <algorithm>

class ConsistentHashing {
private:
    std::unordered_map<int, std::string> circle;
    int virtual_nodes;

    int _hash(const std::string& key) {
        std::hash<std::string> hash_fn;
        return hash_fn(key);
    }

public:
    ConsistentHashing(int virtual_nodes) : virtual_nodes(virtual_nodes) {}

    void addNode(const std::string& node) {
        for (int i = 0; i < virtual_nodes; ++i) {
            std::string virtual_node = node + "-" + std::to_string(i);
            int hash_value = _hash(virtual_node);
            circle[hash_value] = node;
        }
    }

    void removeNode(const std::string& node) {
        for (int i = 0; i < virtual_nodes; ++i) {
            std::string virtual_node = node + "-" + std::to_string(i);
            int hash_value = _hash(virtual_node);
            circle.erase(hash_value);
        }
    }

    std::string getNode(const std::string& key) {
        int hash_value = _hash(key);
        auto it = circle.lower_bound(hash_value);
        if (it == circle.end()) {
            return circle.begin()->second;
        }
        return it->second;
    }
};

Go

package main

import (
    "crypto/md5"
    "fmt"
    "sort"
    "strconv"
)

type ConsistentHashing struct {
    circle    map[int]string
    virtualNodes int
}

func NewConsistentHashing(virtualNodes int) *ConsistentHashing {
    return &ConsistentHashing{
        circle:    make(map[int]string),
        virtualNodes: virtualNodes,
    }
}

func (ch *ConsistentHashing) hash(key string) int {
    hash := md5.Sum([]byte(key))
    return int(hash[0]) | int(hash[1])<<8 | int(hash[2])<<16 | int(hash[3])<<24
}

func (ch *ConsistentHashing) AddNode(node string) {
    for i := 0; i < ch.virtualNodes; i++ {
        hash := ch.hash(node + strconv.Itoa(i))
        ch.circle[hash] = node
    }
}

func (ch *ConsistentHashing) RemoveNode(node string) {
    for i := 0; i < ch.virtualNodes; i++ {
        hash := ch.hash(node + strconv.Itoa(i))
        delete(ch.circle, hash)
    }
}

func (ch *ConsistentHashing) GetNode(key string) string {
    hash := ch.hash(key)
    var keys []int
    for k := range ch.circle {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, k := range keys {
        if hash < k {
            return ch.circle[k]
        }
    }
    return ch.circle[keys[0]]
}

func main() {
    // Example usage
}

八、实际服务应用场景代码框架

// CacheServer.java
public class CacheServer {
    private ConsistentHashing consistentHashing;

    public CacheServer() {
        consistentHashing = new ConsistentHashing();
        // 初始化服务器节点
        consistentHashing.addNode("Server1");
        consistentHashing.addNode("Server2");
        // ... 添加更多服务器节点
    }

    public void put(String key, String value) {
        String server = consistentHashing.findNode(key);
        // 将数据存储到对应的服务器
        storeData(server, key, value);
    }

        public String get(String key) {
        String server = consistentHashing.findNode(key);
        // 从对应的服务器获取数据
        return getData(server, key);
    }

    private void storeData(String server, String key, String value) {
        // 实现数据存储逻辑,例如通过网络发送到指定服务器
    }

    private String getData(String server, String key) {
        // 实现数据获取逻辑,例如通过网络从指定服务器获取数据
        return "value"; // 示例返回值
    }

    public void addServer(String server) {
        consistentHashing.addNode(server);
    }

    public void removeServer(String server) {
        consistentHashing.removeNode(server);
    }

    public static void main(String[] args) {
        CacheServer cacheServer = new CacheServer();
        cacheServer.put("key1", "value1");
        String value = cacheServer.get("key1");
        System.out.println("Retrieved value: " + value);

        // 动态添加和删除服务器
        cacheServer.addServer("Server3");
        cacheServer.removeServer("Server1");
    }
}

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

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

相关文章

【Django5】模板引擎

系列文章目录 第一章 Django使用的基础知识 第二章 setting.py文件的配置 第三章 路由的定义与使用 第四章 视图的定义与使用 第五章 二进制文件下载响应 第六章 Http请求&HttpRequest请求类 第七章 会话管理&#xff08;Cookies&Session&#xff09; 第八章 文件上传…

如何检查我的网站是否支持HTTPS

HTTPS是一种用于安全通信的协议&#xff0c;是HTTP的安全版本。HTTPS的主要作用在于为互联网上的数据传输提供安全性和隐私保护。通常是需要在网站安装部署SSL证书来实现网络数据加密传输&#xff0c;安全加密功能。 那么如果要检查你的网站是否支持HTTPS&#xff0c;可以看下…

培训第十一天(nfs与samba共享文件)

上午 1、环境准备 &#xff08;1&#xff09;yum源 &#xff08;一个云仓库pepl仓库&#xff09; [rootweb ~]# vim /etc/yum.repos.d/hh.repo [a]nameabaseurlfile:///mntgpgcheck0[rootweb ~]# vim /etc/fstab /dev/cdrom /mnt iso9660 defaults 0 0[rootweb ~]# mount -a[…

软件测试09 自动化测试技术(Selenium)

重点/难点 重点&#xff1a;理解自动化测试的原理及其流程难点&#xff1a;Selinum自动化测试工具的使用 目录 系统测试 什么是系统测试什么是功能测试什么是性能测试常见的性能指标有哪些 自动化测试概述 测试面临的问题 测试用例数量增多&#xff0c;工作量增大&#xff…

数据结构初阶(C语言)-二叉树

一&#xff0c;树的概念与结构 树是⼀种非线性的数据结构&#xff0c;它是由 n&#xff08;n>0&#xff09; 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 1.有⼀个特殊的结点&a…

ubuntu22安装拼音输入法

专栏总目录 一、安装命令&#xff1a; sudo apt update sudo apt install fcitx sudo apt install fcitx-pinyin 二、切换输入法

吴恩达深度学习笔记1 Neural Networks and Deep Learning

参考视频&#xff1a;(超爽中英!) 2024公认最好的【吴恩达深度学习】教程&#xff01;附课件代码 Professionalization of Deep Learning_哔哩哔哩_bilibili Neural Networks and Deep Learning 1. 深度学习引言(Introduction to Deep Learning) 2. 神 经 网 络 的 编 程 基 础…

数据库安全:MySQL安全配置,MySQL安全基线检查加固

「作者简介」&#xff1a;冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础著作 《网络安全自学教程》&#xff0c;适合基础薄弱的同学系统化的学习网络安全&#xff0c;用最短的时间掌握最核心的技术。 这一章节我们需…

【目标检测】Anaconda+PyTorch(GPU)+PyCharm(Yolo5)配置

前言 本文主要介绍在windows系统上的Anaconda、PyTorch、PyCharm、Yolov5关键步骤安装&#xff0c;为使用yolo所需的环境配置完善。同时也算是记录下我的配置流程&#xff0c;为以后用到的时候能笔记查阅。 Anaconda 软件安装 Anaconda官网&#xff1a;https://www.anaconda…

微软蓝屏事件:网络安全与系统稳定性的反思与前瞻

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

MFC:以消息为基础的事件驱动系统和消息映射机制

以消息为基础的事件驱动系统和消息映射机制 (1)消息 A.What&#xff08;什么是消息&#xff09; 本质是一个数据结构&#xff0c;用于应用程序不同部分之间进行通信和交互 typedef struct tagMSG {HWND hwnd; // 接收该消息的窗口句柄UINT message; // 消息标…

二分查找的实现

前提&#xff1a;数组是有序的 #include <stdio.h>//作用&#xff1a;利用二分查找法查找数据 //返回值&#xff1a;数据在数组中的索引 //找到了&#xff1a;真实索引 没找到&#xff1a;返回-1 int search(int arr[], int num, int len) {//查找范围int min 0;int …

DC系列靶场---DC 2靶场的渗透测试(一)

信息收集 Nmap扫描 nmap -sV -p- -sC -T4 172.30.1.141 域名解析 echo 172.30.1.141 dc-2 >> /etc/hosts 目录枚举 gobuster dir -u http://172.30.1.141 -w work/lab/CTF/ATT_CK_01/SecLists-master/Discovery/Web-Content/big.txt -x .php,.rar,.html,.zip -t 20 -b…

将代码转换为图表的 6大工具

文章目录 将代码转换为图表的 6大工具 - 前言1、[Diagrams](https://diagrams.mingrammer.com/) - Python2、[Go-Diagrams](https://github.com/blushft/go-diagrams) - Go3、[Mermaid ](https://mermaid.js.org/) - JavaScript4、[PlantUML](https://plantuml.com/zh/) - Java…

VUE之---slot插槽

什么是插槽 slot 【插槽】&#xff0c; 是 Vue 的内容分发机制&#xff0c; 组件内部的模板引擎使用slot 元素作为承载分发内容的出口。slot 是子组件的一个模板标签元素&#xff0c; 而这一个标签元素是否显示&#xff0c; 以及怎么显示是由父组件决定的。 VUE中slot【插槽】…

Druid【基础 01】是什么+主要特点+设计原则+架构+数据结构(简单入门Druid)

Druid入门 1. 是什么2. 主要特点3. 三个设计原则4. Architecture 架构5. 数据结构5.1 DataSource 结构5.2 Segment 结构 Druid 非中文官网&#xff0c;内容不少且介绍的挺详细的&#xff0c;需要英文阅读能力或者翻译工具进行辅助。 1. 是什么 先看看官网怎么说&#xff1a; A…

C#初级——基础语法

前言 学习Unity游戏编程开发会使用到两种语言&#xff0c;一种是C#&#xff0c;另一种是Javascript。有学习C语言基础和想学unity游戏开发的萌新一般都推荐学习C#基础编程&#xff0c;以此来快速上手unity的学习。 本次学习使用到的工具为&#xff1a;VS2022 环境安装 首先&a…

对比预测编码表示学习

对比预测编码表示学习 引言 文章主要提出如下几点&#xff1a;首先将高维数据压缩到更加紧凑的潜在嵌入&#xff08;latent embdding&#xff09;空间&#xff0c;在这个空间中条件预测更容易被建模。第二&#xff0c;在这个潜在空间中使用自回归模型&#xff0c;以对未来的多…

PingCAP 王琦智:下一代 RAG,tidb.ai 使用知识图谱增强 RAG 能力

导读 随着 ChatGPT 的流行&#xff0c;LLMs&#xff08;大语言模型&#xff09;再次进入人们的视野。然而&#xff0c;在处理特定领域查询时&#xff0c;大模型生成的内容往往存在信息滞后和准确性不足的问题。如何让 RAG 和向量搜索技术在实际应用中更好地满足企业需求&#…

【C++11】智能指针深度详解(什么是智能指针?为什么需要智能指针?如何使用智能指针?)

目录 一、前言 二、 智能指针的引入 --- 内存泄露 &#x1f4a2;什么是内存泄漏&#xff1f;&#x1f4a2; &#x1f4a2;内存泄漏有那些危害&#xff1f;&#x1f4a2; &#x1f4a2;内存泄漏的原因&#xff1f;&#x1f4a2; &#x1f4a2;解决内存泄漏的方法 &#x…