一致性哈希虚节点解决雪崩问题

一致性哈希的基本原理大家都知道,就是找一个最近的节点或者顺时针,逆时针找一个节点,而不是通过模一个N去找节点,这样做的好处就是当其中一台磬机的时候,一致性哈希还可以继续工作,而模N的方法,所有定位在磬机节点上都会失败。如下代码是一个简单的一致性哈希:

<?php
class Redis {
    
    //redis实例的id
    public $id;

    public function __construct($id) {
        $this->id = $id;
    }

    public function set($key, $value) {
        echo 'redis '.$this->id.': set key '. $key.'</br>';
    }

    public function ok() {
        return true;
    }
}

class RedisGlobal {

    public $redis = array();

    public function addServer($redis) {
        $this->redis[md5($redis->id)] = $redis;
    }

    public function set($key, $value) {
        $hash = md5($key);
        $serverFind = $hash;
        ksort($this->redis, SORT_REGULAR);
        foreach ($this->redis as $serverIndex=>$redis) {
            if ($serverIndex > $serverFind) {
                //找到一个server节点,检测该节点上redis是否可用
                if ($redis->ok()) {
                    $redis->set($key, $value);
                    break;
                }
            }
        }
    }

}

$global = new RedisGlobal();
$global->addServer(new Redis(1));
$global->addServer(new Redis(2));
$global->addServer(new Redis(3));
for ($i=0; $i<10; $i++) {
    $global->set($i, $i);
}
?>

//output
redis 3: set key 0
redis 2: set key 1
redis 3: set key 2
redis 1: set key 4
redis 3: set key 5
redis 1: set key 6
redis 1: set key 7
redis 3: set key 8
redis 1: set key 9

以上代码基本OK,不过上面的代码主要有三个问题,也是一致性哈希基本思想存在的问题。面试中也经常考察一个方案的弊端。

1.分布不均匀。

2.当一台磬机以后,磬机节点的所有请求会落到下一台主机,这样就有可能使下一台主机也磬机,这就是雪崩问题。

3.不同主机处理能力不同如何配置量。

解决以上两个问题,是通过配虚节点,也就是一个主机映射N个节点,下面的例子中默认映射100个节点,处理能力较强的主机可以配200或者更高的虚节点数量。

<?php
class Redis {
    
    //redis实例的id
    public $id;

    public function __construct($id) {
        $this->id = $id;
    }

    public function set($key, $value) {
        echo 'redis '.$this->id.': set key '. $key.'</br>';
    }

    public function ok() {
        return true;
    }
}

class RedisGlobal {

    public $redis = array();

    public function addServer($redis, $weight=100) {
        for ($i=0; $i<$weight; $i++) {
            $this->redis[md5($redis->id.'-'.$i)] = $redis;
        }
    }

    public function set($key, $value) {
        $hash = md5($key);
        $serverFind = $hash;
        ksort($this->redis, SORT_REGULAR);
        foreach ($this->redis as $serverIndex=>$redis) {
            if ($serverIndex > $serverFind) {
                //找到一个server节点,检测该节点上redis是否可用
                if ($redis->ok()) {
                    $redis->set($key, $value);
                    break;
                }
            }
        }
    }

}

$global = new RedisGlobal();
$global->addServer(new Redis(1));
$global->addServer(new Redis(2));
$global->addServer(new Redis(3));
for ($i=0; $i<10; $i++) {
    $global->set($i, $i);
}
?>

//output
redis 1: set key 0
redis 1: set key 1
redis 3: set key 2
redis 1: set key 3
redis 2: set key 4
redis 1: set key 5
redis 2: set key 6
redis 2: set key 7
redis 3: set key 8
redis 1: set key 9

 

posted @ 2014-03-08 20:41  23lalala  阅读(1543)  评论(0编辑  收藏  举报