memcache入门
本文最后更新于 3011 天前,其中的信息可能已经有所发展或是发生改变。

写在前面

简介

  • memcache是一款开源软件,由LiveJournal的Brad Fitzpatrick开发,以BSD license授权发布。
  • 是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个巨大的hash表,将数据(包括图像、视频、文件以及数据库检索的结果等)调用到内存中,然后从内存中读取,从而大大提高读取速度。

特性

  • 非持久性存储
  • 定位于分布式存储,不适用于单机系统
  • 不支持List、Array等复杂的数据

Memcache和Memcached

  • 这款开源软件的项目名叫Memcache,Memcached (Memcached-Daemon的简称) 是软件的主程序名。
  • 在php中,有两个为memcache开发的扩展(客户端):Memcache扩展和memcached扩展。后者比前者要新,功能也比较多。推荐安装后者。

部署环境

安装memcache服务端

sudo apt-get install memcached #for Ubuntu
yum install memcached #for CentOS

安装php扩展(客户端)

这里以安装memcached扩展为例,从官网下载最新版本,解压并cd到目录

wget http://pecl.php.net/get/memcached-2.2.0.tgz
tar -zxvf memcached-2.2.0.tgz
cd memcached-2.2.0

编译安装

phpize #生成configure配置文件。若找不到命令请用绝对路径 /usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config  #配置
make  #编译
make install #安装。需要root权限

安装完后会提示安装的目录:

Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/

编辑php.ini文件

#找到extension_dir,如果没有设置扩展的路径,则把上面memcached.so所在的路径粘贴进去
extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/"
#找到;extension=php_XXX.dll的地方,在最下面添加上
extension=memcached.so

最后,重启web服务器和php-fpm
看看phpinfo()是否将memcached添加成功。我这里为了做测试编译了两个扩展,实际编译后者就可以。


简单使用

这里只介绍php中memcached类的使用。更多请参考手册

<?php
$md = new memcached;

//向服务器池添加服务器,也可以使用addServers方法一次添加多个
//public bool Memcached::addServer ( string $host , int $port [, int $weight = 0 ] )
$md->addServer('127.0.0.1',11211);

//$expiration默认为0,永不超时。但即使为0,当内存不够用的时候,memcache也会将不常使用的数据删掉
//expiration不能超过60×60×24×30(30天时间的秒数);如果大于这个值,服务端会将其作为一个真实的Unix时间戳来处理而不是 自当前时间的偏移
//public bool Memcached::add ( string $key , mixed $value [, int $expiration ] )
$md->add('addItem','this is content one by add');
$md->add('addItem','this is content two by add');//false.若使用add方法添加已经存在的key,无法添加成功

//public mixed Memcached::get ( string $key [, callback $cache_cb [, float &$cas_token ]] )
echo $md->get('addItem');//after add:this is content one by add

//public bool Memcached::delete ( string $key [, int $time = 0 ] )
$md->delete('addItem');
echo $md->get('addItem');//空

//使用set方法重复添加同样的key是合法的,且后面的数据会覆盖前面的数据
//public bool Memcached::set ( string $key , mixed $value [, int $expiration ] )
$md->set('tip','hello');//tip='hello'
$md->set('tip','world');//tip='world'

//若使用追加方法,必须将OPT_COMPRESSION(压缩)设置为false
$md->setOption(Memcached::OPT_COMPRESSION,false);
$md->set('tip','hello');//tip='hello'
//向指定元素后面追加一个字符串
//public bool Memcached::append ( string $key , string $value )
$md->append('tip',' world');//tip='hello world'

$arr = [
    'name' => 'foam',
    'age'  => '22',
];
//同时存储多个数据
//public bool Memcached::setMulti ( array $items [, int $expiration ] )
$md->setMulti($arr);
echo $md->get('name');//foam
echo $md->get('age');//22

$md->set('count','-99');//count=-99
//自增
//public int Memcached::increment ( string $key [, int $offset = 1 ] )
$md->increment('count');//false.对于负数和非整数是无法自增的
$md->set('count',0);
$md->increment('count');//count=1
$md->increment('count',100);//count=101

