<h2 ID="memcached介绍">Memcached介绍
<h3 ID="memcached是什么">Memcached是什么?
Free & open source,high-performance,distributed memory object caching system(自由&开放源码,高性能,分布式的内存对象缓存系统)。由liveJournal旗下的danga公司开发的老牌nosql应用。
Nosql,指的是非关系型的数据库。相对于传统关系型数据库的"行与列",Nosql的鲜明特点为key-value存储(memcache,redis),或基于文档存储(mongodb)。注:nosql --not only sql,不仅仅是关系型数据库
再linux下编译,需要gcc,make,cmake,autoconf,libtool等工具,这几件工具,以后还要编译redis等使用,所以需要先安装。在linux系统联网后,用如下命令安装
yum install gcc gcc-c++ make cmake autoconf libtool
Memcached依赖于libevent库,因此我们需要先安装libevent。分别到libevent.org和memcached.org下载最新的stable版本(稳定版)。先编译libevent,再编译memcached。编译Memcached时要指定libevent的路径。过程如下:假设源码在/root/package下,安装在/usr/local下
tar zxvf libevent-2.1.8-stable.tar.gzcd libevent-2.1.8-stable./configure --prefix=/usr/local/libeventmake && make install安装memcachedtar zxvf memcached-1.5.1.tar.gzcd memcached-1.5.1./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent/make && make install配置环境变量
vi /etc/profile
export PATH="$PATH:/usr/local/memcached/bin"
source /etc/profile
创建memcached用户
useradd memcached
设置开机自动启动:
vi /etc/rc.local
增加
/usr/local/memcached/bin/memcached -u memcached -m 64 &
<h4 ID="PHP安装memcached扩展">PHP安装Memcached扩展
http://pecl.PHP.net/package/memcache下载扩展包wget https://pecl.PHP.net/get/memcache-2.2.7.tgztar zxvf memcache-2.2.7.tgzcd memcache-2.2.7PHPize //执行PHPize命令,PHPize是PHP的工具,用来将PHP的扩展与PHP程序建立关联
配置编译安装
./configure && make && make install
修改PHP.ini
vi /usr/local/PHP/lib/PHP.ini
在大约928行左右加上扩展配置
;linux extension load
extension=memcache.so
重启Apache
apachectl -k restart
在PHPinfo里可以查找到memcache说明安装成功
<h3 ID="memcached的启动">Memcached的启动
memcached -m 64 -p 11211 -u nobody -d //-d表示后台运行(也可以用&)
可以使用memcached -h查看帮助来了解各个参数的意义。
$memcache = new Memcache();$memcache->connect("192.168.20.131",11211);bool Memcache::set (string $key,mixed $var [,int $flag [,int $expire ]])
$key:要设置值的key
$var:要存储的值,字符串和数值直接存储,其他类型序列化后存储。数据的最大长度为1M。
$flag: 使用MEMCACHE_COMpressed指定对值进行压缩(使用zlib)。通常传入0即可,表示不需要压缩。
$expire:当前写入缓存的数据的失效时间。如果此值设置为0表明此数据永不过期。当时间小于30天时表示的是时间间隔,当时间大于30天表示时间戳bool Memcache::add (string $key,int $expire ]])
说明:与set类似,仅仅可以执行添加 *** 作,不能执行修改 *** 作,当key已经存在时,则add失败bool Memcache::replace (string $key,int $expire ]])
说明:与set类似,仅仅可以执行替换 *** 作,仅仅在key存在时才可以执行,当key不存在时,替换是失败的get($key [,$flag]) --- 获取
获取时,有时需要设置第二个参数,flag标志!
比如如果存储时设置了第二个参数为压缩存储,那么获取时也需要传递压缩存储的参数。increment($key,$num) --- 递增
在原有值得基础上增加,第二个参数不写默认是1decrement($key,$num) --- 递减
在原有值得基础减少,delete($key) --- 删除
fulsh() --- 清空/刷新
close() --- 关闭连接
PHP
如果用 c 语言直接 malloc,free 来向 *** 作系统申请和释放内存时,在不断的申请和释放过程中,形成了一些很小的内存片断,无法再利用。这种空闲,但无法利用内存的现象,--称为内存的碎片化。
Memcached 用 slab allocator 机制来管理内存。 Slab allocator 原理:预告把内存划分成数个 slab cl ass 仓库。(每个 slab class 大小 1 M)各仓库,切分成不同尺时的小块(chunk).(图 3.2) 需要存内容时,判断内容的大小,为其选取合理的仓库.
Memcached 根据收到的数据的大小,选择最适合数据大小的 chunk 组(slab class)(图 3.3)。memcached 中保存着 slab class 内空闲 chunk 的列表,根据该列表选择空的 chunk,然后将数据缓存于其中。
由于 slab allocator 机制中,分配的 chunk 的大小是”固定”的,因此,对于特定的 item,可能造成内存空间的浪费。比如,将 100 字节的数据缓存到 122 字节的 chunk 中,剩余的 22 字节就浪费了图 3.4)
Memcached 在启动时可以通过- f 选项指定 Growth Factor 因子,并在某种程度上控制 slab 之间的差异。默认值为 1.25. 但是,在该选项出现之前,这个因子曾经固定为 2,称为”powers of2”策略。
1: 当某个值过期后,并没有从内存删除,因此,stats 统计时,curr_item 有其信息2: 当某个新值去占用他的位置时,当成空 chunk 来占用.3: 当 get 值时,判断是否过期,如果过期,返回空,并且清空,curr_item 就减少了.这个过期,只是让用户看不到这个数据而已,并没有在过期的瞬间立即从内存删除. 这个称为 lazy expiration,惰性失效.好处:节省了cpu时间和检测的成本
如果以 122byte 大小的 chunk 举例,122 的 chunk 都满了,又有新的值(长度为 120)要加入,要 挤掉谁?memcached 此处用的 lru 删除机制.( *** 作系统的内存管理,常用 fifo,lru 删除)lru: least recently used 最近最少使用fifo: first in,first out 原理:当某个单元被请求时,维护一个计数器,通过计数器来判断最近谁最少被使用. 就把谁t出.注意:即使某个key是设置的永久有效期,也一样会被踢出来!即-永久数据被踢现象
key 的长度: 250 字节,(二进制协议支持 65536 个字节)value 的限制: 1m,一般都是存储一些文本,如新闻列表等等,这个值足够了. 内存的限制: 32 位下最大设置到 2G.如果有 30g 数据要缓存,一般也不会单实例装 30G,(不要把鸡蛋装在一个篮子里),一般建议 开启多个实例(可以在不同的机器,或同台机器上的不同端口)
Memcached并不像MongoDB那样,允许配置多个节点,且节点之间"自动分配数据"。也就是说,Memcached节点之间是不互相通信的因此,Memcached的分布式,要靠用户去设计算法,把数据分布在多个Memcached节点中。
代码:
interface hasher { public function hash($str);}interface distribution {
public function lookup($key);
}/**
Class Moder
求余算法
*/
class Moder implements hasher,distribution {
protected $server = array();
protected $num = 0;//计算一个字符串对应的32 位循环冗余校验码多项式
public function hash($str) {
return sprintf("%u",crc32($str));
}//查询数据应存放的节点服务器
public function lookup($key) {
$index = $this->hash($key) % $this->num;
return $this->server[$index];
}//模拟增加一台Memcached服务器
public function addNode($s) {
$this->server[] = $s;
$this->num++;
}//模拟Memcached服务器宕机
public function delNode($s) {
foreach ($this->server as $key => $value) {
if($s == $value) {
unset($this->server[$key]);
}
}
$this->num--;
$this->server = array_merge($this->server); //重新整理server的键,使其按照0->1->2递增
}
}$moder = new Moder();
$moder->addNode('a');
$moder->addNode('b');
$moder->addNode('c');
$moder->addNode('d');for($i = 0; $i < 100; $i++) {
$key = 'key_'.$i;
echo $key,'---->',$moder->lookup($key),'
';
}
<h3 ID="一致性哈希算法">一致性哈希算法
通俗理解一致性哈希:把各服务器节点映射放在钟表的各个时刻上,把key也映射到钟表的某个时刻上,该key沿着钟表顺时针走,碰到的第一个节点即为该key的存储节点。如下图所示:
一致性哈希+虚拟节点算法代码
interface hasher { public function hash($str);}interface distribution {
public function lookup($key);
}class Consistency implements hasher,distribution {
protected $nodes = array();
protected $points = array();
protected $multi = 64; //每个Memcached服务器对应的虚拟节点数量//计算<a href="https://www.jb51.cc/tag/yige/" target="_blank" >一个</a>字符串对应的32 位循环冗余校验码多项式pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c function hash($str) { return sprintf("%u",crc32($str));}//<a href="https://www.jb51.cc/tag/chaxun/" target="_blank" >查询</a>数据应存放的节点<a href="https://m.jb51.cc/tag/fuwuqi/" target="_blank" >服务器</a>pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c function lookup($key) { $<a href="https://m.jb51.cc/tag/position/" target="_blank" >position</a> = $this->hash($key); <a href="https://m.jb51.cc/tag/reset/" target="_blank" >reset</a>($this->points); //重置指针(因为foreach遍历时会数组指针后移,等到下次遍历数组时,key()<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" >方法</a><a href="https://www.jb51.cc/tag/huoqu/" target="_blank" >获取</a>的就不是第<a href="https://www.jb51.cc/tag/yige/" target="_blank" >一个</a>元素的键了) $needle = key($this->points); //<a href="https://www.jb51.cc/tag/mo/" target="_blank" >默</a>认会落在第<a href="https://www.jb51.cc/tag/yige/" target="_blank" >一个</a>节点 foreach ($this->points as $key => $value) { if($<a href="https://m.jb51.cc/tag/position/" target="_blank" >position</a> <= $key) { $needle = $key; break; } } return $this->points[$needle];}//<a href="https://www.jb51.cc/tag/tianjia/" target="_blank" >添加</a>节点pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c function addNode($node) { $this->nodes[$node] = array(); for($i = 0; $i < $this->m<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>ti; $i++) { $point = $node . '_' . $i; $point = $this->hash($point); //虚拟节点转为数字 $this->points[$point] = $node; $this->nodes[$node][] = $point; $this->resort(); }}pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c function delNode($node) { foreach ($this->nodes[$node] as $p) { //清除64个虚拟节点 unset($this->points[$p]); } unset($this->nodes[$node]); //去掉节点}//对虚拟几点<a href="https://m.jb51.cc/tag/jinxing/" target="_blank" >进行</a><a href="https://m.jb51.cc/tag/paixu/" target="_blank" >排序</a>protected function resort() { ksort($this->points);}
}
$consistency = new Consistency();
$consistency->addNode('A');
$consistency->addNode('B');
$consistency->addNode('C');
$consistency->addNode('D');
echo $consistency->hash('name');
echo $consistency->lookup('name');
<h2 ID="并发处理-乐观锁">并发处理-乐观锁
在新版的memcached中,增加了对并发的控制,处理方案是:乐观锁并发:多个进程(连接),同时 *** 作一个key. 就是并发 *** 作.
乐观锁:进程A,先 *** 作了缓存项 在进程A第二次 *** 作缓存项前,进程B *** 作了缓存项.之后,进程A第二次 *** 作缓存项. 检查,在进程A第一次 *** 作后,是否有其他进程 *** 作过需要的缓存项. 如果有,则放弃第二次 *** 作. 采取乐观的处理态度.(乐观锁定)
支持乐观锁的 *** 作: gets() 获取cas() 设置注意:Memcache扩展还不支持这两个 *** 作,在telnet上可以演示
缓存雪崩一般是由某个缓存节点失效,导致其他节点的缓存命中率下降,缓存中缺失的数据去数据库查询。短时间内,造成数据库服务器崩溃。重启DB,短期又被压垮,但缓存数据也多一些。DB反复多次启动,缓存重建完毕,DB才稳定运行。或者,是由于缓存周期性的失效,比如每6个小时失效一次,那么每6小时,将有一个请求“峰值”,严重者甚至会令DB崩溃。解决办法/方案:把缓存设置为随机3-9个小时的生命周期,这样不同时失效,把工作分担到各个时间点。
网上有人反馈为"memcached 数据丢失",明明设为永久有效,却莫名其妙的丢失了.其实,这要从 2 个方面来找原因: 即前面介绍的 惰性删除,与 LRU 最近最少使用记录删除. 分析(如下图) 1:如果 slab 里的很多 chunk,已经过期,但过期后没有被 get 过,系统不知他们已经过期. 2:永久数据很久没 get 了,不活跃,如果新增 item,则永久数据被踢了.3: 当然,如果那些非永久数据被 get,也会被标识为 expire,从而不会再踢掉永久数据
总结以上是内存溢出为你收集整理的Memcached详解全部内容,希望文章能够帮你解决Memcached详解所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)