memcached 分布式聚类算法
memcached 分布式集群,该决定必须书面开发商自己。和redis 由分布式server决定。上 memcached 有两个选项用于分布式。第一个是:模运算
另一种是:一致性hash 分布式算法。以下我就这两种算法简介一下。
一、取模算法:
取模算法是不靠谱的算法,当有n 台server,突然一台server突然down 掉了。memcached 的命中率仅仅有1/n-1。结果是怎么来的,自己想想吧。比方有8台memchached
server,0,1,2,3。4,5。6,7,8 这8个数字存入到memcached server,突然有一台memcached serverdown 掉了。
我们用取模法,从memcached server取值。
发现仅仅有0。1,2。3,4,5,6。56 这些数被7,8取模的值是一样的。也就是说这么写能被命中。
也就得出了结论 n/n(n-1) 个数可以被命中。相同也得出结论server集群越大,
命中率也就越低。
这中算法直接pass 掉。
并且命中率会无限趋进于0。
二、一致性hash 分布式算法:
介绍这样的算法。首先我引进一个概念是虚拟节点。虚拟节点也就是说在没台memcached server虚拟非常多节点。并算出每一个节点的hash 值。
存key 时,同一时候计算出key 的hash
值,当key的hash 值,接近或等于某个节点的hash 值,就放到某个 memcached server。当某个memcached serverdown 掉后。其它server共同承担这台server的压力。
以下是我的一段代码:
<?php /* memcached 一致性hash分布式算法 */ interface hash{ public function _hash($str); // hash 算法 } interface distribution{ public function _lookup($key); } class contents implements hash,distribution{ public $address_array=array(); public function _hash($str){ return sprintf("%u",crc32($str)); } public function _lookup($key){ //通过key 找到server $look_key=$this->_hash($key); $current_key=current($this->address_array); foreach($this->address_array as $key=>$value){ if($look_key<=$key){ $current_key=$key; break; } } return $value; } public function _address($node){ // 虚拟节点 for($i=0;$i<64;$i++){ $key=$this->_hash($node.'_'.$i); $this->address_array[$key]=$node; } $this->_sortkey(); } public function _sortkey(){ // 对hash 后的值排序 ksort($this->address_array,SORT_REGULAR); } public function delnode($node){ // down 掉了某台server $new_address=array_diff_key($this->address_array,array_flip(array_keys($this->address_array,$node))); $this->address_array=$new_address; return $new_address; } }
以下另一段模拟降低一台memcached server的代码:
<?php require_once 'config.php'; //载入配置文件 require_once 'distribute.php'; //载入一致hash $distribute=new contents(); //循环加入server foreach($mem_array as $key=>$value){ $distribute->_address($key); } //降低一台server $distribute->delnode('B'); for($i=0;$i<10000;$i++){ $key='key'.sprintf("%04d",$i); $memca_key=$distribute->_lookup($key); $memca_party=$mem_array[$memca_key]; $memcache = memcache_connect($memca_party['ip'],$memca_party['port']); if(!$memcache->get($key)){ $memcache->add($key,'value'.$i,0,0); } $memcache->close(); }
以下是memcached server的配置文件
/* memcached 的配置文件 */ $mem_array=array( 'A'=>array('ip'=>'127.0.0.1','port'=>'11211'), 'B'=>array('ip'=>'127.0.0.1','port'=>'11212'), 'C'=>array('ip'=>'127.0.0.1','port'=>'11213'), 'D'=>array('ip'=>'127.0.0.1','port'=>'11214') );
以下是计算memcached 的命中率:
<?php require_once 'config.php'; //载入配置文件 $int_get=0; $int_hits=0; $rand=1; foreach($mem_array as $value){ $memcache = memcache_connect($value['ip'],$value['port']); $statistics =$memcache->getStats(); $int_get += $statistics['cmd_get']; $int_hits += $statistics['get_hits']; } if($int_get==0){ echo $rand; return true; } $rand=$int_hits/$int_get; echo $rand;
以下是highcharts 插件统计的 memcached 命中率
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Highcharts Example</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <style type="text/css"> ${demo.css} </style> <script type="text/javascript"> $(function () { $(document).ready(function () { Highcharts.setOptions({ global: { useUTC: false } }); $('#container').highcharts({ chart: { type: 'spline', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function () { // set up the updating of the chart each second var series = this.series[0]; setInterval(function () { var x = (new Date()).getTime(), // current time y = parseFloat($.ajax({url:'hit.php',async:false}).responseText); series.addPoint([x, y], true, true); }, 1000); } } }, title: { text: 'memcached 命中率' }, xAxis: { type: 'datetime', tickPixelInterval: 150 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { formatter: function () { return '<b>' + this.series.name + '</b><br/>' + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 2); } }, legend: { enabled: false }, exporting: { enabled: false }, series: [{ name: 'Random data', data: (function () { // generate an array of random data var data = [], time = (new Date()).getTime(), i; for (i = -19; i <= 0; i += 1) { data.push({ x: time + i * 1000, y: Math.random() }); } return data; }()) }] }); }); }); </script> </head> <body> <script src="js/highcharts.js"></script> <script src="js/modules/exporting.js"></script> <div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div> </body> </html>