//自减
//public int Memcached::decrement ( string $key [, int $offset = 1 ] )
$md->decrement('count');//count=100
$md->decrement('count',50);//count=50
$md->decrement('count',60);//count=0.最小只能减到0

//清空所有数据
//public bool Memcached::flush ([ int $delay = 0 ] )
$md->flush();
$md->get('tip');//false
$md->get('count');//false

其他

Memcache的分布式缓存

Memcache尽管说是分布式缓存服务器,但其分布式却是由客户端实现的。
保存数据时,客户端会根据数据的键使用特定的算法(不同的客户端,算法不同)选择要保存的服务器,将其存入其中。获取数据时也是使用相同的算法在所在的服务器获取。这些客户端算法一般在Hash的基础上进行改良,保证其数据的分散性。
具体策略就不展开说明了。

Memcache与锁

在高并发的业务场景中,都会使用锁的概念。如:
场景一:一批cache同时过期,许多客户端同时并发请求cache。此时业务逻辑发现cache过期,会高并发下从DB同时请求数据并重新设置给memcache。整个过程都是高并发的,就如决堤,洪水汹涌袭来一般。对系统的负载能力将是巨大的挑战。
场景二:脏数据场景,有两个客户A和B。A发送投票请求,服务器在12时00分00秒10毫秒取出当前票数100,在12时00分00秒15毫秒给当前票数自增1并保存。B发送投票请求,服务器在12时00分00秒12毫秒取出当前票数100,在12时00分00秒13毫秒给当前票数自增1并保存。总票数原本应该为102的实际上却是101。即使数据库上已经使用了锁的策略,但由于磁盘IO速度慢,仍然会导致读写脏数据的错误。如果锁的动作发生在内存上就不一样了。
我们可以用memcache实现简单的锁,重点在于memcache的add方法,如果cache上有相同的键了,add方法会返回false。下面是用php实现的简单锁。

class MemcacheLock
{
    private $_host = '127.0.0.1';
    private $_port = 11212;
    private $_md;
    const TIMEOUT = 10;

    public function __construct()
    {
        $this->_md = new memcached;
        $this->_md->addServer($this->_host,$this->_port);
    }
    //为防止产生死锁,需要设置过期时间,时间长度根据业务决定
    public function lock($key,$timeOut=self::TIMEOUT)
    {
        return $this->_md->add($key,'lock',$timeOut);
    }

    public function unLock($key)
    {
        $this->_md->delete($key);
    }
}

Memcache和Memcached扩展互不兼容

NOTIC:这里指的是Memcache的两个php扩展

php.net上有人做了测试,两个扩展间由于调用的客户端库不同,存储的方式有一定的差异。
测试脚本:

$memcache = new Memcache;
$memcacheD = new Memcached;

$memcache->addServer('127.0.0.1',11211);
$memcacheD->addServer('127.0.0.1',11211);

$checks = array(
    123,
    4542.32,
    'a string',
    true,
    array(123, 'string'),
    (object)array('key1' => 'value1'),
);
foreach ($checks as $i => $value) {
    print "<br/><br/>Checking WRITE with Memcache<br/>";
    $key = 'cachetest' . $i;
    $memcache->set($key, $value);
    usleep(100);
    $val = $memcache->get($key);
    $valD = $memcacheD->get($key);
    if ($val !== $valD) {
        print "Not compatible!<br/>";
        var_dump(compact('val', 'valD'));
    }

    print "<br/><br/>Checking WRITE with MemcacheD";
    $key = 'cachetest' . $i;
    $memcacheD->set($key, $value);
    usleep(100);
    $val = $memcache->get($key);
    $valD = $memcacheD->get($key);
    if ($val !== $valD) {
        print "Not compatible!<br/>";
        var_dump(compact('val', 'valD'));
    }
}

 

结果是只有处理string的方式一样,其他数据结构都采用了不同的处理方式。因此两个扩展间是不能相互切换的。

Last:还有许多值得说的地方,日后若有memcache的相关内容可能会补充进来。以上内容若存在错误的地方,请留言或发邮件指正错误,谢谢。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