2N 方定点算法
2N方定点算法,是一次机缘巧合 针对DB层水平扩容的定制的算法。支持流量均衡,扩容节点流量只需偏移一台DB机器。
缘起
因最近负责的系统流量增长,需对DB层做水平扩容。我们的系统有以下特点:
DB层采用Redis做主库,MySQL做Redis异构库(用于数据持久化)
流量基本打在Redis上(Cache-As-SoR + Write-Behind)
比赛系统,写多读少,而且流量峰值到来可预估
用户平时不会使用系统,只有比赛期间才使用
比赛期间,需支持某一地区的所有学生 同时在线答题
分析
基于这个特点,可能很多同学看到了,会立马想到 一致性哈希算法
是的,我一上来也是按照一致性哈希算法去实现。但是在实现的过程中,发现 流量分布特别不均衡
当然最后发现不均衡的原因是 :
不过这都是 后话了。为了解决流量不均衡这个问题,我误打误撞想出这么个方法,觉得还是比较有趣。便记了下来
当时我就自以为是的分析了一下,不均衡的原因:
一致性哈希算法的节点使用ip来计算,而我们采用序号。
因为我们使用了阿里云的云服务,不直接连IP。其次 一致性哈希算法的节点环,实际上也不是均衡的,节点少时需设置虚拟节点。而我测试的时候,设置的虚拟节点数不够
一致性哈希算法的流量,是基于用户ip或者服务器的IP来计算哈希环,而我们使用用户ID
不使用用户IP的原因是:
我们是一个比赛系统。我们同其他系统不同,我们的用户IP有可能只有几个(每个学校的机房IP)
不使用服务器IP 使用用户ID的原因是:
其次我们希望比赛过程中,用户的流量不管他是否更换网络都落在一个DB节点上
这样做的好处是:
```
1. 同个用户的请求没有落在不同节点导致的读写一致性问题
2. 某个节点如果挂了,只影响这个节点的用户。其他节点的用户不受影响。
某个节点挂了,这个节点的流量偏移到临近的一个节点上。
增加节点,也只有临近一个节点流量偏移
```
带来的问题:
因为我们使用了用户ID,而同一个学校下的学生,他的用户ID一般都是连续的 (业务场景决定,后台名单批量导入,且用户系统使用自增ID) 。
每场比赛的用户,一般为同校或同区域的学生。所以,请求的流量中,大部分的用户ID落在一个几乎连续的范围内
> 对一段连续的ID,如何均衡到各个节点成了这个问题的关键
实现
基于以上分析,我有了个大胆的想法。一致性哈希算法原理其实就是哈希环范围比对。
没有加虚拟节点分布不均衡的原因,在于节点之间的距离长度不相等。既然如此,其实我们可以根据节点对哈希环进行等量切分。这样的话,流量就能均衡流到对应节点。
也就是固定位置,将整个哈希环无限2等分下去。每次新增节点,旧节点保持不变,新节点插入到旧节点的2等分处。注意序号不能更改,序号代表着流量指向。
节点变化图:
1个节点扩展为两个
两个节点扩充为4个
4个节点扩容为8个
这样因为节点总是在哈希环2N次方节点上,且每次插入节点旧节点序号不变,所以流量近乎均衡。
且新增流量,仅分摊顺时钟前一个节点一半的流量。流量倾斜,只影响一个节点。
节点位置
从上面的流量图中,我们可以看到:
由此我们可以得到节点哈希环位置公式:
其中k为节点序号,n为假定哈希环的最大长度。level为第几次切分。
别问,如何算出来的。数学渣,算了一个大半个晚上。
用户ID离散
节点已经确定,接下去就是如何将一段连续的用户ID映射到这个环上。这个时候,我就采取一个比较粗暴的方法。
因为哈希环肯定是一个2n次方范围的环,只要将用户ID离散分布到这个环上即可实现流量均衡。
那么如何将这段连续的用户ID离散呢
离散和连续实际是一个有范围限定的概念。一段连续的ID,对小的范围来说其实就是离散的。
如何理解: 假定一段连续用户ID [1024-2047]
步长为1。
这段ID 对于 2^32
的哈希环来说,当然是连续的,它就连续分布在哈希环上1024起,占整个环的 (1/2^22)
但是这段ID对于一个 2^10
的哈希环来说,他是连续的同时,也是离散的,因为它已经同时均匀分摊到整个环。
也就是对于任意个用户ID对应的哈希环位置有:
代码
基于上面的分析,也就是我们只要比对用户哈希环的范围,和节点哈希环的范围就能实现,流量均衡到相应节点。
为了实现简单,我也不做哈希环等比放大。默认节点环和用户ID环长度相同
大概的实现思路就是这样的。节点环和用户ID环如果长度需要不一致,只需要做缩放即可。
先简单介绍,后面有空再继续完善吧 (立flag小能手)
参考阅读
评论