大家好,我是蓝胖子,想起之前学算法的时候,常常只知表面,不得精髓,这个算法到底有哪些应用场景,如何应用在工作中,后来随着工作的深入,一些不懂的问题才慢慢被抽丝剥茧分解出来。

今天我们就来看看工作和面试中经常被点名的算法,一致性hash算法,并且我会介绍它在实际的应用场景并用代码实现出来。

本节的源码已经上传到github

https://github.com/HobbyBear/codelearning/tree/master/consistenthash

原理介绍

首先我们来看看一致性hash的定义和算法思想,一致性hash算法有别于传统hash算法,例如我们有3个节点,现在要考虑某个key值落到哪个节点上,传统hash算法是将key通过hash函数后通过节点数量进行取模运算得到需要落到的节点序号。

nodeIdx := hashFunc(key)%len(nodes)

传统hash算法在节点数量变化时基本上会导致大量旧数据经过hash得到的节点序号失效, 而一致性hash算法则能够保证只有少部分旧数据需要重新改变需要落到的节点,其余数据依然能够保证节点扩容后,hash计算得到的节点序号和之前一致。

一致性hash算法假设了一个很大的数字空间,比如2的32次方, 节点信息会被映射到这个数字空间的某个数字上,当我们需要看某个key落到哪个节点上时,也需要将key进行hash计算得到某个数字,接着就是找到在这个超大数字空间内,第一个大于该数字的节点。如果没有大于该数字的节点,则将第一个节点作为key需要落到的节点。

这样就等效于整个数字空间构成了一个环形结构,寻找key需要落到的节点上时,则是从key开始顺时针寻找第一个节点。

用下面的示意图来表示这个过程会更好理解

我们假设有3个节点A1,B1,C1, 这三个节点的信息(比如主机名,ip等信息)经过hash运算后得到了3个数字,A1对应10000,B1对应12000,C1 对应30000,现在需要看某个key需要落到哪个节点上,就应该这样来看。

注意这里的节点我是拿服务器来举例,实际上,节点也可以是表,某个key可以看出是表中的某一行,而一致性性hash算法的目的则是看某一行数据应该落到哪个表中,总之你可以发挥你的想象将算法中的事物进行代替抽象,算法的思想终究是不变的。

当某个key经过hash计算后,得到数字9000,那么在顺时针寻找到第一个大于它的节点则是节点A1,如果key经过hash计算后,得到数字11000,那么寻找到的第一个大于它的节点则是节点B1。 注意一种特殊情况,如果key经过hash计算得到的数字是40000,那么此时没有任何一个节点是大于这个数字的,这种情况,正如上图所示,一致性hash算法的数组空间是环形结构,这样key会落到第一个节点A1上。

这个只是最初版本的一致性hash,它会在节点数量较少时,出现分配数据不均匀的情况,比如可能会出现下面的场景

所有的节点都偏向了一侧,这样将会有大量数据落到A1 节点,造成数据分配不均匀。

所以一致性hash算法的改进版本提出虚拟节点的概念,通过引入节点的副本来让整个hash环上的节点数量多起来。

这里假设引入的副本是一个,那么参与分配的key的节点在hash环上则是6个,6个节点会让对hash环的分配更加均匀,注意虚拟节点在实际环境中并不存在,比如这里虚拟节点A2和实际的节点A1指向的其实都是同一个实际环境中的节点。

应用场景

在了解了一致性hash算法的原理后,我们再来看看它的一些适用场景,这样能够明白算法的目的,不至于纸上谈兵。

负载均衡

首先来看下第一种应用场景,在负载均衡中的应用,拿memcache举例,memcache的分布式架构其实是依赖客户端来实现的,客户端将缓存key通过一致性hash算法计算需要缓存到哪台后端服务器上。

而采用一致性hash的好处则是在扩缩容时,不会导致大面积的缓存失效。



如上图所示,现在要将D1节点下掉,由于一致性hash算法路由节点是顺时针的,那么只会影响到D1和A1之间的数据,这部分数据后续需要在B1节点上进行读取,而其他节点上的数据则不会影响。

