Memcache操作详解及分析

一、 linux 编译 memcached 准备编译环境
在 linux 编译,需要 gcc,make,cmake,autoconf,libtool 等工具

#yum install gcc make cmake autoconf libtool

二、 编译 memcached
memcached 依赖于 libevent 库.libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口.因此我们需要先安装 libevent.
分别到 libevent.org 和 memcached.org 下载最新的 stable 版本(稳定版).
先编译 libevent ,再编译 memcached,
编译 memcached 时要指定 libevent 的路径.
过程如下: 假设源码在/usr/local/src 下, 安装在/usr/local 下

# tar zxvf libevent-2.0.21-stable.tar.gz
# cd libevent-2.0.21-stable
# ./configure --prefix=/usr/local/libevent
# 如果出错,读报错信息,查看原因,一般是缺少库
# make && make install
# tar zxvf memcached-1.4.5.tag.gz
# cd memcached-1.4.5
#./configure--prefix=/usr/local/memcached \
--with-libevent=/usr/local/libevent
# make && make install

注意: 在虚拟机下练习编译,一个容易碰到的问题---虚拟机的时间不对,
导致的 gcc 编译过程中,检测时间通不过,一直处于编译过程.
解决:

# date -s ‘yyyy-mm-dd hh:mm:ss’
# clock -w # 把时间写入 cmos

三、 memcached 的启动

# /usr/local/memcached/bin/memcached -m 64 -p 11211 -u nobody -vv
slab class 1: chunk size 96 perslab 10922
slab class 2: chunk size 120 perslab 8738
slab class 3: chunk size 152 perslab 6898
slab class 4: chunk size 192 perslab 5461
....
....
slab class 9: chunk size 600 perslab 1747
slab class 10: chunk size 752 perslab 1394
slab class 39: chunk size 493552 perslab 2
slab class 40: chunk size 616944 perslab 1
slab class 41: chunk size 771184 perslab 1
slab class 42: chunk size 1048576 perslab 1

memcached 后台运行,只需要加-d 选项

# /usr/local/memcached/bin/memcached -m 64 -p 11211 -u nobody -d

查看linux端口状态

#netstat –an //观察11211端口
#netstat –anb //程序监听 那些客户连接服务器

memcached -h 查看帮助

-p  tcp port number to listen on (default: 11211) // 监听的端口
-u  udp port number to listen on (default: 0, off)
-s  unix socket path to listen on (disables network support)
-a  access mask for unix socket, in octal (default 0700)
-l  interface to listen on, default is indrr_any
-d start tell memcached to start
-d restart tell running memcached to do a graceful restart
-d stop|shutdown tell running memcached to shutdown
-d install install memcached service // 把 memcached 注册成服务
-d uninstall uninstall memcached service
-r maximize core file limit
-u  assume identity of  (only when run as root)
-m  max memory to use for items in megabytes, default is 64
mb //分配给 memcached 的最大内存
-m return error on memory exhausted (rather than removing
items)
-c  max simultaneous connections, default is 1024 // 最大的连接数
-k lock down all paged memory. note that there is a
limit on how much memory you may lock. trying to
allocate more than that would fail, so be sure you
set the limit correctly for the user you started
the daemon with (not for -u  user;
under sh this is done with 'ulimit -s -l num_kb').
-v verbose (print errors/warnings while in event loop) //输出错误信息
-vv very verbose (also print client commands/reponses) //输出所有信息
-h print this help and exit
-i print memcached and libevent license
-b run a managed instanced (mnemonic: buckets)
-p  save pid in , only used with -d option
-f  chunk size growth factor, default 1.25 //增长因子
-n  minimum space allocated for key+value+flags, default 48

在 windows 下启动 memcached:
下载解压后,不用安装,通过命令行下进入到 memcached.exe 所在的目录

>memcached -m 64 -p 11211 -vvv

四、memcached 连接的几种方式
php无memcache扩展情况下可以使用原生的socket通信操作,memcached-client.php类库附在本文底部

mem04.jpg

memcached 客户端与服务器端的通信比较简单,使用的基于文本的协议,而不是二进制协议.

# 格式 telnet host port
# telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.

五、memcached命令
增: add 往内存增加一行新记录
语法: add key flag expire length 回车
key 给值起一个独特的名字
flag 标志,要求为一个正整数
expire 有效期
length 缓存的长度(字节为单位)

