1、memcached分布式简介

  memcached虽然称为“分布式”缓存服务器,但服务器端并没有“分布式”功能。Memcache集群主机不能够相互通信传输数据,它的“分布式”是基于客户端的程序逻辑算法进一步实现的。

请看下面简图:

根据上图我们简述分析分布式memcached的set与get的过程

set过程:

1、首先通过应用程序set(‘key’,’value’)

2、进入程序,使用key通过逻辑算法得出这个key需要存储的节点位置

3、根据节点位置连接相应的memcached服务器,并发送set命令

get过程:

1、首先通过应用程序get(‘key’)

2、接着使用该key通过逻辑算法获取该key的存储节点

3、根据节点连接相应的memcached服务器,并发送get命令

  实现memcached有很多种方式,其中最常用的就是一致哈希思想的分布式(就简称为一致哈希分布式啦)。好的东西当然需要次劣品来衬托它的优点啦,因此在这里除了讲解一致哈希分布式,还会讲到取模分布式。从而进一步分析他们的优缺点。

  这里的例子都会采用PHP代码实现,当然啦,最重要的是思想与方法嘛!毕竟这两样东西在任何语言中都是相通的。

2、取模算法方式

  何为取模算法方式分布式?就是将key转换为32位的数字,并与memcached服务器的总数进行相除取得余数。而这个余数就是memcached服务器的节点node。有了这个node我们就可以确定memcached服务器,就可以发送命令给memcached执行了。

图示解析:

整个过程上图所示。

1)、PHP代码实现

GetModMemcache.class.php

 <?php
#分布式memcache(取模计算)
class GetModMemcache
{
private $total=''; #存储memcache服务器的总数
private $servers=array(); #存储memcache服务器的具体信息
/**
* @desc 构造函数
*
* @param $serversArr array | memcache服务器具体信息
*/
public function __construct($serversArr)
{
$this->total=count($serversArr);
$this->servers=$serversArr;
} /**
* @desc 计算$key的存储位置(即哪个服务器)
*
* @param string | key字符串
*
* @return int 返回第几个服务器
*/
protected function position($key)
{
#使用crc32(),将字符串转化为32为的数字
return sprintf('%u',crc32($key))%$this->total; #取余
} /**
* @desc 获取memcached对象
*
* @param $position int | key的位置信息
*
* @return object 返回实例化memcached对象
*/
protected function getMemcached($position)
{
$host=$this->servers[$position]['host']; #服务器池中某台服务器host
$port=$this->servers[$position]['port']; #服务器池中某台服务器port
$m= new memcached();
$m->addserver($host, $port);
return $m;
} /**
* @desc 设置key-value值
*
* @param string | key字符串
* @param mixed | 值可以是任何有效的非资源型php类型
*
* @return 返回结果
*/
public function setKey($key, $value)
{
$num=$this->position($key);
echo $num; #调试用
$m=$this->getMemcached($num); #获取memcached对象
return $m->set($key, $value);
} public function getKey($key)
{
$num=$this->position($key);
$m=$this->getMemcached($num);
return $m->get($key);
} } $arr=array(
array('host'=>'192.168.95.11', 'port'=>'11210'),
array('host'=>'192.168.95.11', 'port'=>'11211'),
array('host'=>'192.168.95.11', 'port'=>'11212'),
);
$mod=new GetModMemcache($arr); /*
#存储数据
$a=$mod->setKey('key3', 'key33333');
echo "<pre>";
print_r($a);
echo "</pre>";die;
*/
/*
#获取数据
$b=$mod->getKey('key1');
echo "<pre>";
print_r($b);
echo "</pre>";die;
*/
?>

2)、进行相应测试

  1、连续插入三个数据

  #set(‘key1’,’value11111’);  #node=1

  #set(‘key2’,’value22222’);  #node=1

  #set(‘key3’,’value33333’;)  #node=0

  2、分别telnet连接192.168.95.11:(11210、11211、11212)

  11210含有key3数据

  11211含有key1、key2数据

  11212不含数据

  3、使用程序get数据

  结果都能够将数据取出来