其实,从这里应该能够看出,一致性hash算法在负载均衡中一个极大的好处就是,对于有状态的服务,能够做到扩缩容节点时,影响面最小。

分库分表

再来看看在分库分表中的应用,如果分表时采用传统hash算法,当还想扩容表时,不得不面对对所有分表数据进行重新hash,重新写入,这无论是对于磁盘io还是cpu都有极大的压力,我们应该在新增分表时尽量迁移少量的数据,减少影响面,这不正是一致性hash算法的功能吗。



如上图所示,现在新增了分表D1,那么会影响到之前D1到A1的之前的数据,这部分数据之前是存到E1这张表上的,现在要迁移到D1表,所以你可以看到新增一个分表只会设计两张表部分数据的迁移,相比传统hash的全量迁移,优势不言而喻。

代码实现

现在我们来看下如何实现下这个算法。

我们需要将节点信息以及用户key信息映射成一个数字,这里要用到hash函数,hash函数有很多,我们直接用一个,crc32的hash方式,这样返回的数字刚好在2的32次方以内。

func ChecksumIEEE(data []byte) uint32

同时我们需要一个映射结构存储节点在环上的hash key与节点信息,还需要一个有序列表存储hash key,以便于查询用户key对应的节点hash key是哪一个。

这里的代码比较简单,短短20多行即可。

package main  

import (
"fmt"
"hash/crc32"
"sort") func main() {
ch := NewConsistentHash(3)
ch.AddNodes("node1")
ch.AddNodes("node2")
ch.AddNodes("node3")
fmt.Println(ch.GetNode("lanpangzi"))
} type ConsistentHash struct {
nodes map[uint32]string
keys []uint32
replicates int
} func NewConsistentHash(replicate int) *ConsistentHash {
return &ConsistentHash{
nodes: make(map[uint32]string),
keys: make([]uint32, 0),
replicates: replicate,
}
} func (c *ConsistentHash) AddNodes(node string) {
for i := 0; i <= c.replicates; i++ {
nodename := fmt.Sprintf("%s#%d", node, i)
hashKey := crc32.ChecksumIEEE([]byte(node))
c.nodes[hashKey] = nodename
c.keys = append(c.keys, hashKey)
}
sort.Slice(c.keys, func(i, j int) bool {
return c.keys[i] < c.keys[j]
})
} func (c *ConsistentHash) GetNode(key string) string {
hashKey := crc32.ChecksumIEEE([]byte(key))
nodekeyIndex := sort.Search(len(c.keys), func(i int) bool {
return c.keys[i] >= hashKey
})
if nodekeyIndex == len(c.keys) {
nodekeyIndex = 0
}
return c.nodes[c.keys[nodekeyIndex]]
}

我们搞定了一致性hash算法,代码实现并不难,关键是要搞懂算法的原理以及作用,这样才能灵活运用。