flag意义:memcached 基本文本协议,传输的东西,理解成字符串来存储.序列化成字符串,往出取的时候,自然还要反序列化成 对象/数组/json 格式

  • 1:字符串,
  • 2:反转成数组
  • 3:反序列化对象

expire 设置缓存的有效期,有3种格式

  • 1:设置秒数, 从设定开始数,第 n 秒后失效.(直接给秒数,则最大可设为 30360024,超过则失效)
  • 2:时间戳, 到指定的时间戳后失效.(如果你希望保持时间超过30天 time()+天数360024 即可;
    比如在团购网站,缓存的某团到中午 12:00 失效. add key 0 1379209999 6)
  • 3: 设为 0. 不自动失效.
    注: 有种误会,设为 0,永久有效.错误的.
    编译 memcached 时,指定一个最长常量,默认是 30 天.即使设为 0,30 天后也会失效.也有可能等不到 30 天,就会被新数据挤出去.
    delete 删除
    delete key [time seconds]
    删除指定的 key. 如加可选参数 time,则指删除 key,并在删除 key 后的 time 秒内,不允许使用get,add,replace 操作此 key.
    replace 替换
    replace key flag expire length
    get 查询
    get key
    set 是设置和修改值
    用add 时,key 不存在,才能建立此键值.
    但对于已经存在的键,可以用 replace 进行替换/更改
    repalce,key 存在时,才能修改此键值,不存在,则没改成功.
    而 set 想当于有 add replace 两者的功能.
    set key flag expire leng 时
    如果服务器无此键 ----> 增加的效果
    如果服务器有此键 ----> 修改的效果.
    incr ,decr 命令:增加/减少值的大小
    语法: incr/decr key num
    注意:incr,decr 操作是把值理解为 32 位无符号来+-操作的. 值在[0-2^32-1]范围内
    应用场景------秒杀功能,
    一个人下单,要牵涉数据库读取,写入订单,更改库存,及事务要求, 对于传统型数据库来说,压力是巨大的.可以利用 memcached 的 incr/decr 功能, 在内存存储 count 库存量, 秒杀 1000 台每人抢单主要在内存操作,速度非常快,抢到 count<=1000 的号人,得一个订单号,再去另一个页面慢慢支付
    统计命令: stats
stat uptime 4237 持续运行时间
stat time 1370054990
stat version 1.2.6
stat pointer_size 32
stat curr_items 4 当前存储的键个数
stat total_items 13
stat bytes 236
stat curr_connections 3
stat total_connections 4
stat connection_structures 4
stat cmd_get 20
stat cmd_set 16
stat get_hits 13 //查询到数据的次数
stat get_misses 7 // 未查询到数据的次数
stat evictions 0 //LRU释放的对象数目 挤掉对象 可以配置此算法不生效
stat bytes_read 764
stat bytes_written 618
stat limit_maxbytes 67108864
stat threads 1
end

memcache01.gif

memcache02.jpg

命中率是指: (查询到数据的次数/查询总数)100%
13/(13+7) = 60% , 的命中率.
** flush_all 清空所有的存储对象
*

Memcache管理后台,MemAdmin

官网下载部署到网站根目录使用即可

memadmin安装教程
memadmin下载地址

memcache03.png

六、php相关代码
不便修改 php.ini 文件,处理session存入memcached功能, 我们通过函数可以去修改 php.ini 的配置.

memcache分布式相关的php代码

php存值

addServer('127.0.0.1',11211);
    $mem->addServer('127.0.0.1',11212);
    $mem->addServer('127.0.0.1',11213);
    $mem->addServer('127.0.0.1',11214);
    //注意,把key放入那个端口的mem我们不用操心,由$mem对象采用哈希一致性原理来维护.
    if($mem->set('key1','hello',MEMCACHE_COMPRESSED,300)){
        echo 'add1 ok!';
    }
    if($mem->set('key2','hello2',MEMCACHE_COMPRESSED,300)){
        echo 'add2 ok!';
    }
    if($mem->set('key3','hello3',MEMCACHE_COMPRESSED,300)){
        echo 'add3 ok!';
    }

