一致性哈希算法(Consistent Hashing Algorithm)

浅谈一致性Hash原理及应用

 

  在讲一致性Hash之前我们先来讨论一个问题。

  问题:现在有亿级用户,每日产生千万级订单,如何将订单进行分片分表?

  小A:我们可以按照手机号的尾数进行分片,同一个尾数的手机号写入同一片/同一表中。

  大佬:我希望通过会员ID来查询这个会员的所有订单信息,按照手机号分片/分表的话,前提是需要该用户的手机号保持不变,并且在查询订单列表时需要提前查询该用户的手机号,利用手机号尾数不太合理。

  小B:按照大佬的思路,我们需要找出一个唯一不变的属性来进行分片/分表。

  大佬:迷之微笑~

  小B:(信心十足)会员在我们这边保持不变的就是会员ID(int),我们可以通过会员ID的尾数进行分片/分表

  小C:尽然我们可以用会员ID尾数进行分片/分表,那就用取模的方式来进行分片/分表,通过取模的方式可以达到很好的平衡性。示意图如下:

  

  大佬:嗯嗯嗯,在不考虑会员冷热度的情况下小B和小C说的方案绝佳;但是往往我们的会员有冷热度和僵尸会员,通过取模的方式往往会出现某个分片数据异常高,部分分片数据异常低,导致平衡倾斜。示意图如下:

  

  大佬:当出现某个分片/分表达到极限时我们需要添加片/表,此时发现我们无法正常添加片/表。因为一旦添加片/或表的时候会导致绝大部分数据错乱,按照原先的取模方式是无法正常获取数据的。示意图如下

  

添加分片/分表前4,5,6会员的订单分别存储在A,B,C上,当添加了片/表的时候在按照(会员ID%N)方式取模去取数据4,5,6会员的订单数据时发现无法取到订单数据,因为此时4,5,6这三位会员数据分布存在了D,E,A上,具体示意图如下:

  

  大佬:所以通过取模的方式也会存在缺陷;好了接下来我们来利用一致hash原理的方式来解决分片/分表的问题。

首先什么是一致性哈希算法?一致性哈希算法(Consistent Hashing Algorithm)是一种分布式算法,常用于负载均衡。Memcached client也选择这种算法,解决将key-value均匀分配到众多Memcached server上的问题。它可以取代传统的取模操作,解决了取模操作无法应对增删Memcached Server的问题(增删server会导致同一个key,在get操作时分配不到数据真正存储的server,命中率会急剧下降)。

  还以上述问题为例,假如我们有10片,我们利用Hash算法将每一片算出一个Hash值,而这些Hash点将被虚拟分布在Hash圆环上,理论视图如下:  

  

  按照顺时针的方向,每个点与点之间的弧形属于每个起点片的容量,然后按照同样的Hash计算方法对每个会员ID进行Hash计算得出每个Hash值然后按照区间进行落片/表,以保证数据均匀分布。

如果此时需要在B和C之间新增一片/表(B1)的话,就不会出现按照取模形式导致数据几乎全部错乱的情况,仅仅是影响了(B1,C)之间的数据,这样我们清洗出来也就比较方便,也不会出现数据大批量

瘫痪。

  但是如果我们仅仅是将片/表进行计算出Hash值之后,这些点分布并不是那么的均匀,比如就会下面的这种情况,导致区间倾斜。如图

  这个时候虚拟节点就此诞生,下面让我们来看一下虚拟节点在一致性Hash中的作用。当我们在Hash环上新增若干个点,那么每个点之间的距离就会接近相等。按照这个思路我们可以新增若干个

片/表,但是成本有限,我们通过复制多个A、B、C的副本({A1-An},{B1-Bn},{C1-Cn})一起参与计算,按照顺时针的方向进行数据分布,按照下图示意:

  

此时A=[A,C1)&[A1,C2)&[A2,B4)&[A3,A4)&[A4,B1);B=[B,A1)&[B2,C)&[B3,C3)&[B4,C4)&[B1,A);C=[C1,B)&[C2,B2)&[C,B3)&[B3,C3)&[C4,A3);由图可以看出分布点越密集,平衡性约好。

using System;
using System.Collections.Generic;
using System.Data.HashFunction;
using System.Data.HashFunction.xxHash;
using System.Linq;
 
namespace HashTest
{
    public class ConsistentHash
    {
        /// <summary>
        /// 虚拟节点数
        /// </summary>
        private static readonly int VirturalNodeNum = 10;
 
        /// <summary>
        /// 服务器IP
        /// </summary>
        private static readonly string[] Nodes = { "192.168.1.1", "192.168.1.2", "192.168.1.3"};
 
        /// <summary>
        /// 按照一致性Hash进行分组
        /// </summary>
        private static readonly IDictionary<uint, string> ConsistentHashNodes = new Dictionary<uint, string>();
 
        private static uint[] _nodeKeys = null;
               
        public static void ComputeNode()
        {
            foreach (var node in Nodes)
            {
                AddNode(node);
            }
        }
 
