Consistent HASH(自己读后理解)
一致性HASH, 作用在于当有服务器实效的情况下,保持CACHE的一致性.避免引起大量读CACHE失效.
假设服务部分的架构如下: appServer(10台)--- servicesServer(3台)---Database(2台 master,slave)
servicesServer有相关用户数据的CACHE, 通过id模servicesServerNum映射到相应的servicesServer上。 假设 有用户 1 到 3 访问某个页面,即用户(u1,u2,u3)访问 appServer. appServer通过接口调用访问servicesServer,用户0的CACHE在servicesServer0上,以此类推.
可是天有不测风云, servicesServer0因为磁盘问题挂掉了,现在会出现什么情况呢?
由于我们通过 id模servicesServerNum映射得到相应的servicesServer,但是现在只有2台机器了, 于是 :
before servicesServer0 down: (0--s0, 1--s1, 2--s2)
u1 1mod3--> s1
u2 2mod3--> s2
u3 3mod3--> s0
after servicesServer0 down(0--s1, 1--s2)
u1 1mod2--> s2 change
u2 2mod2--> s1 change
u3 3mod2--> s2 change
在这种情况下,原来的CACHE全部失效, 所有的读请求全部瞬间压倒Database上面。
一致性CACHE就是为了解决这个问题提出来的。它不是直接的取模映射,而是将一个区间影射到特定服务器上,这样即使机器减少或者增加,大部分的CACHE仍然能够有效,避免了瞬间的冲击。 为了更好的平衡CACHE的分布,还引入了虚拟节点的概念,一个实际的节点,可以化生为多个不同的虚拟节点,这样能够让区间映射更加平衡。 下面是我测试的代码 :
class Program { BNode[] _BNodes; int _ArrayLength; static void Main(string[] args) { Program ptest = new Program(); ptest.InitArray(5, 4); ptest.ShowNodes(5,4); ptest.SortArray(); int restring = ptest.BserachServer(3); restring = ptest.BserachServer(152); restring = ptest.BserachServer(155); restring = ptest.BserachServer(0); } void InitArray(int RealNodeNum, int VirtualCopyNum) { int totalNum = RealNodeNum * VirtualCopyNum ; _BNodes = new BNode[totalNum]; _ArrayLength = totalNum; for (int i = 0; i < RealNodeNum; i++) { for(int j = 0; j < VirtualCopyNum ; j++) { BNode tempNode = new BNode(); tempNode.IPAddr = "172.16.73." + (i+1); tempNode.hashcode = ( "V" + j + tempNode.IPAddr ).GetHashCode(); //Console.Out.WriteLine(tempNode.hashcode); tempNode.flag = 1; //Console.Out.WriteLine(tempNode.hashcode); _BNodes[i] = tempNode; } } } void ShowNodes(int RealNodeNum, int VirtualCopyNum) { int totalNum = RealNodeNum * VirtualCopyNum; for (int i = 0; i < totalNum; i++) { Console.Out.Write(_BNodes[i].hashcode + " "); Console.Out.Write(_BNodes[i].flag + " "); Console.Out.WriteLine(_BNodes[i].IPAddr); } } bool DelOneNode(int machineID, int VirtualCopyNum) { int i = machineID; for (int j = 0; j < VirtualCopyNum; j++) { string IPAddr = "172.16.73." + (i + 1); int hashcode = ("V" + j + IPAddr).GetHashCode(); BNode delNode = BserachNode(hashcode); if (delNode != null) { delNode.flag = 0; } } return true; } int FindNode(int objectHashCode) { int pos = BserachServer(objectHashCode); int tempos = pos; int cycleFlag =0; while ( !( tempos == pos && cycleFlag == 1) ) { if (_BNodes[pos].flag > 0) { return pos; } else { if (pos == 0) { pos = _ArrayLength-1; cycleFlag = 1; } pos = pos - 1; } } return 0; } void SortArray() { ShowNodes(5, 4); IComparer myComparer = new myCompareClass(); Array.Sort(_BNodes, myComparer); ShowNodes(5, 4); } int BserachServer(int cmphashcode) { if (_BNodes[_ArrayLength - 1].hashcode <= cmphashcode || _BNodes[0].hashcode > cmphashcode) { return _ArrayLength - 1; } { int lowPos = 0; int highPos = _ArrayLength; int middlePos = (lowPos + highPos) / 2; while (true) { middlePos = (lowPos + highPos) / 2; if (middlePos < lowPos ) { return middlePos; } if (lowPos > highPos) { return middlePos; } if (cmphashcode < _BNodes[middlePos].hashcode) { highPos = middlePos - 1; } else if (cmphashcode > _BNodes[middlePos].hashcode) { lowPos = middlePos + 1; } else { return middlePos; } } } } BNode BserachNode(int cmphashcode) { int lowPos = 0; int highPos = _ArrayLength; int middlePos = (lowPos + highPos) / 2; while (middlePos < lowPos || lowPos > highPos) { middlePos = (lowPos + highPos) / 2; if (cmphashcode < _BNodes[middlePos].hashcode) { highPos = middlePos - 1; } else if (cmphashcode > _BNodes[middlePos].hashcode) { lowPos = middlePos + 1; } else { return _BNodes[middlePos]; } } return null; } internal class BNode { public int hashcode; public string IPAddr; public int flag; } internal class myCompareClass : IComparer { int IComparer.Compare(object x, object y) { if (((BNode)x).hashcode == ((BNode)y).hashcode) { return 0; } else if (((BNode)x).hashcode > ((BNode)y).hashcode) { return 1; } else return -1; } } }