php取值

 从多个mem中取出你的key
    $mem=new Memcache;
    $mem->addServer('127.0.0.1',11211);
    $mem->addServer('127.0.0.1',11212);
    $mem->addServer('127.0.0.1',11213);
    $mem->addServer('127.0.0.1',11214);
    $val=$mem->get('key1'); 
    echo '程序通过哈希运算取出分布的值:'.$val;

memcached-client.php类库(无需php扩展memcache)

                 |
// | All rights reserved.                                                      |
// |                                                                           |
// | Redistribution and use in source and binary forms, with or without        |
// | modification, are permitted provided that the following conditions        |
// | are met:                                                                  |
// |                                                                           |
// | 1. Redistributions of source code must retain the above copyright         |
// |    notice, this list of conditions and the following disclaimer.          |
// | 2. Redistributions in binary form must reproduce the above copyright      |
// |    notice, this list of conditions and the following disclaimer in the    |
// |    documentation and/or other materials provided with the distribution.   |
// |                                                                           |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
// +---------------------------------------------------------------------------+
// | Author: Ryan T. Dean                             |
// | Heavily influenced by the Perl memcached client by Brad Fitzpatrick.      |
// |   Permission granted by Brad Fitzpatrick for relicense of ported Perl     |
// |   client logic under 2-clause BSD license.                                |
// +---------------------------------------------------------------------------+
//
// $TCAnet$
//

/**
 * This is the PHP client for memcached - a distributed memory cache daemon.
 * More information is available at http://www.danga.com/memcached/
 *
 * Usage example:
 *
 * require_once 'memcached.php';
 * 
 * $mc = new memcached(array(
 *              'servers' => array('127.0.0.1:10000', 
 *                                 array('192.0.0.1:10010', 2),
 *                                 '127.0.0.1:10020'),
 *              'debug'   => false,
 *              'compress_threshold' => 10240, //压缩
 *              'persistant' => true));        //长链接/短连接模式
 *
 * $mc->add('key', array('some', 'array'));
 * $mc->replace('key', 'some random string');
 * $val = $mc->get('key');
 *
 * @author  Ryan T. Dean 
 * @package memcached-client
 * @version 0.1.2
 */

// {{{ requirements
// }}}

// {{{ constants
// {{{ flags

/**
 * Flag: indicates data is serialized
 */
define("MEMCACHE_SERIALIZED", 1<<0);

/**
 * Flag: indicates data is compressed
 */
define("MEMCACHE_COMPRESSED", 1<<1);

// }}}

/**
 * Minimum savings to store data compressed
 */
define("COMPRESSION_SAVINGS", 0.20);

// }}}

// {{{ class memcached
/**
 * memcached client class implemented using (p)fsockopen()
 *
 * @author  Ryan T. Dean 
 * @package memcached-client
 */
class memcached
{
   // {{{ properties
   // {{{ public

   /**
    * Command statistics
    *
    * @var     array
    * @access  public
    */
   var $stats;
   
   // }}}
   // {{{ private

   /**
    * Cached Sockets that are connected
    *
    * @var     array
    * @access  private
    */
   var $_cache_sock;
   
   /**
    * Current debug status; 0 - none to 9 - profiling
    *
    * @var     boolean
    * @access  private
    */
   var $_debug;
   
   /**
    * Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
    *
    * @var     array
    * @access  private
    */
   var $_host_dead;
   
   /**
    * Is compression available?
    *
    * @var     boolean
    * @access  private
    */
   var $_have_zlib;
   
   /**
    * Do we want to use compression?
    *
    * @var     boolean
    * @access  private
    */
   var $_compress_enable;
   
   /**
    * At how many bytes should we compress?
    *
    * @var     interger
    * @access  private
    */
   var $_compress_threshold;
   
   /**
    * Are we using persistant links?
    *
    * @var     boolean
    * @access  private
    */
   var $_persistant;
   
   /**
    * If only using one server; contains ip:port to connect to
    *
    * @var     string
    * @access  private
    */
   var $_single_sock;
   
   /**
    * Array containing ip:port or array(ip:port, weight)
    *
    * @var     array
    * @access  private
    */
   var $_servers;
   
   /**
    * Our bit buckets
    *
    * @var     array
    * @access  private
    */
   var $_buckets;
   
   /**
    * Total # of bit buckets we have
    *
    * @var     interger
    * @access  private
    */
   var $_bucketcount;
   
   /**
    * # of total servers we have
    *
    * @var     interger
    * @access  private
    */
   var $_active;