        private static void AddNode(string node)
        {
            for (int i = 0; i < VirturalNodeNum; i++)
            {
                var key = node + ":" + i;
                var hashValue = ComputeHash(key);
                if (!ConsistentHashNodes.ContainsKey(hashValue))
                {
                    ConsistentHashNodes.Add(hashValue, node);
                }
            }
 
            _nodeKeys = ConsistentHashNodes.Keys.ToArray();
        }
 
        private static uint ComputeHash(string virturalNode)
        {
            var hashFunction = xxHashFactory.Instance.Create();
            var hashValue = hashFunction.ComputeHash(virturalNode);
            return BitConverter.ToUInt32(hashValue.Hash, 0);
        }
 
        public static string Get(string item)
        {
            var hashValue = ComputeHash(item);
            var index = GetClockwiseNearestNode(hashValue);
            return ConsistentHashNodes[_nodeKeys[index]];
        }
 
        private static int GetClockwiseNearestNode(uint hash)
        {
            int begin = 0;
            int end = _nodeKeys.Length - 1;
 
            if (_nodeKeys[end] < hash || _nodeKeys[0] > hash)
            {
                return 0;
            }
 
            while ((end - begin) > 1)
            {
                var mid = (end + begin) / 2;
                if (_nodeKeys[mid] >= hash) end = mid;
                else begin = mid;
            }
 
            return end;
        }
    }
}
 1 using System;
2 using System.Collections.Generic;
3 using System.Data.HashFunction;
4 using System.Data.HashFunction.xxHash;
5 using System.Linq;
6
7 namespace HashTest
8 {
9 public class ConsistentHash
10 {
11 /// <summary>
12 /// 虚拟节点数
13 /// </summary>
14 private static readonly int VirturalNodeNum = 10;
15
16 /// <summary>
17 /// 服务器IP
18 /// </summary>
19 private static readonly string[] Nodes = { "192.168.1.1", "192.168.1.2", "192.168.1.3"};
20
21 /// <summary>
22 /// 按照一致性Hash进行分组
23 /// </summary>
24 private static readonly IDictionary<uint, string> ConsistentHashNodes = new Dictionary<uint, string>();
25
26 private static uint[] _nodeKeys = null;
27
28 public static void ComputeNode()
29 {
30 foreach (var node in Nodes)
31 {
32 AddNode(node);
33 }
34 }
35
36 private static void AddNode(string node)
37 {
38 for (int i = 0; i < VirturalNodeNum; i++)
39 {
40 var key = node + ":" + i;
41 var hashValue = ComputeHash(key);
42 if (!ConsistentHashNodes.ContainsKey(hashValue))
43 {
44 ConsistentHashNodes.Add(hashValue, node);
45 }
46 }
47
48 _nodeKeys = ConsistentHashNodes.Keys.ToArray();
49 }
50
51 private static uint ComputeHash(string virturalNode)
52 {
53 var hashFunction = xxHashFactory.Instance.Create();
54 var hashValue = hashFunction.ComputeHash(virturalNode);
55 return BitConverter.ToUInt32(hashValue.Hash, 0);
56 }
57
58 public static string Get(string item)
59 {
60 var hashValue = ComputeHash(item);
61 var index = GetClockwiseNearestNode(hashValue);
62 return ConsistentHashNodes[_nodeKeys[index]];
63 }
64
65 private static int GetClockwiseNearestNode(uint hash)
66 {
67 int begin = 0;
68 int end = _nodeKeys.Length - 1;
69
70 if (_nodeKeys[end] < hash || _nodeKeys[0] > hash)
71 {
72 return 0;
73 }
74
75 while ((end - begin) > 1)
76 {
77 var mid = (end + begin) / 2;
78 if (_nodeKeys[mid] >= hash) end = mid;
79 else begin = mid;
80 }
81
82 return end;
83 }
84 }
85 }

1using System;
2using System.Collections.Generic;
3using System.Data.HashFunction;
4using System.Data.HashFunction.xxHash;
5using System.Linq;
6 7namespace HashTest
8{
9publicclass ConsistentHash
10 {
11///<summary>12/// 虚拟节点数
13///</summary>14privatestaticreadonlyint VirturalNodeNum = 10;
1516///<summary>17/// 服务器IP
18///</summary>19privatestaticreadonlystring[] Nodes = { "192.168.1.1", "192.168.1.2", "192.168.1.3"};
2021///<summary>22/// 按照一致性Hash进行分组
23///</summary>24privatestaticreadonly IDictionary<uint, string> ConsistentHashNodes = new Dictionary<uint, string>();
2526privatestaticuint[] _nodeKeys = null;
2728publicstaticvoid ComputeNode()
29 {
30foreach (var node in Nodes)
31 {
32 AddNode(node);
33 }
34 }
3536privatestaticvoid AddNode(string node)
37 {
38for (int i = 0; i < VirturalNodeNum; i++)
39 {
40var key = node + ":" + i;
41var hashValue = ComputeHash(key);
42if (!ConsistentHashNodes.ContainsKey(hashValue))
43 {
44 ConsistentHashNodes.Add(hashValue, node);
45 }
46 }
4748 _nodeKeys = ConsistentHashNodes.Keys.ToArray();
49 }
5051privatestaticuint ComputeHash(string virturalNode)
52 {
53var hashFunction = xxHashFactory.Instance.Create();
54var hashValue = hashFunction.ComputeHash(virturalNode);
55return BitConverter.ToUInt32(hashValue.Hash, 0);
56 }
5758publicstaticstring Get(string item)
59 {
60var hashValue = ComputeHash(item);
61var index = GetClockwiseNearestNode(hashValue);
62return ConsistentHashNodes[_nodeKeys[index]];
63 }
6465privatestaticint GetClockwiseNearestNode(uint hash)
66 {
67int begin = 0;
68int end = _nodeKeys.Length - 1;
6970if (_nodeKeys[end] < hash || _nodeKeys[0] > hash)
71 {
72return0;
73 }
7475while ((end - begin) > 1)
76 {
77var mid = (end + begin) / 2;
78if (_nodeKeys[mid] >= hash) end = mid;
79else begin = mid;
80 }
8182return end;
83 }
84 }
85 }

