Memcached详解
Memcached介绍
Memcached是什么?
Free & open source, high-performance, distributed memory object caching system(自由&开放源码,高性能,分布式的内存对象缓存系统)。由LiveJournal旗下的danga公司开发的老牌nosql应用。
什么是NoSQL?
NoSQL,指的是非关系型的数据库。
相对于传统关系型数据库的"行与列",NoSQL的鲜明特点为key-value存储(memcache,redis),或基于文档存储(mongodb)。
注:nosql --not only sql,不仅仅是关系型数据库
Memcached安装
Linux下编译Memcached
准备编译环境
再Linux下编译,需要gcc,make,cmake,autoconf,libtool等工具,这几件工具,以后还要编译redis等使用,所以需要先安装。在Linux系统联网后,用如下命令安装
yum install gcc gcc-c++ make cmake autoconf libtool
编译Memcached
Memcached依赖于libevent库,因此我们需要先安装libevent。分别到libevent.org和memcached.org下载最新的stable版本(稳定版)。
先编译libevent,再编译memcached。
编译Memcached时要指定libevent的路径。
过程如下:假设源码在/root/package下,安装在/usr/local下
tar zxvf libevent-2.1.8-stable.tar.gz
cd libevent-2.1.8-stable
./configure --prefix=/usr/local/libevent
make && make install
安装memcached
tar zxvf memcached-1.5.1.tar.gz
cd 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 &
PHP安装Memcached扩展
http://pecl.php.net/package/memcache下载扩展包
wget https://pecl.php.net/get/memcache-2.2.7.tgz
tar zxvf memcache-2.2.7.tgz
cd memcache-2.2.7
phpize //执行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说明安装成功
Memcached的启动
memcached -m 64 -p 11211 -u nobody -d //-d表示后台运行(也可以用&)
可以使用memcached -h查看帮助来了解各个参数的意义。
Memcached基本使用
PHP操作Memcache
方法 | 方法说明 |
---|---|
connect() | 打开一个memcached服务端连接 |
add() | 增加一个条目到缓存服务器 |
addServer() | 向连接池中添加一个memcache服务器 |
increment() | 增加一个元素的值 |
decrement() | 减小一个元素的值 |
delete() | 从服务端删除一个元素 |
flush() | 清洗(删除)已经存储的所有的元素 |
get() | 从服务端检回一个元素 |
set() | 保存数据到缓存服务器 |
replace() | 替换已经存在的元素的值 |
pconnect() | 打开一个到服务器的持久化连接 |
close() | 关闭memcache连接 |
PHP连接memcache服务
$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 , mixed $var [, int $flag [, int $expire ]])
说明:与set类似,仅仅可以执行添加操作,不能执行修改操作,当key已经存在时,则add失败
bool Memcache::replace (string $key , mixed $var [, int $flag [, int $expire ]])
说明:与set类似,仅仅可以执行替换操作,仅仅在key存在时才可以执行,当key不存在时,替换是失败的
get($key [, $flag]) --- 获取
获取时, 有时需要设置第二个参数, flag标志!
比如如果存储时设置了第二个参数为压缩存储,那么获取时也需要传递压缩存储的参数。
increment($key, $num) --- 递增
在原有值得基础上增加,第二个参数不写默认是1
decrement($key, $num) --- 递减
在原有值得基础减少,
delete($key) --- 删除
fulsh() --- 清空/刷新
close() --- 关闭连接
PHP
Memcached的内存管理与删除机制
内存的碎片化
如果用 c 语言直接 malloc, free 来向操作系统申请和释放内存时,在不断的申请和释放过程中,形成了一些很小的内存片断,无法再利用。这种空闲,但无法利用内存的现象,--称为内存的碎片化。
slab allocator缓解内存碎片化
Memcached 用 slab allocator 机制来管理内存。
Slab allocator 原理:预告把内存划分成数个 slab cl ass 仓库。(每个 slab class 大小 1 M)各仓库,切分成不同尺时的小块(chunk).(图 3.2)
需要存内容时,判断内容的大小,为其选取合理的仓库.
系统如何选择合适的chunk?
Memcached 根据收到的数据的大小,选择最适合数据大小的 chunk 组(slab class)(图 3.3)。memcached 中保存着 slab class 内空闲 chunk 的列表,根据该列表选择空的 chunk,然后将数据缓存于其中。
警告:
如果有100byte的内存要存,但122大小的仓库的chunk满了
并不会寻找更大的,如144的仓库来存储,
而是把122仓库的旧数据踢掉!详见过期与删除机制
固定大小的chunk带来的内存浪费
由于 slab allocator 机制中,分配的 chunk 的大小是”固定”的,因此,对于特定的 item,可能造成内存空间的浪费。
比如,将 100 字节的数据缓存到 122 字节的 chunk 中,剩余的 22 字节就浪费了图 3.4)
对于 chunk 空间的浪费问题,无法彻底解决,只能缓解该问题。
开发者可以对网站中缓存中的 item 的长度进行统计,并制定合理的 slab class 中的 chunk 的大小。
可惜的是,我们目前还不能自定义 chunk 的大小,但可以通过参数来调整各 slab class 中 chunk 大小的增长速度。即增长因子,grow factor!
growfactor调优
Memcached 在启动时可以通过- f 选项指定 Growth Factor 因子,并在某种程度上控制 slab 之间的差异。默认值为 1.25. 但是,在该选项出现之前,这个因子曾经固定为 2, 称为”powers of2”策略。
对比可知,当 f=2 时,各 slab 中的 chunk size 增长很快,有些情况下就相当浪费内存。因此,我们应细心统计缓存的大小,制定合理的增长因子。
注意:
当 f=1.25 时,从输出结果来看,某些相邻的 slab class 的大小比值并非为 1.25,可能会觉得有些 计算误差,这些误差是为了保持字节数的对齐而故意设置的.
memcached的过期数据惰性删除
1: 当某个值过期后,并没有从内存删除, 因此,stats 统计时, curr_item 有其信息
2: 当某个新值去占用他的位置时,当成空 chunk 来占用.
3: 当 get 值时,判断是否过期,如果过期,返回空,并且清空, curr_item 就减少了.
这个过期,只是让用户看不到这个数据而已,并没有在过期的瞬间立即从内存删除. 这个称为 lazy expiration, 惰性失效.
好处:节省了CPU时间和检测的成本
memcached的LRU删除机制
如果以 122byte 大小的 chunk 举例, 122 的 chunk 都满了, 又有新的值(长度为 120)要加入, 要 挤掉谁?
memcached 此处用的 lru 删除机制.
(操作系统的内存管理,常用 fifo,lru 删除)
lru: least recently used 最近最少使用
fifo: first in ,first out
原理:当某个单元被请求时,维护一个计数器,通过计数器来判断最近谁最少被使用. 就把谁t出.
注意:即使某个key是设置的永久有效期,也一样会被踢出来!即-永久数据被踢现象
Memcached的一些参数限制
key 的长度: 250 字节, (二进制协议支持 65536 个字节)
value 的限制: 1m, 一般都是存储一些文本,如新闻列表等等,这个值足够了.
内存的限制: 32 位下最大设置到 2G.
如果有 30g 数据要缓存,一般也不会单实例装 30G, (不要把鸡蛋装在一个篮子里), 一般建议 开启多个实例(可以在不同的机器,或同台机器上的不同端口)
分布式集群算法
Memcached如何实现分布式
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), '<br>';
}
一致性哈希算法
通俗理解一致性哈希:把各服务器节点映射放在钟表的各个时刻上,把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服务器对应的虚拟节点数量
//计算一个字符串对应的32 位循环冗余校验码多项式
public function hash($str) {
return sprintf("%u", crc32($str));
}
//查询数据应存放的节点服务器
public function lookup($key) {
$position = $this->hash($key);
reset($this->points); //重置指针(因为foreach遍历时会数组指针后移,等到下次遍历数组时,key()方法获取的就不是第一个元素的键了)
$needle = key($this->points); //默认会落在第一个节点
foreach ($this->points as $key => $value) {
if($position <= $key) {
$needle = $key;
break;
}
}
return $this->points[$needle];
}
//添加节点
public function addNode($node) {
$this->nodes[$node] = array();
for($i = 0; $i < $this->multi; $i++) {
$point = $node . '_' . $i;
$point = $this->hash($point); //虚拟节点转为数字
$this->points[$point] = $node;
$this->nodes[$node][] = $point;
$this->resort();
}
}
public function delNode($node) {
foreach ($this->nodes[$node] as $p) { //清除64个虚拟节点
unset($this->points[$p]);
}
unset($this->nodes[$node]); //去掉节点
}
//对虚拟几点进行排序
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');
并发处理-乐观锁
在新版的memcached中,增加了对并发的控制,处理方案是:乐观锁
并发:多个进程(连接), 同时操作一个key. 就是并发操作.
乐观锁:
进程A, 先操作了缓存项
在进程A第二次操作缓存项前, 进程B操作了缓存项.
之后, 进程A第二次操作缓存项. 检查, 在进程A第一次操作后, 是否有其他进程操作过需要的缓存项. 如果有, 则放弃第二次操作. 采取乐观的处理态度.(乐观锁定)
支持乐观锁的操作:
gets() 获取
cas() 设置
注意:Memcache扩展还不支持这两个操作,在telnet上可以演示
Memcached经典问题或现象
缓存雪崩现象及真实案例
缓存雪崩一般是由某个缓存节点失效,导致其他节点的缓存命中率下降,缓存中缺失的数据去数据库查询。短时间内,造成数据库服务器崩溃。
重启DB,短期又被压垮,但缓存数据也多一些。
DB反复多次启动,缓存重建完毕,DB才稳定运行。
或者,是由于缓存周期性的失效,比如每6个小时失效一次,那么每6小时,将有一个请求“峰值”,严重者甚至会令DB崩溃。
解决办法/方案:把缓存设置为随机3-9个小时的生命周期,这样不同时失效,把工作分担到各个时间点。
缓存的无底洞现象multiget-hole
永久数据被踢现象
网上有人反馈为"memcached 数据丢失",明明设为永久有效,却莫名其妙的丢失了.
其实,这要从 2 个方面来找原因:
即前面介绍的 惰性删除,与 LRU 最近最少使用记录删除.
分析(如下图)
1:如果 slab 里的很多 chunk,已经过期,但过期后没有被 get 过, 系统不知他们已经过期.
2:永久数据很久没 get 了,不活跃,如果新增 item,则永久数据被踢了.
3: 当然,如果那些非永久数据被 get,也会被标识为 expire,从而不会再踢掉永久数据
Memcached详解的更多相关文章
- 【山外笔记-数据库】Memcached详解教程
本文打印版文档下载地址 [山外笔记-数据库]Memcached详解教程-打印版.pdf 一.Memcached数据库概述 1.Memcached简介 (1)Memcached是一个自由开源的,高性能, ...
- php使用memcached详解
一.memcached 简介 在很多场合,我们都会听到 memcached 这个名字,但很多同学只是听过,并没有用过或实际了解过,只知道它是一个很不错的东东.这里简单介绍一下,memcached 是高 ...
- 基于linux操作系统安装、使用memcached详解
1.memcached的应用背景及作用 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态. ...
- (转)Memcached用法--参数和命令详解
Memcached用法--参数和命令详解 1. memcached 参数说明: # memcached -h 1.1 memcached 的参数 常用参数 -p <num> 监听的TCP端 ...
- Memcached集群/分布式/高可用 及 Magent缓存代理搭建过程 详解
当网站访问量达到一定时,如何做Memcached集群,又如何高可用,是接下来要讨论的问题. 有这么一段文字来描述“Memcached集群” Memcached如何处理容错的? 不处理!:) 在memc ...
- memcached 命令操作详解
memcached 命令操作详解 一.存储命令 存储命令的格式: <command name> <key> <flags> <exptime> < ...
- 分布式缓存Memcached/memcached/memcache详解及区别
先来解释下标题中的三种写法:首字母大写的Memcached,指的是Memcached服务器,就是独立运行Memcached的后台服务器,用于存储缓存数据的“容器”.memcached和memcache ...
- 猫哥网络编程系列:详解 BAT 面试题
从产品上线前的接口开发和调试,到上线后的 bug 定位.性能优化,网络编程知识贯穿着一个互联网产品的整个生命周期.不论你是前后端的开发岗位,还是 SQA.运维等其他技术岗位,掌握网络编程知识均是岗位的 ...
- redis配置详解
##redis配置详解 # Redis configuration file example. # # Note that in order to read the configuration fil ...
随机推荐
- chip8模拟器的python3实现-1-CHIP8简介
打算编写一个NES模拟器,先从简单的chip8模拟器入手 1.CHIP-8简介 CHIP-8是一个解释型语言,由Joseph Weisbecker开发.最初CHIP-8在上个世纪70年代被使用在COS ...
- Java-Oracle数据库连接
Oracle数据库先创建一个表和添加一些数据,下面是连接数据库的具体实现.(导入jar包:ojdbc14.jar) import java.sql.Connection; import java.sq ...
- css -html-文档流
首先先考虑一下什么是普通流?普通流就是正常的文档流,在HTML里面的写法就是从上到下,从左到右的排版布局. 例: <div id="01"></div>&l ...
- elementUI中表格中表单的验证
表格中的表单验证,就是在将表格放在表单中,将表格绑定的数据也放在表单中. 最重要的是要给表格中需要验证的字段动态添加prop,再给其绑定规则. <el-form :model="tab ...
- python基础之Day17
一.包 1.包 含有--init--.py的文件夹 是模块的一种形式 本质是一个文件夹(用来存放文件,包内所有的文件都是用来被导入的) import 包 包.名字 往包的init里加名字 导 ...
- mr统计每年中每月温度的前三名
weatherMapper package com.laoxiao.mr.weather; import java.text.ParseException; import java.text.Simp ...
- sqlite基本用法
DDL-数据定义语言 CREATE 创建一个新的表,一个表的视图,或者数据库中的其他对象. ALTER 修改数据库中的某个已有的数据库对象,比如一个表. DROP 删除整个表,或者表的视图,或者数据库 ...
- 2019新版UI设计面试题汇总(附答案)
问题一.Android手机的常用设计尺寸有_________.怎么适配ios和安卓. 答案:安卓320 X 480是常规模拟器.但现在的开发都是用360x640做一倍率.480 X 800(1.5倍率 ...
- Python之路(第三十九篇)管道、进程间数据共享Manager
一.管道 概念 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信. 先画一幅图帮助大家理解下管道的基本原理 现有2个 ...
- Java 初学UDP传输
不谈理论,先举简单例子. 发送端代码: public class UDPDemo { public static void main(String[] args) throws Exception { ...