   // }}}
   // }}}
   // {{{ methods
   // {{{ public functions
   // {{{ memcached()

   /**
    * Memcache initializer
    *
    * @param   array    $args    Associative array of settings
    *
    * @return  mixed
    * @access  public
    */
   function memcached ($args)
   {
      $this->set_servers($args['servers']);
      $this->_debug = $args['debug'];
      $this->stats = array();
      $this->_compress_threshold = $args['compress_threshold'];
      $this->_persistant = isset($args['persistant']) ? $args['persistant'] : false;
      $this->_compress_enable = true;
      $this->_have_zlib = function_exists("gzcompress");
      
      $this->_cache_sock = array();
      $this->_host_dead = array();
   }

   // }}}
   // {{{ add()

   /**
    * Adds a key/value to the memcache server if one isn't already set with 
    * that key
    *
    * @param   string   $key     Key to set with data
    * @param   mixed    $val     Value to store
    * @param   interger $exp     (optional) Time to expire data at
    *
    * @return  boolean
    * @access  public
    */
   function add ($key, $val, $exp = 0)
   {
      return $this->_set('add', $key, $val, $exp);
   }

   // }}}
   // {{{ decr()

   /**
    * Decriment a value stored on the memcache server
    *
    * @param   string   $key     Key to decriment
    * @param   interger $amt     (optional) Amount to decriment
    *
    * @return  mixed    FALSE on failure, value on success
    * @access  public
    */
   function decr ($key, $amt=1)
   {
      return $this->_incrdecr('decr', $key, $amt);
   }

   // }}}
   // {{{ delete()

   /**
    * Deletes a key from the server, optionally after $time
    *
    * @param   string   $key     Key to delete
    * @param   interger $time    (optional) How long to wait before deleting
    *
    * @return  boolean  TRUE on success, FALSE on failure
    * @access  public
    */
   function delete ($key, $time = 0)
   {
      if (!$this->_active)
         return false;
         
      $sock = $this->get_sock($key);
      if (!is_resource($sock))
         return false;
      
      $key = is_array($key) ? $key[1] : $key;
      
      $this->stats['delete']++;
      $cmd = "delete $key $time\r\n";
      if(!fwrite($sock, $cmd, strlen($cmd)))
      {
         $this->_dead_sock($sock);
         return false;
      }
      $res = trim(fgets($sock));
      
      if ($this->_debug)
         printf("MemCache: delete %s (%s)\n", $key, $res);
      
      if ($res == "DELETED")
         return true;
      return false;
   }

   // }}}
   // {{{ disconnect_all()

   /**
    * Disconnects all connected sockets
    *
    * @access  public
    */
   function disconnect_all ()
   {
      foreach ($this->_cache_sock as $sock)
         fclose($sock);

      $this->_cache_sock = array();
   }

   // }}}
   // {{{ enable_compress()

   /**
    * Enable / Disable compression
    *
    * @param   boolean  $enable  TRUE to enable, FALSE to disable
    *
    * @access  public
    */
   function enable_compress ($enable)
   {
      $this->_compress_enable = $enable;
   }

   // }}}
   // {{{ forget_dead_hosts()

   /**
    * Forget about all of the dead hosts
    *
    * @access  public
    */
   function forget_dead_hosts ()
   {
      $this->_host_dead = array();
   }

   // }}}
   // {{{ get()

   /**
    * Retrieves the value associated with the key from the memcache server
    *
    * @param  string   $key     Key to retrieve
    *
    * @return  mixed
    * @access  public
    */
   function get ($key)
   {
      if (!$this->_active)
         return false;
         
      $sock = $this->get_sock($key);
      
      if (!is_resource($sock))
         return false;
         
      $this->stats['get']++;
      
      $cmd = "get $key\r\n";
      if (!fwrite($sock, $cmd, strlen($cmd)))
      {
         $this->_dead_sock($sock);
         return false;
      }
      
      $val = array();
      $this->_load_items($sock, $val);
      
      if ($this->_debug)
         foreach ($val as $k => $v)
            printf("MemCache: sock %s got %s => %s\r\n", $sock, $k, $v);
      
      return $val[$key];
   }

   // }}}
   // {{{ get_multi()