一致性哈希算法(Consistent Hashing Algorithm)的更多相关文章

  1. (转)每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)

    背景:在redis集群中,有关于一致性哈希的使用. 一致性哈希:桶大小0~(2^32)-1 哈希指标:平衡性.单调性.分散性.负载性 为了提高平衡性,引入“虚拟节点” 每天进步一点点——五分钟理解一致 ...

  2. 一致性哈希算法(consistent hashing)(转)

    原文链接:每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)  一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网 ...

  3. 转 白话解析:一致性哈希算法 consistent hashing

    摘要: 本文首先以一个经典的分布式缓存的应用场景为铺垫,在了解了这个应用场景之后,生动而又不失风趣地介绍了一致性哈希算法,同时也明确给出了一致性哈希算法的优点.存在的问题及其解决办法. 声明与致谢: ...

  4. 白话解析:一致性哈希算法 consistent hashing【转】

    学习一致性哈希算法原理的时候看到博主朱双印的一片文章,看完就懂,大佬! 白话解析:一致性哈希算法 consistent hashing

  5. 一致性哈希算法(consistent hashing)PHP实现

    一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希 ...

  6. _00013 一致性哈希算法 Consistent Hashing 新的讨论,并出现相应的解决

    笔者博文:妳那伊抹微笑 博客地址:http://blog.csdn.net/u012185296 个性签名:世界上最遥远的距离不是天涯,也不是海角,而是我站在妳的面前.妳却感觉不到我的存在 技术方向: ...

  7. 五分钟理解一致性哈希算法(consistent hashing)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法 ...

  8. 每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...

  9. 一致性哈希算法(consistent hashing)【转】

    一致性哈希算法 来自:http://blog.csdn.net/cywosp/article/details/23397179       一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希 ...

随机推荐

  1. Servlet之javax.servlet包

    链接 : http://blog.sina.com.cn/s/blog_5d4214c70102wnf1.html

  2. Vue项目骨架屏注入实践

    相比于早些年前后端代码紧密耦合.后端工程师还得写前端代码的时代,如今已发展到前后端分离,这种开发方式大大提升了前后端项目的可维护性与开发效率,让前后端工程师关注于自己的主业.然而在带来便利的同时,也带 ...

  3. 缓存地图 ArcGIS ——Local compact and exploded tile cache layer for WPF API

      ArcGISArcGIS 主页 特色 合约 图库 地图 组 帮助 我的内容 我的组织 登录 我的个人资料 帮助 管理员指南 登出 0 搜索全部内容 搜索地图 搜索图层 搜索应用程序 搜索工具 搜索 ...

  4. 20170731xlVba根据数据表和模板表生成新表

    Public Sub SplitData() Dim Wb As Workbook Dim Sht As Worksheet Dim NewSht As Worksheet Dim arr As Va ...

  5. 『Kaggle』Sklearn中几种分类器的调用&词袋建立

    几种分类器的基本调用方法 本节的目的是基本的使用这些工具,达到熟悉sklearn的流程而已,既不会设计超参数的选择原理(后面会进行介绍),也不会介绍数学原理(应该不会涉及了,打公式超麻烦,而且近期也没 ...

  6. C# 中的时间(DataTime)

    在做报表或查询的时候,常常会预设一些可选的日期范围,如本周.本月.本年等,利用 C# 内置的DateTime基本上都可以实现这些功能. 当前时间: DateTime dt = DateTime.Now ...

  7. Java容器涉及的类(代码)

    Customer: public class Customer implements Comparable{ private Integer customerId; private String cu ...

  8. iOS UI-集合视图(UICollectionView)

    BowenCollectionViewCell.xib #import <UIKit/UIKit.h> @interface BowenCollectionViewCell : UICol ...

  9. [LeetCode] 29. Divide Two Integers(不使用乘除取模,求两数相除) ☆☆☆

    转载:https://blog.csdn.net/Lynn_Baby/article/details/80624180 Given two integers dividend and divisor, ...

  10. Vysor安装图解

    Vysor安装图解   11 准备东西       路径 C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default   ...