通俗理解一致性哈希

把各服务器节点映射放在钟表的各个时刻上, 把 key 也映射到钟表的某个时刻上. 该 key 沿钟表顺时针走,碰到的第 1 个节点即为该 key 的存储节点

疑问 1: 时钟上的指针最大才 11 点,如果我有上百个 memcached 节点怎么办?
答: 时钟只是为了便于理解做的比喻,在实际应用中,我们可以在圆环上分布[0,2^32-1]的数字, 这样,全世界的服务器都可以装下了. 疑问 2: 我该如何把”节点名”,”键名”转化成整数?
答: 你可以用现在的函数,如 crc32().
也可以自己去设计转化规则,但注意转化后的碰撞率要低. 即不同的节点名,转换为相同的整数的概率要低.

一致性哈希对其他节点的影响

通过图可以看出,当某个节点 down 后,只影响该节点顺时针之后的 1 个节点,而其他节点

不受影响.因此,Consistent Hashing 最大限度地抑制了键的重新分布

 一致性哈希+虚拟节点对缓存命中率的影响

由图中可以看到,理想状态下, 1) 节点在圆环上分配分配均匀,因此承担的任务也平均,但事实上, 一般的 Hash 函数对于节
点在圆环上的映射,并不均匀. 2) 当某个节点 down 后,直接冲击下 1 个节点,对下 1 个节点冲击过大,能否把 down 节点上的
压力平均的分担到所有节点上?
完全可以---引入虚拟节点来达到目标 
虚拟节点即----N 个真实节点,把每个真实节点映射成 M 个虚拟节点, 再把 M*N 个虚拟节点, 散列在圆环上. 各真实节点对应的虚拟节点相互交错分布
这样,某真实节点 down 后,则把其影响平均分担到其他所有节点上

 

<?php

class Hash
{

</span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$nodeS</span> =<span style="color: #000000;"> [];
</span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$num</span> = 64<span style="color: #000000;">;
</span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$virtualNodeS</span> =<span style="color: #000000;"> [];

</span><span style="color: #008000;">//</span><span style="color: #008000;">需要把字符串转换为正整数</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> strToInt(<span style="color: #800080;">$str</span><span style="color: #000000;">)
{
    </span><span style="color: #0000ff;">return</span> <span style="color: #008080;">sprintf</span>('%u', <span style="color: #008080;">crc32</span>(<span style="color: #800080;">$str</span><span style="color: #000000;">));
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> findServer(<span style="color: #800080;">$key</span><span style="color: #000000;">)
{
    </span><span style="color: #800080;">$point</span> = <span style="color: #800080;">$this</span>-&gt;strToInt(<span style="color: #800080;">$key</span><span style="color: #000000;">);
    </span><span style="color: #008000;">//</span><span style="color: #008000;">foreach ($this-&gt;nodeS as $k =&gt; $v) {</span>
    <span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$this</span>-&gt;virtualNodeS <span style="color: #0000ff;">as</span> <span style="color: #800080;">$k</span> =&gt; <span style="color: #800080;">$v</span><span style="color: #000000;">) {
        </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$point</span> &lt;= <span style="color: #800080;">$k</span><span style="color: #000000;">) {
            </span><span style="color: #800080;">$node</span> = <span style="color: #800080;">$v</span><span style="color: #000000;">;
            </span><span style="color: #0000ff;">break</span><span style="color: #000000;">;
        }
    }

    </span><span style="color: #800080;">$node</span> = <span style="color: #800080;">$node</span> ?: <span style="color: #008080;">reset</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">nodeS);
    </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$node</span><span style="color: #000000;">;


}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> addServer(<span style="color: #800080;">$node</span><span style="color: #000000;">)
{

    </span><span style="color: #008000;">/*</span><span style="color: #008000;">$this-&gt;nodeS[$this-&gt;hashStr($node)] = $node;
    ksort($this-&gt;nodeS);</span><span style="color: #008000;">*/</span>
    <span style="color: #0000ff;">for</span> (<span style="color: #800080;">$i</span> = 1; <span style="color: #800080;">$i</span> &lt;= <span style="color: #800080;">$this</span>-&gt;num; <span style="color: #800080;">$i</span>++<span style="color: #000000;">) {

        </span><span style="color: #800080;">$this</span>-&gt;virtualNodeS[<span style="color: #800080;">$this</span>-&gt;strToInt(<span style="color: #800080;">$node</span>['host'] . '_' . <span style="color: #800080;">$node</span>['port'] . '_' . <span style="color: #800080;">$i</span>)] = <span style="color: #800080;">$node</span>['host'] . '_' . <span style="color: #800080;">$node</span>['port'<span style="color: #000000;">];
    }
    </span><span style="color: #008080;">ksort</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">virtualNodeS);
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> removeServer(<span style="color: #800080;">$key</span><span style="color: #000000;">)
{
    </span><span style="color: #800080;">$keyS</span> = <span style="color: #008080;">array_keys</span>(<span style="color: #800080;">$this</span>-&gt;virtualNodeS, <span style="color: #800080;">$key</span><span style="color: #000000;">);

    </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$this</span>-&gt;virtualNodeS <span style="color: #0000ff;">as</span> <span style="color: #800080;">$k</span> =&gt; &amp;<span style="color: #800080;">$v</span><span style="color: #000000;">) {
        </span><span style="color: #0000ff;">if</span> (<span style="color: #008080;">in_array</span>(<span style="color: #800080;">$k</span>, <span style="color: #800080;">$keyS</span><span style="color: #000000;">)) {
            </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$v</span><span style="color: #000000;">);
        }
    }
    </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$v</span><span style="color: #000000;">);
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> getServerList()
{
    </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">nodeS);
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> getVirtualNodeList()
{
    </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">virtualNodeS);
}

}

$Hash = new Hash();

\(Hash</span>-&gt;addServer(['host' =&gt; '10.16.134.65', 'port' =&gt; '11211'<span style="color: #000000;">]); </span><span style="color: #800080;">\)Hash->addServer(['host' => '10.16.134.65', 'port' => '11212']);
\(Hash</span>-&gt;addServer(['host' =&gt; '10.16.134.65', 'port' =&gt; '11213'<span style="color: #000000;">]); </span><span style="color: #800080;">\)Hash->getVirtualNodeList();
echo "<br>";
echo \(Hash</span>-&gt;strToInt('name'<span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> "&lt;br&gt;"<span style="color: #000000;">; </span><span style="color: #0000ff;">echo</span> <span style="color: #800080;">\)Hash->findServer('name');