3)、优缺点

  优点:

  1、简单实用易理解

  2、数据分布均匀

  缺点:

  1、宕了一台memcached服务器时不能自动调整群组去处理数据,使一部分数据不能使用缓存,一直持续从数据库中获取数据。

  2、当需要扩容的时候,增加多台memcached服务器,那么原来已经缓存的数据大多数都不能够被命中,即数据无用。

3、一致哈希算法方式

何为一致哈希算法方式分布式呢?

  想象一下,将32位的所有数字从小到大按顺时针分布在一个圆环上;

  其次,将每个存储节点赋予一个名字,并通过crc32函数将其转换为32位的数字,此数字就是该memcached服务器的存储节点

  接着,将key也通过crc32函数转换为32位的数字,它的所在位置按顺时针方向走第一个遇到的存储节点所对应的memcached服务器就是该key的最终存储服务器。

1)、图像解析

  假设node1节点服务器挂了,根据按顺时针最近原则,那么原本存储在node1节点的数据此时也可存储在node3节点中。

  假设有扩容的需要,增加的两台memcached服务器,又将会怎么样呢?请看下图分析

结果显示只有少量数据会受到影响,相对于整体数据来说这些影响还是在可接受的范围内。

  从上面的图示我们可以很容易发现存在这么个缺点,即是使用crc32函数我们不能控制memcached存储节点的具体位置,并且节点的总数量相对于2的32次方是显得多么的渺小。假若恰好即使这几个存储节点都距离的非常近呢,那么必将有一个memcached服务器承受绝大多数的数据缓存。

请看下图分析:

解决办法:

  将一个真实存储节点映射为多个虚拟存储节点,即真实节点+后缀再通过crc32处理(例如:node1_1、node1_2、node1_3、…..、node1_n)

看下图节点分布:

  三个真实节点在圆环上就变成了三十个存储节点,这样就可以避免存储节点相距太近而导致数据缓存分布不均匀的问题了,而且存储机制没有任何变化。

2)、PHP代码实现

ConsistentHashMemcache.class.php

 <?php
#分布式memcache 一致性哈希算法(采用环状数据结构)
class ConsistentHashMemcache
{
private $virtualNode=''; #用于存储虚拟节点个数
private $realNode=array(); #用于存储真实节点
private $servers=array(); #用于存储memcache服务器信息
#private $totalNode=array(); #节点总数
/**
* @desc 构造函数
*
* @param $servers array | memcache服务器的信息
* @param $virtualNode int | 虚拟节点个数,默认64个
*/
public function __construct($servers, $virtualNode=64)
{
$this->servers=$servers;
$this->realNode=array_keys($servers);
$this->virtualNode=$virtualNode;
} /**
* @return int 返回32位的数字
*/
private function hash($str)
{
return sprintf('%u',crc32($str)); #将字符串转换为32位的数字
} /**
* @desc 处理节点
*
* @param $realNode array | 真实节点
* @param $virturalNode int | 虚拟节点个数
*
* @return array 返回所有节点信息
*/
private function dealNode($realNode, $virtualNode)
{
$totalNode=array();
foreach ($realNode as $v)
{
for($i=0; $i<$virtualNode; $i++)
{
$hashNode=$this->hash($v.'-'.$i);
$totalNode[$hashNode]=$v;
}
}
ksort($totalNode); #按照索引进行排序,升序
return $totalNode;
} /**
* @desc 获取key的真实存储节点
*
* @param $key string | key字符串
*
* @return string 返回真实节点
*/
private function getNode($key)
{
$totalNode=$this->dealNode($this->realNode, $this->virtualNode); #获取所有虚拟节点
/* #查看虚拟节点总数
echo "<pre>";
print_r($totalNode);
echo "</pre>";die;
*/
$hashNode=$this->hash($key); #key的哈希节点
foreach ($totalNode as $k => $v) #循环总结点环查找
{
if($k >= $hashNode) #查找第一个大于key哈希节点的值
{
return $v; #返回真实节点
}
}
return reset($totalNode); #假若总节点环的值都比key哈希节点小,则返回第一个总哈希环的value值
} /**
* @desc 返回memcached对象
*
* @param $key string | key值
*
* @return object
*/
private function getMemcached($key)
{
$node=$this->getNode($key); #获取真实节点
echo $key.'真实节点:'.$node.'<br/>'; #测试使用,查看key的真实节点
$host=$this->servers[$node]['host']; #服务器池中某台服务器host
$port=$this->servers[$node]['port']; #服务器池中某台服务器port
$m= new memcached(); #实例化
$m->addserver($host, $port); #添加memcache服务器
return $m; #返回memcached对象
} /**
* @desc 设置key-value值
*/
public function setKey($key, $value)
{
$m=$this->getMemcached($key);
return $m->set($key, $value);
} /**
* @desc 获取key中的value
*/
public function getKey($key)
{
$m=$this->getMemcached($key);
return $m->get($key);
} } ?>