   /**
    * Get multiple keys from the server(s)
    *
    * @param   array    $keys    Keys to retrieve
    *
    * @return  array
    * @access  public
    *  get('key1')   keys=array('key1','key2');
    */
   function get_multi ($keys)
   {
      if (!$this->_active)
         return false;
         
      $this->stats['get_multi']++;
      
      foreach ($keys as $key)
      {
         $sock = $this->get_sock($key);
         if (!is_resource($sock)) continue;
         $key = is_array($key) ? $key[1] : $key;
         if (!isset($sock_keys[$sock]))
         {
            $sock_keys[$sock] = array();
            $socks[] = $sock;
         }
         $sock_keys[$sock][] = $key;
      }
      
      // Send out the requests
      foreach ($socks as $sock)
      {
         $cmd = "get";
         foreach ($sock_keys[$sock] as $key)
         {
            $cmd .= " ". $key;
         }
         $cmd .= "\r\n";
         
         if (fwrite($sock, $cmd, strlen($cmd)))
         {
            $gather[] = $sock;
         } else
         {
            $this->_dead_sock($sock);
         }
      }
      
      // Parse responses
      $val = array();
      foreach ($gather as $sock)
      {
         $this->_load_items($sock, $val);
      }
      
      if ($this->_debug)
         foreach ($val as $k => $v)
            printf("MemCache: got %s => %s\r\n", $k, $v);
            
      return $val;
   }

   // }}}
   // {{{ incr()

   /**
    * Increments $key (optionally) by $amt
    *
    * @param   string   $key     Key to increment
    * @param   interger $amt     (optional) amount to increment
    *
    * @return  interger New key value?
    * @access  public
    */
   function incr ($key, $amt=1)
   {
      return $this->_incrdecr('incr', $key, $amt);
   }

   // }}}
   // {{{ replace()

   /**
    * Overwrites an existing value for key; only works if key is already set
    *
    * @param   string   $key     Key to set value as
    * @param   mixed    $value   Value to store
    * @param   interger $exp     (optional) Experiation time
    *
    * @return  boolean
    * @access  public
    */
   function replace ($key, $value, $exp=0)
   {
      return $this->_set('replace', $key, $value, $exp);
   }

   // }}}
   // {{{ run_command()

   /**
    * Passes through $cmd to the memcache server connected by $sock; returns 
    * output as an array (null array if no output)
    *
    * NOTE: due to a possible bug in how PHP reads while using fgets(), each
    *       line may not be terminated by a \r\n.  More specifically, my testing
    *       has shown that, on FreeBSD at least, each line is terminated only
    *       with a \n.  This is with the PHP flag auto_detect_line_endings set
    *       to falase (the default).
    *
    * @param   resource $sock    Socket to send command on
    * @param   string   $cmd     Command to run
    *
    * @return  array    Output array
    * @access  public
    */
   function run_command ($sock, $cmd)
   {
      if (!is_resource($sock))
         return array();
      
      if (!fwrite($sock, $cmd, strlen($cmd)))
         return array();
         
      while (true)
      {
         $res = fgets($sock);
         $ret[] = $res;
         if (preg_match('/^END/', $res))
            break;
         if (strlen($res) == 0)
            break;
      }
      return $ret;
   }

   // }}}
   // {{{ set()

   /**
    * Unconditionally sets a key to a given value in the memcache.  Returns true
    * if set successfully.
    *
    * @param   string   $key     Key to set value as
    * @param   mixed    $value   Value to set
    * @param   interger $exp     (optional) Experiation time
    *
    * @return  boolean  TRUE on success
    * @access  public
    */
   function set ($key, $value, $exp=0)
   {
      return $this->_set('set', $key, $value, $exp);
   }

   // }}}
   // {{{ set_compress_threshold()

   /**
    * Sets the compression threshold
    *
    * @param   interger $thresh  Threshold to compress if larger than
    *
    * @access  public
    */
   function set_compress_threshold ($thresh)
   {
      $this->_compress_threshold = $thresh;
   }

   // }}}
   // {{{ set_debug()

   /**
    * Sets the debug flag
    *
    * @param   boolean  $dbg     TRUE for debugging, FALSE otherwise
    *
    * @access  public
    *
    * @see     memcahced::memcached
    */
   function set_debug ($dbg)
   {
      $this->_debug = $dbg;
   }

   // }}}
   // {{{ set_servers()

