最容易想到的算法是取模算法,即 N 个节点要,从 0->N-1 编号. key 对 N 取模,余 i,则 key 落在第 i 台服务器上.

余数分布式的缺陷

假设有 8 台服务器, 运行中,突然 down 一台, 则求余的底数变成 7
后果:

key0%8==0, key0%7 ==0 hits ….

key6%8==6, key6%7== 6 hits
key7%8==7, key7%7==0 miss
key9%8==1, key9%7 == 2 miss

key55%8 ==7 key55%7 == 6 miss

取模命中率为(1 / N),所以: 服务器越多, 则 down 机的后果越严重!

<?php

class Residual
{
protected \(nodeS</span> =<span style="color: #000000;"> []; </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">\)nodeNum = 0;

</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: #008000;">//</span><span style="color: #008000;">通过key找到对应服务器</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: #800080;">$nodeSCount</span> = <span style="color: #008080;">count</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">nodeS);

    </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$nodeSCount</span> &gt; 0<span style="color: #000000;">) {

        </span><span style="color: #800080;">$node</span> = <span style="color: #800080;">$point</span> % <span style="color: #800080;">$nodeSCount</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$node</span><span style="color: #000000;">;


    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
        </span><span style="color: #0000ff;">return</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> addServer(<span style="color: #800080;">$node</span><span style="color: #000000;">)
{

    </span><span style="color: #800080;">$this</span>-&gt;nodeS[] = <span style="color: #800080;">$node</span><span style="color: #000000;">;
    </span><span style="color: #800080;">$this</span>-&gt;nodeNum += 1<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> removeServer(<span style="color: #800080;">$node</span><span style="color: #000000;">)
{
    </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$this</span>-&gt;nodeS <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;">$v</span> == <span style="color: #800080;">$node</span><span style="color: #000000;">) {
            </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$this</span>-&gt;nodeS[<span style="color: #800080;">$k</span><span style="color: #000000;">]);
            </span><span style="color: #800080;">$this</span>-&gt;nodeNum -= 1<span style="color: #000000;">;
            </span><span style="color: #0000ff;">break</span><span style="color: #000000;">;
        }
    }
    </span><span style="color: #800080;">$this</span>-&gt;nodeS = <span style="color: #008080;">array_merge</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">nodeS);

}

</span><span style="color: #008000;">//</span><span style="color: #008000;">获取服务器列表</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: #008080;">var_dump</span>(<span style="color: #800080;">$this</span>-&gt;<span style="color: #000000;">nodeNum);
}

}

\(Residual</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Residual(); </span><span style="color: #800080;">\)Residual->addServer(['host' => '10.16.134.65', 'port' => '11211']);
\(Residual</span>-&gt;addServer(['host' =&gt; '10.16.134.65', 'port' =&gt; '11212'<span style="color: #000000;">]); </span><span style="color: #800080;">\)Residual->addServer(['host' => '10.16.134.65', 'port' => '11213']);
$Residual->getServerList();

for (\(i</span> = 1; <span style="color: #800080;">\)i <= 10; \(i</span>++<span style="color: #000000;">) { </span><span style="color: #800080;">\)key = 'key__num_' . \(i</span><span style="color: #000000;">; </span><span style="color: #0000ff;">echo</span> <span style="color: #800080;">\)Residual->findServer(\(key</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> '&lt;br&gt;'<span style="color: #000000;">; } </span><span style="color: #800080;">\)Residual->removeServer(['host' => '10.16.134.65', 'port' => '11212']);
$Residual->getServerList();