最容易想到的算法是取模算法,即 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>->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>-><span style="color: #000000;">nodeS);
</span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$nodeSCount</span> > 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>->nodeS[] = <span style="color: #800080;">$node</span><span style="color: #000000;">;
</span><span style="color: #800080;">$this</span>->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>->nodeS <span style="color: #0000ff;">as</span> <span style="color: #800080;">$k</span> => <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>->nodeS[<span style="color: #800080;">$k</span><span style="color: #000000;">]);
</span><span style="color: #800080;">$this</span>->nodeNum -= 1<span style="color: #000000;">;
</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;
}
}
</span><span style="color: #800080;">$this</span>->nodeS = <span style="color: #008080;">array_merge</span>(<span style="color: #800080;">$this</span>-><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>-><span style="color: #000000;">nodeS);
</span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$this</span>-><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>->addServer(['host' => '10.16.134.65', 'port' => '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> '<br>'<span style="color: #000000;">;
}
</span><span style="color: #800080;">\)Residual->removeServer(['host' => '10.16.134.65', 'port' => '11212']);
$Residual->getServerList();