   /**
    * Sets the server list to distribute key gets and puts between
    *
    * @param   array    $list    Array of servers to connect to
    *
    * @access  public
    *
    * @see     memcached::memcached()
    */
   function set_servers ($list)
   {
      $this->_servers = $list;
      $this->_active = count($list);
      $this->_buckets = null;
      $this->_bucketcount = 0;
      
      $this->_single_sock = null;
      if ($this->_active == 1)
         $this->_single_sock = $this->_servers[0];
   }

   // }}}
   // }}}
   // {{{ private methods
   // {{{ _close_sock()

   /**
    * Close the specified socket
    *
    * @param   string   $sock    Socket to close
    *
    * @access  private
    */
   function _close_sock ($sock)
   {
      $host = array_search($sock, $this->_cache_sock);
      fclose($this->_cache_sock[$host]);
      unset($this->_cache_sock[$host]);
   }

   // }}}
   // {{{ _connect_sock()

   /**
    * Connects $sock to $host, timing out after $timeout
    *
    * @param   interger $sock    Socket to connect
    * @param   string   $host    Host:IP to connect to
    * @param   float    $timeout (optional) Timeout value, defaults to 0.25s
    *
    * @return  boolean
    * @access  private
    */
   function _connect_sock (&$sock, $host, $timeout = 0.25)
   {
      list ($ip, $port) = explode(":", $host);
      if ($this->_persistant == 1)
      {
         $sock = @pfsockopen($ip, $port, $errno, $errstr, $timeout);
      } else
      {
         $sock = @fsockopen($ip, $port, $errno, $errstr, $timeout);
      }
      
      if (!$sock)
         return false;
      return true;
   }

   // }}}
   // {{{ _dead_sock()

   /**
    * Marks a host as dead until 30-40 seconds in the future
    *
    * @param   string   $sock    Socket to mark as dead
    *
    * @access  private
    */
   function _dead_sock ($sock)
   {
      $host = array_search($sock, $this->_cache_sock);
      list ($ip, $port) = explode(":", $host);
      $this->_host_dead[$ip] = time() + 30 + intval(rand(0, 10));
      $this->_host_dead[$host] = $this->_host_dead[$ip];
      unset($this->_cache_sock[$host]);
   }

   // }}}
   // {{{ get_sock()

   /**
    * get_sock
    *
    * @param   string   $key     Key to retrieve value for;
    *
    * @return  mixed    resource on success, false on failure
    * @access  private
    */
   function get_sock ($key)
   {
      if (!$this->_active)
         return false;

      if ($this->_single_sock !== null)
         return $this->sock_to_host($this->_single_sock);
      
      $hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
      
      if ($this->_buckets === null)
      {
         foreach ($this->_servers as $v)
         {
            if (is_array($v))
            {
               for ($i=0; $i<$v[1]; $i++)
                  $bu[] = $v[0];
            } else
            {
               $bu[] = $v;
            }
         }
         $this->_buckets = $bu;
         $this->_bucketcount = count($bu);
      }
      
      $realkey = is_array($key) ? $key[1] : $key;
      for ($tries = 0; $tries<20; $tries++)
      {
         $host = $this->_buckets[$hv % $this->_bucketcount];
         $sock = $this->sock_to_host($host);
         if (is_resource($sock))
            return $sock;
         $hv += $this->_hashfunc($tries . $realkey);
      }
      
      return false;
   }

   // }}}
   // {{{ _hashfunc()

   /**
    * Creates a hash interger based on the $key
    *
    * @param   string   $key     Key to hash
    *
    * @return  interger Hash value
    * @access  private
    */
   function _hashfunc ($key)
   {
      $hash = 0;
      for ($i=0; $i_active)
         return null;
         
      $sock = $this->get_sock($key);
      if (!is_resource($sock))
         return null;
         
      $key = is_array($key) ? $key[1] : $key;
      $this->stats[$cmd]++;
      if (!fwrite($sock, "$cmd $key $amt\r\n"))
         return $this->_dead_sock($sock);
         
      stream_set_timeout($sock, 1, 0);
      $line = fgets($sock);
      if (!preg_match('/^(\d+)/', $line, $match))
         return null;
      return $match[1];
   }

   // }}}
   // {{{ _load_items()