3)、测试

1、查看所有虚拟节点

一共64*3=132个虚拟节点(虚拟节点设置还是属于偏少的,一般都会设置在100~200)

2、set测试

 include './ConsistentHashMemcache.class.php';
header("content-type: text/html;charset=utf8;");
$arr=array(
'node1'=>array('host'=>'192.168.95.11', 'port'=>'11210'),
'node2'=>array('host'=>'192.168.95.11', 'port'=>'11211'),
'node3'=>array('host'=>'192.168.95.11', 'port'=>'11212'),
); $c=new ConsistentHashMemcache($arr); #测试set
$c->setKey('aaa', '11111');
$c->setKey('bbb', '22222');
$c->setKey('ccc', '33333');

分别telnet连接192.168.95.11:(11210、11211、11212)

在节点node1中get(‘aaa’)、get(‘bbb’)能取到值

在节点node3中get(‘ccc’)能取到值

3、get测试

 include './ConsistentHashMemcache.class.php';
header("content-type: text/html;charset=utf8;");
$arr=array(
'node1'=>array('host'=>'192.168.95.11', 'port'=>'11210'),
'node2'=>array('host'=>'192.168.95.11', 'port'=>'11211'),
'node3'=>array('host'=>'192.168.95.11', 'port'=>'11212'),
); $c=new ConsistentHashMemcache($arr);
#测试get
echo $c->getKey('aaa').'<br/>';
echo $c->getKey('bbb').'<br/>';
echo $c->getKey('ccc').'<br/>';

4、优缺点

  相对于取模方式分布式,一致性哈希方式分布式的代码复杂性要高一点,但这也在可以接受的范围内,不构成任何阻碍问题。相反它的优点就非常显著,通过虚拟节点的方式实现,可以使不可控的存储节点能够尽可能的均匀分布在圆环上,从而达到数据均匀缓存在各个主机里。其次增加与删除虚拟节点对于之前缓存的整体数据影响非常小。

(以上是自己的一些见解与总结,若有不足或者错误的地方请各位指出)

作者:那一叶随风

声明:以上只代表本人在工作学习中某一时间内总结的观点或结论。转载时请在文章页面明显位置给出原文链接