一致性hash算法原理及实践的更多相关文章

  1. 给面试官讲明白:一致性Hash的原理和实践

    "一致性hash的设计初衷是解决分布式缓存问题,它不仅能起到hash作用,还可以在服务器宕机时,尽量少地迁移数据.因此被广泛用于状态服务的路由功能" 01分布式系统的路由算法 假设 ...

  2. 分布式缓存技术memcached学习(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...

  3. 分布式缓存技术memcached学习系列(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到"分布式一致性hash算法"这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前, ...

  4. 一致性Hash算法原理及C#代码实现

    一.一致性Hash算法原理 基本概念 一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),整个哈希空间环如下: 整个空间按顺 ...

  5. 一致性Hash算法原理,java实现,及用途

    学习记录: 一致性Hash算法原理及java实现:https://blog.csdn.net/suifeng629/article/details/81567777 一致性Hash算法介绍,原理,及使 ...

  6. 【数据结构与算法】一致性Hash算法及Java实践

    追求极致才能突破极限 一.案例背景 1.1 系统简介 首先看一下系统架构,方便解释: 页面给用户展示的功能就是,可以查看任何一台机器的某些属性(以下简称系统信息). 消息流程是,页面发起请求查看指定机 ...

  7. 一致性Hash算法的原理与实现(分布式映射算法)

    一致性Hash算法解决的问题: 解决分布式系统中的负载均衡问题 背景问题:有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均发到每台服务器上,每台服务器负载1/N的服务 硬Hash映射:将 ...

  8. 一致性Hash算法在数据库分表中的实践

    最近有一个项目,其中某个功能单表数据在可预估的未来达到了亿级,初步估算在90亿左右.与同事详细讨论后,决定采用一致性Hash算法来完成数据库的自动扩容和数据迁移.整个程序细节由我同事完成,我只是将其理 ...

  9. 7.redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?

    作者:中华石杉 面试题 redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗? 面试官心理分析 在前几年, ...

  10. [速成]了解一致性hash算法

    定义 一致性hash算法,在维基百科的定义是: Consistent hashing is a special kind of hashing such that when a hash table ...

随机推荐

  1. [Linux]VMware启动CENOTS7时报"welcome to emergency mode!"【转载】

    1 问题描述 由于通过VMwaer快速克隆了一台CENTOS7.9的虚拟机. 但启动时报如下错误信息 welcome to emergency mode!after logging in ,type ...

  2. LeeCode 二叉树问题(四)

    二叉搜索树的应用问题 二叉搜索树的定义 若左子树不空,则左子树上所有节点的值均小于根节点的值 若右子树不空,则右子树上所有节点的值均大于根节点的值 它的左右子树也均为二叉搜索树 中序遍历结果为一个升序 ...

  3. 【Java SE】网络编程

    1. 网络编程概述 网络编程的目的:直接或者间接地通过网络协议与其他计算机实现数据交换,进行通讯. 网络编程两个主要的问题: ①如何精准地定位网络上的一台或多台主机,并定位主机上的特定应用 ②找到主机 ...

  4. c++基本数据结构

    基本数据结构: 一.线性表 1.顺序结构 线性表可以用普通的一维数组存储. 你可以让线性表可以完成以下操作(代码实现很简单,这里不再赘述): 返回元素个数. 判断线性表是否为空. 得到位置为p的元素. ...

  5. 3.2 构造器、this、包机制、访问修饰符、封装

    构造器 构造器:在实例化的一个对象的时候会给对象赋予初始值,因此我们可以通过修改构造器,来改变对象的初始值,构造器是完成对象的初始化,并不是创建对象 我们也可以创建多个构造器实现不同的初始化,即构造器 ...

  6. 基于 Rainbond 的混合云管理解决方案

    内容概要:文章探讨了混合云场景中的难点.要点,以及Rainbond平台在跨云平台的混合云管理方面的解决方案.包括通过通过统一控制台对多集群中的容器进行编排和管理,实现了对混合云中应用的一致性管理.文章 ...

  7. 【Docker】Harbor 分布式仓库管理

    一.Harbor 介绍 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目,其目标是帮助用户迅速搭建一个企业级的 Docker Registry (私有仓库)服务. ...

  8. 从原理到应用,人人都懂的ChatGPT指南

    作者:京东科技 何雨航 引言 如何充分发挥ChatGPT潜能,已是众多企业关注的焦点.但是,这种变化对员工来说未必是好事情.IBM计划用AI替代7800个工作岗位,游戏公司使用MidJourney削减 ...

  9. 使用 Lambda 函数将 CloudWatch Log 中的日志归档到 S3 桶中

    > 作者:[SRE运维博客](https://www.cnsre.cn/) > 博客地址:[https://www.cnsre.cn/](https://www.cnsre.cn/) &g ...

  10. 2023-04-06:拥抱Golang,优化FFmpeg音频编码器,探究encode_audio.c的内部结构。

    2023-04-06:拥抱Golang,优化FFmpeg音频编码器,探究encode_audio.c的内部结构. 答案2023-04-06: 见moonfdd/ffmpeg-go库. 这段代码是一个示 ...