1 安装memcache服务端,以ubuntu系统为例
(1) 在安装memcache之前要先安装依赖库libevent
wget https://github.com/downloads/libevent/libevent-2.0.15-stable.tar.gz
tar -zxvf libevent-2.0.15-stable.tar.gz
cd libevent-2.0.15-stable
./configure --prefix=/usr/local
make
sudo make install
(2) libevent 安装完毕之后,开始安装memcached服务器,安装过程如下:
wget http://memcached.googlecode.com/files/memcached-1.4.9.tar.gz
tar -zxvf memcached-1.4.9.tar.gz
cd memcached-1.4.9
./configure --prefix=/usr/local/memcached
make
sudo make install
(3) memcached 安装完毕之后,使用以下命令启动memcache服务器
/usr/local/memcached/bin/memcached -d -m 128 -u root -p 11211
(4) memcached 常用启动选项:
-d ##以守护程序(daemon)的方式运行memcached
-m ## 设置memcached的内存大小,以M为单位
-l ## 设置memcached监听的ip,如果是本机的话,该参数可以不用设置
-p ## 设置监听的端口
-u ## 指定用户,如果当前用户为root,需要设置此参数
使用 usr/memcached /local/bin/memcached -h 可以了解更多的参数选项,常用的只有上边那几个
2 安装memcached客户端
## memcached的客户端很多,以php的memcache扩展为例:
(1)安装PHP的memcache扩展
wget http://pecl.php.net/get/memcache-2.2.5.tgz
tar -zxvf memcache-2.2.5.tgz
cd memcache-2.2.5.tgz
phpize
./configure --enable-memcache --with-php-config=/usr/local/php/bin/php-config --with-zlib-dir
make
sudo make install
(2)在php的配置文件中加入以下配置项:
extension=/usr/local/php/lib/php/extensions/no-debug-non-zts-20120625/memcache.so
(3)重启web服务器
如果安装成功,可以在phpinfo页面看到memcache的相关信息
使用以下php程序进行测试:
$mc = new Memcache();
$mc->connect('127.0.0.1',11211);
$mc->set('key','value',0,10);
$val = $mc->get('key');
$mc->delete('key');
$mc->flush();
$mc->close();
3 使用memcache扩展访问memcached服务器:(下边是memcache扩展的一些常用方法)
Memcache::connect(string $host [,int $port [,int $timeout]])
Memcache::addServer()
Memcache::add()
Memcache::replace()
Memcache::set()
Memcache::get()
Memcache::delete()
Memcache::flush()
getServerSatus()
Memcache::getSatus()
Memcache::close()
以上方法的具体使用,请参照php手册,在线手册地址:https://secure.php.net/manual/zh/book.memcache.php
4 使用memcache加速web应用
在实际应用中,把数据库查询加过放到memcache,下次直接访问缓存,不再进行数据库操作,可以减小数据库的压力,并提高访问速度
$mc = new Memcache();//创建memcache连接 $mc->connect('127.0.0.1',11211); $uid = $_GET['uid']; $sql = "select * from users where uid = '$uid'"; $key = md5($sql); //查询数据库查询结果是否已经保存在缓存中 if(!($datas=$mc->get($key))){ //在memcache中未找到缓存数据,查询数据库 $con = mysqli_connect('localhost','nj','nj'); mysqli_select_db('test'); $res = mysqli_query($sql); while($row=mysqli_fetch_object($res)){ $datas[]=$row; } //将从数据库查询的结果放到memcache服务器 $mc->add($key,$datas); } var_dump($datas);
5 memcache使用多路复用IO模型,不会阻塞在某一个用户连接,使用消息通知的模式
memcache使用多线程,在启动服务时,使用-t选项指定开启的线程数,但并不是线程数越多越好,一般指定为CPU核数,这样效率最高,因为线程数越多,CPU需要的调度时间越多,指定为CPU核数时,系统需要的线程调度时间最少
6 使用slab分配算法来保存数据
memcached因为使用slab内存分配算法,使其默认存储值不能大于1M,优点:可以减少内存碎片,提高内存使用效率,该算法的源码在slabs.c文件中
slab_clsid()根据参数size找到一个chunk块大小合适的slabclass,把其中可用的chunk块分配给emmcache使用,具体分配由do_slabd_alloc函数完成,它会先尝试从被回收的chunk列表(slots)中获取,如果没有可用的,就从空闲的chunk块列表获取可用的chunk块并返回
7 删除过期item
memcache为每个item设置一个过期时间,但不是到期就把item从内存中删除,而是访问item时,如果到了有效期,才把item从缓存中删除,这样可不必每次检查过期item,而是在查找时判断,提高CPU的工作效率
8 使用LRU算法淘汰数据
memcached会把刚刚访问过的item放到item列表的头部,所以尾部的item都是没有访问过的,所以,memcache在slabs_alloc函数申请内存失败时,从items list的尾部开始遍历,在列表中查找引用计数为0的item将其释放,如果找不到,就找最近3小时内没有访问的item,如果还是找不到,就返回null,申请内存失败
9 memcache的多线程模型
memcache是多线程的,分为主线程(main thread)接受客户端连接,并分配给工作线程处理
工作线程(worker thread):处理客户端的连接请求
主线程主要侦听客户端连接,有新的连接请求时,memcached调用accept接收来自客户端的请求,把连接push到工作线程的CQ队列,并向工作线程发送一个信号,通知工作线程有新的客户端连接
10 memcache的一致性分布实现
#分为6个步骤 #(1)把一个32位整数想象成一个环,0是首,2^32-1是尾 #(2)用hash函数把key处理成整数,找到环中对应位置 #(3)将mem群映射到环,用hash函数处理服务器使用的ip # 经过以上处理把服务器和数据的key都映射到了环上 #(4)把数据映射到环上 #(5)移除服务器,若一个server崩溃,只需将映射到该server的数据映射到下一个可用server #(6)添加服务器,重新映射改分布到该服务器的数据即可 class Flexihash { private $serverList = array();//保存的服务器列表 private $isSorted = false;//记录服务器列表是否已经排序 // 添加服务器 public function addServer($server) { $hash = mHash($server); if (!isset($this->serverList[$hash])) { $this->serverList[$hash] = $server; } $this->isSorted = false;//此时服务器列表发生了变化,所以把sort设为false return true; } // 移除服务器 public function removeServer($server) { $hash = mhash($server); if (isset($this->serverList[$hash])) { unset($this->serverList[$hash]); } $this->isSorted = false; return true; } // 在当前服务器列表中找到合适的服务器存放数据 public function lookup($key) { $hash = mhash($key); if (!$this->isSorted) { ksort($this->serverList, SORT_NUMERIC); $this->isSorted = true; } foreach ($this->serverList as $pos => $server) { if ($hash >= $pos) { return $server; } } return $this->serverList[count($this->serverList)-1]; } }