memcached分布式缓存的更多相关文章

  1. Memcached分布式缓存初体验

    1 Memcached简介/下载/安装 Memcached是一个高性能的不是内存对象缓存系统,用于动态Web应用以减轻数据库负载.Memcached基于一个存储键/值对的HashMap.其客户端可以使 ...

  2. Memcached 分布式缓存实现原理

    摘要 在高并发环境下,大量的读.写请求涌向数据库,此时磁盘IO将成为瓶颈,从而导致过高的响应延迟,因此缓存应运而生.无论是单机缓存还是分布式缓存都有其适应场景和优缺点,当今存在的缓存产品也是数不胜数, ...

  3. 缓存应用--Memcached分布式缓存简介

    一.   什么是Memcached Memcached 是一个高性能的分布式内存 对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象 来减少读取数据库的次数,从而提供动态. ...

  4. Discuz!NT中集成Memcached分布式缓存

    大约在两年前我写过一篇关于Discuz!NT缓存架构的文章,在那篇文章的结尾介绍了在IIS中如果开启多个应用程序池会造成多个缓存实例之间数据同步的问题.虽然给出了一个解决方案,但无形中却把压力转移到了 ...

  5. Memcached 分布式缓存系统部署与调试

    Memcache 分布式缓存系统部署与调试 工作机制:通过在内存中开辟一块区域来维持一个大的hash表来加快页面访问速度,和数据库是独立的;目前主要用来缓存数据库的数据;存放在内存的数据通过LRU算法 ...

  6. memcached分布式缓存系统

    在数据驱动的Web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵活.此时 ...

  7. Memcached 分布式缓存实现原理简介

    摘要 在高并发环境下,大量的读.写请求涌向数据库,此时磁盘IO将成为瓶颈,从而导致过高的响应延迟,因此缓存应运而生.无论是单机缓存还是分布式缓存都有其适应场景和优缺点,当今存在的缓存产品也是数不胜数, ...

  8. [Memcached]分布式缓存系统Memcached在Asp.net下的应用

    Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached ...

  9. Memcached分布式缓存快速入门

    一.从单机到分布式 走向分布式第一步就是解决:多台机器共享登录信息的问题. •例如:现在有三台机器组成了一个Web的应用集群,其中一台机器用户登录,然后其他另外两台机器共享登录状态? •解决1:Asp ...

随机推荐

  1. 【2017-02-21】分支语句if...else...、分支嵌套、变量的作用域

    语句是指程序命令,都是按照顺序执行的. 语句又分为: 顺序语句:从上到下按顺序执行,挨个执行一遍. 分支语句:选择性执行语句,有的可能会执行,有的可能不执行.满足条件执行. 循环语句: 一.分支语句 ...

  2. MES项目参观交流会

    2016年11月10日,普实软件组织了河南蔚林.江苏正恺.吴通控股.上海锐拓等单位的26位企业家代表,走进科兴电器,开展企业家现场交流活动.企业家们参观了科兴花园式数字化工厂.感受了大数据中心的强大功 ...

  3. JNI只C性能测试

    深入学习JNI之前,介绍一个比较好的学习文档:jni详细教程.我这里只是一个Demo测试性能,至于入门教程请看我前一篇博文. Demo展示 这个Demo用于从小到大的冒泡排序,App.java代码: ...

  4. 关于Response.redirect和Response.End出现线程中止异常的处理

    最近做了一个项目其中使用了多线程获取POST过来的数据后再Response回复,但由于是多线程,在Response.End()的时候报出了异常: 2013-10-20 10:05:31,606 res ...

  5. Selenium 使用css selector (资源来源于网络)

    Selenium - CSS Selector 昨天我练习了用CSS(即层叠样式表Cascading Stylesheet) Selector来定位(locate)页面上的元素(Elements).S ...

  6. visibility: hidden和 display: none的区别

    visibility: hidden----将元素隐藏,但是在网页中该占的位置还是占着. display: none----将元素的显示设为无,即在网页中不占任何的位置.

  7. Prince2是怎么考试的?想了解P2

    自己在项目管理培训的行业已经有了5年的时间,经历了很多的学员和企业,和他们交流的问题,话题也很多. 在前几年,对于项目经理来讲关注的很多是单项目管理的工具技术模板,谈论最多的是,进度延期,成本超支,范 ...

  8. 模仿Wireshark网络抓包工具实现---c++

    最近在用Wireshark抓包工具的时候,老感觉这东西用起来很简单,功能强大,所以想了解他的实现原理,我就自己好奇写了一个实现基本功能的demo吧. 其实叫抓包工具,其实就是抓取流经自己网卡的所有ip ...

  9. 1774: [Usaco2009 Dec]Toll 过路费

    1774: [Usaco2009 Dec]Toll 过路费 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 263  Solved: 154[Submit ...

  10. 1615: [Usaco2008 Mar]The Loathesome Hay Baler麻烦的干草打包机

    1615: [Usaco2008 Mar]The Loathesome Hay Baler麻烦的干草打包机 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit:  ...