一致性哈希算法的实现

本文深入探讨了一致性哈希算法,重点讲解了其平衡性和如何通过引入虚拟结点来优化分布式系统中的负载均衡策略。一致性哈希是解决分布式缓存中数据分片问题的关键技术,它有效地解决了节点动态增减时数据迁移的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一致性哈希算法

package com.hashdemo;

import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 不带虚拟节点的一致性Hash算法
 * 重点关注:
 * 1、如何造一个hash环
 * 2、如何在哈希环上映射服务器节点
 * 3、如何找到对应的节点
 */
public class ConsistHashWithoutVirsualNode {

    /**
     * 服务器列表
     */
    private static String[] servers = {"server1", "server2", "server3", "server4"};

    /**
     * key:服务器生成的hash值
     * value:服务器名称
     */
    private static SortedMap<Integer, String> hashServerMap = new TreeMap<Integer, String>();

    /**
     * 预先生成服务器对应的hash值,并放到map里面
     */
    static {
        for (int i = 0; i < servers.length; i++) {
            int hash = generateHash(servers[i]);
            System.out.println("[" + servers[i] + "]加入集合中,其hash值为" + hash);
            hashServerMap.put(hash, servers[i]);
        }
    }

    /**
     * 生成Hash的算法FNV1_32_Hash
     *
     * @param str 任意字符串
     * @return hash值
     */
    private static int generateHash(String str) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < str.length(); i++)
            hash = (hash ^ str.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 17;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        //不允许为负值
        if (hash < 0) hash = Math.abs(hash);
        return hash;
    }

    /**
     * 寻找路由
     * @param key 任意的字符串
     * @return 返回服务器名称
     */
    private static String findRouting(String key) {
        //计算这个Key的Hash值
        int hash = generateHash(key);
        //查找到大于该hash值的所有map
        SortedMap<Integer, String> greaterThanThisHashMap = hashServerMap.tailMap(hash);
        //如果没有找到比该hash值大的map
        if (greaterThanThisHashMap.isEmpty()) {
            //从第一个node开始
            Integer i = hashServerMap.firstKey();
            //返回对应的服务器名
            return hashServerMap.get(i);
        } else {
            //取出第一个Node,因这个Node离Key所对应的hash值最近
            Integer i = greaterThanThisHashMap.firstKey();
            //返回对应的服务器名
            return greaterThanThisHashMap.get(i);
        }
    }

    public static void main(String[] args) {
        String[] keys = {"key1", "key2", "key3", "key4", "key5", "key3"};
        for (int i = 0; i < keys.length; i++) {
            System.out.println("[" + keys[i] + "]的hash值为" + generateHash(keys[i]) + ",被路由到结点[" + findRouting(keys[i]) + "]");
        }
    }

}

平衡性 带虚拟结点的哈希算法

package com.hashdemo;

import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 带虚拟节点的一致性哈希算法
 */
public class ConsistHashWithVirsualNode {
    //待加入的服务器列表
    private static String[] servers = {"server1", "server2", "server3", "server4"};
    //真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
    private static List<String> rNodes = new LinkedList<String>();
    private static SortedMap<Integer, String> vNodes = new TreeMap<Integer, String>();
    //每个真实节点,加上5个虚拟节点
    private static final int V_Nodes_LEN = 10;

    static {
        for (int i = 0; i < servers.length; i++)
            rNodes.add(servers[i]);
        for (String str : rNodes) {
            for (int i = 0; i < V_Nodes_LEN; i++) {
                String virtualNodeName = str + "&&VN" + String.valueOf(i);
                int hash = generateHash(virtualNodeName);
                System.out.println("虚拟节点[" + virtualNodeName + "]被添加,hash值为" + hash);
                vNodes.put(hash, virtualNodeName);
            }
        }
        System.out.println();
    }

    //使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别
    private static int generateHash(String str) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < str.length(); i++)
            hash = (hash ^ str.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;
    }

    /**
     * 寻找路由
     *
     * @param key
     * @return
     */
    private static String findRouting(String key) {
        //计算这个Key的Hash值
        int hash = generateHash(key);
        //查找到大于该hash值的所有map
        SortedMap<Integer, String> greaterThanThisHashMap = vNodes.tailMap(hash);
        String vn;
        //如果没有找到比该hash值大的map
        if (greaterThanThisHashMap.isEmpty()) {
            //从第一个node开始
            Integer i = vNodes.firstKey();
            //返回对应的虚拟服务器名
            vn = vNodes.get(i);
        } else {
            //取出第一个Node,因这个Node离Key所对应的hash值最近
            Integer i = greaterThanThisHashMap.firstKey();
            //返回对应的虚拟服务器名
            vn = greaterThanThisHashMap.get(i);
        }
        //从虚拟服务器名,可以分割出真实的服务器名
        if (vn != null && vn.length() > 0)
            return vn.substring(0, vn.indexOf("&&"));

        return null;
    }

    public static void main(String[] args) {
        String[] keys = {"key1", "key2", "key3", "key4", "key5", "key3"};
        for (int i = 0; i < keys.length; i++) {
            System.out.println("[" + keys[i] + "]的hash值为" + generateHash(keys[i]) + ",被路由到结点[" + findRouting(keys[i]) + "]");
        }
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值