当前位置:首页 > 问答 > 正文

用redis搞分布式计算,顺带聊聊一致性哈希算法怎么实现的

用Redis搞分布式计算,这个想法其实挺自然的,Redis本身就是一个非常快的内存数据库,它最擅长的是存一些简单的键值对,并且能保证很高的读写速度,当我们的系统越来越大,一台服务器撑不住的时候,很自然地就会想到把数据和计算任务分散到多台机器上去,这就是分布式,Redis本身就支持这种分布式的部署方式,也就是我们常说的Redis集群。

把数据分到多台机器上,马上就会遇到一个问题:我有一堆数据,也有一堆Redis服务器,我该把哪条数据放在哪台服务器上呢?最直接的办法是“哈希取模”,我有3台Redis服务器,编号0、1、2,当我需要存储一个键为“user:1001”的数据时,我计算一下这个键的哈希值,然后除以3,看余数是多少,如果余数是0,就放到0号服务器;余数是1,就放到1号服务器,以此类推,取数据的时候,也用同样的算法,就能知道该去哪个服务器上找了。

这个方法简单直接,在服务器数量固定的时候很好用,但问题就出在“固定”这两个字上,分布式系统里,机器故障是常态,我们可能需要随时增加新机器来扩容,或者下线一些老机器,这时候,“哈希取模”的弊端就暴露无遗了。

假设我们现在从3台服务器扩容到4台,那么计算余数的时候,除数就从3变成了4,我们再来算一下“user:1001”这个键,它的哈希值除以4的余数,很可能和之前除以3的余数不一样了,这意味着,这个数据本来在0号服务器上,现在根据新算法,它应该被分配到1号服务器,但1号服务器上根本没有这个数据!结果就是,大部分的数据位置都发生了错乱,当客户端去请求数据时,会大量地找不到数据,也就是“缓存雪崩”,整个系统可能就瘫痪了,这显然是不可接受的。

(引用来源:一致性哈希算法通常被归功于1997年由Karger等人发表的一篇学术论文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》,其初衷就是为了解决互联网热点问题以及简单的哈希取模在分布式缓存中带来的大规模缓存失效问题。)

正是为了解决这个“一动就乱”的难题,一致性哈希算法被提出来了,它的核心思想非常巧妙,不是去改变数据和服务器的映射关系,而是把它们都映射到一个固定的“圈”上。

你可以想象一个圆形的钟表盘,这个盘的取值范围是0到2的32次方减一,首尾相连,形成一个哈希环,我们不再用服务器的编号,而是用服务器的IP地址或者唯一标识名来计算哈希值,把这个哈希值映射到环上的某个点,同样地,对于每一条要存储的数据的键,我们也计算其哈希值,映射到环上。

这条数据应该存放在哪台服务器上呢?规则是:从数据的哈希值点出发,沿着环顺时针方向往前走,遇到的第一台服务器的节点,就把数据存在那台服务器上。

这样做有什么天大的好处呢?当我们增加一台新服务器时,比如在环上的某个位置插入了一个新节点C,受影响的仅仅是那些哈希值落在新节点C和环上它逆时针方向的上一个节点B之间的数据,原本这些数据是属于节点B的,现在它们需要被重新分配到新节点C上,而对于环上其他大部分的数据,比如原本属于节点A的,它们顺时针方向遇到的第一个节点还是A,完全不受影响!同样地,下线一台服务器时,也只会影响直接关联的一小部分数据。

这样一来,扩容或缩容时,需要移动的数据量就从一个巨大的、几乎全量的规模(如哈希取模),降到了一个局部的、小范围的规模,这就保证了数据迁移的成本最低,对系统的影响最小。

基本的一致性哈希算法还有个问题:如果服务器节点在环上分布不均匀,就可能导致某些节点负载过高,成为“热点”,为了解决这个问题,又引入了“虚拟节点”的概念,就是不再让一台物理服务器对应环上的一个点,而是让它对应环上的多个点(比如200个虚拟节点),这些虚拟节点的哈希值是通过在服务器IP后加上编号(如“192.168.1.1#1”、“192.168.1.1#2”)来计算的,这样,一台物理服务器就分散在了环的多个地方,数据分布会更均匀,当需要扩容时,新加入的物理服务器也会以多个虚拟节点的形式散落在环上,从而从多个现有的服务器上接管一小部分数据,进一步实现了负载的均衡。

(引用来源:虚拟节点的概念是对基础一致性哈希的重要改进,在实际应用中(如Amazon的Dynamo数据库系统)被广泛采用,以确保负载在各节点间更均匀地分布。)

回到用Redis搞分布式计算这个话题上,Redis集群的内部数据分片机制,正是采用了类似一致性哈希的思想(具体实现是哈希槽,但核心理念相通),来保证在增加或移除节点时,只有少量数据需要迁移,从而维持集群的高可用性,而基于Redis的分布式计算,比如一些任务队列(如Celery的后端用Redis),其分布式主要是通过多个工作节点从同一个Redis队列里取任务来实现的,它更关注任务的分配和执行,和数据分片是不同层面的问题,但理解了一致性哈希,你就理解了分布式系统中最核心的数据分布难题是如何被优雅解决的。

用redis搞分布式计算,顺带聊聊一致性哈希算法怎么实现的