   /**
    * Load items into $ret from $sock
    *
    * @param   resource $sock    Socket to read from
    * @param   array    $ret     Returned values
    *
    * @access  private
    */
   function _load_items ($sock, &$ret)
   {
      while (1)
      {
         $decl = fgets($sock);
         if ($decl == "END\r\n")
         {
            return true;
         } elseif (preg_match('/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match))
         {
            list($rkey, $flags, $len) = array($match[1], $match[2], $match[3]);
            $bneed = $len+2;
            $offset = 0;
            
            while ($bneed > 0)
            {
               $data = fread($sock, $bneed);
               $n = strlen($data);
               if ($n == 0)
                  break;
               $offset += $n;
               $bneed -= $n;
               $ret[$rkey] .= $data;
            }
            
            if ($offset != $len+2)
            {
               // Something is borked!
               if ($this->_debug)
                  printf("Something is borked!  key %s expecting %d got %d length\n", $rkey, $len+2, $offset);

               unset($ret[$rkey]);
               $this->_close_sock($sock);
               return false;
            }
            
            $ret[$rkey] = rtrim($ret[$rkey]);

            if ($this->_have_zlib && $flags & MEMCACHE_COMPRESSED)
               $ret[$rkey] = gzuncompress($ret[$rkey]);

            if ($flags & MEMCACHE_SERIALIZED)
               $ret[$rkey] = unserialize($ret[$rkey]);

         } else 
         {
            if ($this->_debug)
               print("Error parsing memcached response\n");
            return 0;
         }
      }
   }

   // }}}
   // {{{ _set()

   /**
    * Performs the requested storage operation to the memcache server
    *
    * @param   string   $cmd     Command to perform
    * @param   string   $key     Key to act on
    * @param   mixed    $val     What we need to store
    * @param   interger $exp     When it should expire
    *
    * @return  boolean
    * @access  private
    */
   function _set ($cmd, $key, $val, $exp)
   {
      if (!$this->_active)
         return false;
         //get_sock就是去获取到memcached服务器连接
      $sock = $this->get_sock($key);
      if (!is_resource($sock))
         return false;
         
      $this->stats[$cmd]++;
      
      $flags = 0;
      
      if (!is_scalar($val))
      {
         $val = serialize($val);
         $flags |= MEMCACHE_SERIALIZED;
         if ($this->_debug)
            printf("client: serializing data as it is not scalar\n");
      }
      
      $len = strlen($val);
      
      if ($this->_have_zlib && $this->_compress_enable && 
          $this->_compress_threshold && $len >= $this->_compress_threshold)
      {
         $c_val = gzcompress($val, 9);
         $c_len = strlen($c_val);
         
         if ($c_len < $len*(1 - COMPRESS_SAVINGS))
         {
            if ($this->_debug)
               printf("client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len);
            $val = $c_val;
            $len = $c_len;
            $flags |= MEMCACHE_COMPRESSED;
         }
      }
      if (!fwrite($sock, "$cmd $key $flags $exp $len\r\n$val\r\n"))
         return $this->_dead_sock($sock);
         
      $line = trim(fgets($sock));
      
      if ($this->_debug)
      {
         if ($flags & MEMCACHE_COMPRESSED)
            $val = 'compressed data';
         printf("MemCache: %s %s => %s (%s)\n", $cmd, $key, $val, $line);
      }
      if ($line == "STORED")
         return true;
      return false;
   }

   // }}}
   // {{{ sock_to_host()


   /**
    * Returns the socket for the host
    *
    * @param   string   $host    Host:IP to get socket for
    *
    * @return  mixed    IO Stream or false
    * @access  private
    */
   function sock_to_host ($host)

   {
     
      if (isset($this->_cache_sock[$host]))
         return $this->_cache_sock[$host];
      
      $now = time();
      list ($ip, $port) = explode (":", $host);
      if (isset($this->_host_dead[$host]) && $this->_host_dead[$host] > $now ||
          isset($this->_host_dead[$ip]) && $this->_host_dead[$ip] > $now)
         return null;
         
      if (!$this->_connect_sock($sock, $host))
         return $this->_dead_sock($host);
         
      // Do not buffer writes
      stream_set_write_buffer($sock, 0);
      
      $this->_cache_sock[$host] = $sock;
      
      return $this->_cache_sock[$host];
   }

   // }}}
   // }}}
   // }}}
}

// }}}
?> 

你可能感兴趣的:(Memcache操作详解及分析)