PHP缓存技术:memcache函数详解

Memcache函数库是在PECL(PHP Extension Community Library)中,主要作用是搭建大容量的内存数据的临时存放区域,在分布式的时候作用体现的非常明显,否则不建议使用。+ `$ b, U3 k4 |9 f# A" E3 Y
memcache函数所有的方法列表如下:- Z) r- Q; b+ }6 n6 v$ y5 O
参考http://www.php.net/manual/zh/function.Memcache-add.php9 F0 j. H4 e- u2 ?! C) ?2 j
- Z" _+ S/ }* e- N

Memcache::add - 添加一个值,如果已经存在,则返回false" y' @  z! W3 A
Memcache::addServer - 添加一个可供使用的服务器地址
Memcache::close - 关闭一个Memcache对象" y4 z' A, f0 H9 Z, @8 A2 Z
Memcache::connect - 创建一个Memcache对象0 T% `  R: Q! c" x, U
memcache_debug - 控制调试功能1 [. O6 Y. j$ h% M0 J1 h2 _" d& r) ?
Memcache::decrement - 对保存的某个key中的值进行减法操作6 Y+ @" \7 H, [; F) F
Memcache::delete - 删除一个key值- m# U/ z/ e- M; k
Memcache::flush - 清除所有缓存的数据- |) f& ^% P1 N" N0 E* p- q; s+ t
Memcache::get - 获取一个key值
Memcache::getExtendedStats - 获取进程池中所有进程的运行系统统计
Memcache::getServerStatus - 获取运行服务器的参数$ P/ E0 c$ `& X" [- M
Memcache::getStats - 返回服务器的一些运行统计信息
Memcache::getVersion - 返回运行的Memcache的版本信息
Memcache::increment - 对保存的某个key中的值进行加法操作9 a' {2 C; D$ J" @* C- r# x) ], R
Memcache::pconnect - 创建一个Memcache的持久连接对象" ?* N5 D5 [5 G* G
Memcache::replace -对一个已有的key进行覆写操作7 Y  r3 M7 ^" _% R' Z
Memcache::set - 添加一个值,如果已经存在,则覆写; r: r+ C2 y) Q/ e3 I. ?  N
Memcache::setCompressThreshold - 对大于某一大小的数据进行压缩$ H* ^) K2 v9 L( R
Memcache::setServerParams - 在运行时修改服务器的参数4 |9 D) O& m8 f! s" v
+ s; i/ [/ R) |: q9 B9 D. _
Memcache::add用法

bool Memcache::add ( string $key , mixed $var [, int $flag [, int $expire ]] )

说明:  H1 Z5 p6 ~8 I) h
如果$key不存在的时候,使用这个函数来存储$var的值。功能相同的函数是memcache_add()。

参数:
$key :将要存储的键值。
$var :存储的值,字符型和整型会按原值保存,其他类型自动序列化以后保存。! C$ b; q) h! c  v+ Q8 f4 ^
$flag:是否用MEMCACHE_COMPRESSED来压缩存储的值,true表示压缩,false表示不压缩。
$expire:存储值的过期时间,如果为0表示不会过期,你可以用unix时间戳或者描述来表示从现在开始的时间,但是你在使用秒数表示的时候,不要超过2592000秒 (表示30天)。
: ~5 g/ V% ~% ^  M  F6 `) n) a
返回值:
如果成功则返回 TRUE,失败则返回 FALSE。如果$key值已经存在,则会返回FALSE。 其他情况下Memcache::add()的用法类似于Memcache::set()。( a9 [$ M% K+ y7 K$ P& g

例子:7 i4 ]4 K0 M: J

<?php

$memcache_obj = memcache_connect(”localhost”, 11211);
/* 面向过程API */
memcache_add($memcache_obj, ’var_key’, ’test variable’, false, 30);* m2 ]+ P: Q- \. ?
/* 面向对象API */& {3 n; |; E' z- N; Y( G
$memcache_obj->add(’var_key’, ’test variable’, false, 30);
?>

Memcache::addServer用法+ ~+ ?) e; x+ c7 I3 }$ w7 T3 F

bool Memcache::addServer ( string $host [, int $port [, bool $persistent [, int $weight [, int $timeout [, int $retry_interval [, bool $status [, callback $failure_callback ]]]]]]] )

说明:
添加一个可供使用的服务器地址到连接池中,连接用Memcache::addServer打开,脚本执行完后自动关闭,或者可以用Memcache::close()手动关闭。相同函数是memcache_add_server()。
当用这个方法的时候(相对于Memcache::connect()和 Memcache::pconnect()方法),网络连接只有等需要的时候才会建立,因此不会因为增加很多的服务器到连接池而增加系统负担,因为很多服务器可能没有使用。2 y+ |7 V+ n& I) ]
故障恢复会发生在这个方法执行的任何阶段,只要其他的服务器是正常的,这些连接请求的失败用户不会注意到。任何一种socket或者memcached服务器级的错误可以触发故障恢复。正常的客户端错误比如增加一个存在的键值不会引发故障恢复。, x5 b. Z' y. K  I# O. [

参数:( [4 ?# S3 O( `0 K
$host服务器的地址
$port服务器端口3 W2 q, y6 n+ j# i
$persistent是否是一个持久连接
$weight这台服务器在所有服务器中所占的权重: b! x3 S( d" {( \. u* N* ?8 h
$timeout连接的持续时间) @7 \7 D/ x  H( B/ ~4 d  e
$retry_interval连接重试的间隔时间,默认为15,设置为-1表示不进行重试4 j4 K( j: a7 K6 U4 n
$status控制服务器的在线状态& q( S. f- N- ^/ t6 I5 S2 M9 I2 c
$failure_callback允许设置一个回掉函数来处理错误信息。
9 D' @- j( q4 h# u% k
返回值:
如果成功则返回 TRUE,失败则返回 FALSE。

例子:

<?php

/* 面向对象API */
$memcache = new Memcache;
$memcache->addServer(’memcache_host’, 11211);
$memcache->addServer(’memcache_host2′, 11211);/ y# C5 o4 T2 a3 e
/* 面向过程 API */
$memcache_obj = memcache_connect(’memcache_host’, 11211);
memcache_add_server($memcache_obj, ’memcache_host2′, 11211);" X3 }5 }- _0 A. ~1 c
?>
0 a5 i% B, l6 w: g& f( y: [
- S; O0 V7 x. S9 H: x
Memcache::close用法: i$ k- K. M( I- l: x
bool Memcache::close ( void )% b9 B5 F4 r8 u$ B7 Z3 w
说明:
关闭memcache服务器连接。这个函数不会关闭长连接,长连接只有在web服务器关闭或者重启的时候才会关闭。相同的函数memcache_close()

返回值:" ?$ ]  {/ F) U1 w; f0 M
如果成功则返回 TRUE,失败则返回 FALSE。
+ _" t* N' c* Z, V8 L
例子:" w; ~+ k2 T: N6 B3 |

<?php

/* 面向过程 API */
$memcache_obj = memcache_connect(’memcache_host’, 11211);
/* . J  S: j( W/ V) W# s# B% M- E* p

执行一些代码..! N$ u6 r: }9 ]' ^
*/
memcache_close($memcache_obj);

/* 面向对象 API */+ U& o' d9 r; K! r
$memcache_obj = new Memcache;+ R+ p. E+ J, r; f: i
$memcache_obj->connect(’memcache_host’, 11211);
/*
memcache::debug# c2 w" _: Q+ W# C% O! @# @! I

bool memcache_debug ( bool $on_off )

* ^+ O3 W; g7 ^/ }$ v6 l$ d1 y

说明:
& e3 r6 f# M7 \! w) |" h6 w
控制调试功能,前提是php在编译的时候使用了-enable-debug选项,否则这个函数不会有作用。
9 R# p& ]8 Q+ w- U4 I( H

9 \2 u, w2 A9 q/ c  M
参数:
+ b7 N& y, A; k# F2 A  e8 j4 p
$on_off:true表示开启调试,false表示关闭调试


4 ]" ^  T& w+ ~
返回值:6 g+ _% o! I5 q. F
2 B9 c6 `& Y% v9 y# Z
如果php在编译的时候使用了-enable-debug选项,返回true,否则返回false+ _& v$ o1 T' H" I1 e


4 d  G9 @% j" X. F/ o
& z0 p& ^  \& ]7 X
+ e& D* M0 X  I5 n5 L9 G# z% f
Memcache::decrement用法

int Memcache::decrement ( string $key [, int $value ] )7 ~) o6 j, C6 Q5 Q1 M9 m
1 J* d. {6 k, z0 E
# [+ V* o* {/ h
) n; T# q* x9 ~* B; u' z7 ~; R4 s
说明:. D' E; T; K+ f( M! b" |
0 `4 C; r" k0 e3 ^) s( z2 R
Memcache::decremen方法的作用是对保存的某个key中的值进行减法操作,用法跟Memcache::increment类似。

你也可以用memcache_decrement()函数。# d/ V% }& X/ ~; O- b

6 s0 L+ _! ^9 O3 E: v, V
. ^0 k" r6 G& y+ K# a. j
参数:1 T* @2 [2 Y) I) t. N/ O
9 v/ y0 z; r* G+ D% W' C3 Z
Key:想要减少的键的名字! t1 u! S) \4 Z9 Z
1 ~6 y& M" m& ?& ]
Value:想要减少的值。

7 c& {) }( X) Q8 i2 f- n
3 s$ }1 ?0 U. Q6 y) k) i. r
返回值:4 {) M5 K+ f9 b! ~$ y

如果成功,返回被减少后的值,如果失败返回false。

; K) d- H! m  h4 Q' Q/ R2 ^& a; ~

例子:( o9 M& z/ }% [  R0 C9 O
) G1 {8 I% \- Q. c4 ?; y  ?
<?php
4 i* I" t. W, X: s
$memcache = new Memcache;4 G3 a- _( M; p; |! o

$memcache->connect(’localhost’, 11211);
4 L$ @' ]$ v6 \
$memcache->set(’test_item’, 8);# p" i* E4 j0 {! G0 W' c* N. Y+ x3 ]" n/ |
& G8 c( t0 [! f* h, ]
$memcache->increment(’test_item’, 4);

echo $memcache->decrement(’test_item’, 7);) Y. @6 q; Q2 D0 h0 G0 s

// 显示 5

?>. B$ O3 k2 X9 _1 Y  H8 B

这个例子连Memcache::increment函数都一块演示了。5 z" b7 _6 n$ i9 Q( J. P
/ [2 o. {+ U. a- E

$ y' ]0 t! {4 t) h; U8 S
Memcache::delete用法
, f3 }4 r5 {& o7 K' @
bool Memcache::delete ( string $key [, int $timeout ] )$ J7 s2 F9 O) x. G

/ B; C* z7 O5 x7 V# \' q
. y/ L) R* P6 R! n' Q- U
说明:

删除一个key值,如果参数$timeout被设置,那么存储的值会在设置的秒数以后过期,你也可以用函数memcache_delete()
. I4 n4 Z$ M, P

3 t( n* C8 s" f
返回值:
2 b: q7 U/ h4 E3 \3 F+ q  F4 Y
如果成功则返回 TRUE,失败则返回 FALSE。

% r/ S8 ~) r! C/ {$ H

例子:

<?php
2 E! L  P. W: u: x
/* 面向过程 API */5 p" V9 d( T6 {. |/ Z$ M- Q5 ]1 m
$memcache_obj = memcache_connect(’memcache_host’, 11211);$ y. |5 Q) l" s$ L4 {6 w, j; k% K
- T8 W) V- o2 g( G" I
/* 在10秒以后,缓存的值会被服务器删除 */
memcache_delete($memcache_obj, ’key_to_delete’, 10);5 d2 T. Y3 [% L# P2 E7 x
/ ~( C* J4 `% b' B2 c) \
/* 面向对象 API */
$memcache_obj = new Memcache;
$memcache_obj->connect(’memcache_host’, 11211);

$memcache_obj->delete(’key_to_delete’, 10);2 c2 W3 ]2 O: s* N2 d# Y
: X! ]3 w) A% p' t+ ^8 N
?>
' h! ]' v1 I4 J9 C

: S+ p2 E* p8 P


Memcache::flush

bool Memcache::flush ( void )

& R- z8 K) [. q( X$ @* T) |8 ^) s
) n# _3 r/ |. T; B( m2 V
说明:

清除所有缓存的数据。Memcache::flush实际上没有释放资源,它仅仅将所有的缓存标记为过期,这样可以使新的缓存来覆盖被占的内存空间。一样的函数是memcache_flush()7 o% Q) @% d6 y$ A* J& p


( c5 p2 l' f! C$ l# p% A
返回值:

如果成功则返回 TRUE,失败则返回 FALSE。4 w& O% |7 p. w7 p8 H9 V/ m; S& y+ n

6 y/ k3 S& G" R' m7 S4 s
$ r7 A, K5 g7 z- D2 y
例子:- S5 z$ `7 t6 a0 m

<?php

/* 面向过程 API */
$memcache_obj = memcache_connect(’memcache_host’, 11211);

memcache_flush($memcache_obj);! o; {8 I. j6 |4 m# b

/* 面向对象 API */

$memcache_obj = new Memcache;
$memcache_obj->connect(’memcache_host’, 11211);/ v9 A/ _$ R. Y' Q
+ E$ O6 k. _4 B4 d5 H
$memcache_obj->flush();

?>
+ I$ Q2 a* a4 Y$ Q, @
3 p8 j7 ]+ G. g3 i% a* h


1 o/ i, L' J' m" `9 F( d
Memcache::get# a+ O$ Q. W9 i6 x" J1 I
1 N* M. f! f- m) ?) r- ]0 k
string Memcache::get ( string $key [, int &$flags ] )
: j. w% w; N" ^! n+ p! w
array Memcache::get ( array $keys [, array &$flags ] )+ D( \! d% v( S! a: A/ \



说明:
1 l; h7 M7 {3 Q
方法的作用是获取一个key值,key值可以是一个数组,结果会包含键值对。: ?1 Q" J% L2 I. G! @" U6 g( Q
% w7 _2 J- p9 m: A. ?! N; P
: u0 `. }8 r; d, q- u: `

参数:, O. q1 m5 j/ w' F2 N
% [8 @0 L. W; t) h
$key是键值或者一个键的数组值。$ O# z" ~" v* G2 q4 T" J) `0 T$ r
, }2 Z% @8 d4 _9 `: q- n
$flags如果这个参数存在,那么$flags跟写入这个参数的值相关,这些$flags 类似于Memcache::set()函数里的$flags。



返回值:

如果成功,则返回key对应的值,如果失败则返回false.
( Z: w0 V4 ]1 F1 L* y/ I+ I


例子:; s" i$ k5 u# H! W) L( C7 N
" P- Z9 S- m$ |  E, ]
<?php* K; ]" K% h. d
2 W  m3 q, Q7 }, R
/* 面向过程 API */( c3 [- k5 U' W* q# ?$ W- f0 d
$memcache_obj = memcache_connect(’memcache_host’, 11211);
$var = memcache_get($memcache_obj, ’some_key’);( V, [- @7 u0 T1 P  g6 K
5 T& q- u2 C0 J& c4 X/ l+ a
/* 面向对象 API */9 |+ P$ U9 E9 y, I8 {, X+ \1 U6 g. q
$memcache_obj = new Memcache;
$memcache_obj->connect(’memcache_host’, 11211);( W7 M0 I& C, o" ~" `7 F0 ?
$var = $memcache_obj->get(’some_key’);
0 Y7 s& D$ e7 ~" q
/*
*  集群架构方面的问题
o memcached是怎么工作的?3 r3 }/ R+ V6 u" o
o memcached最大的优势是什么?
o memcached和MySQL的query cache相比,有什么优缺点?" D2 D2 l, F) S6 G
o memcached和服务器的local cache(比如PHP的APC、mmap文件等)相比,有什么优缺点?
o memcached的cache机制是怎样的?
o memcached如何实现冗余机制?       . c/ s! C1 k  }
o memcached如何处理容错的?
o 如何将memcached中item批量导入导出?
o 但是我确实需要把memcached中的item都dump出来,确实需要把数据load到memcached中,怎么办?
o memcached是如何做身份验证的?
o 如何使用memcached的多线程是什么?如何使用它们?
o memcached能接受的key的最大长度是多少?(250bytes)
o memcached对item的过期时间有什么限制?(为什么有30天的限制?)
o memcached最大能存储多大的单个item?(1M byte)
o 为什么单个item的大小被限制在1M byte之内?8 f  ^/ L& A8 `9 D" Y7 `0 I
o 为了让memcached更有效地使用服务器的内存,可以在各个服务器上配置大小不等的缓存空间吗?. q$ s, c9 A( V2 u" J) c
o 什么是binary协议?它值得关注吗?6 ~% v! r5 C0 o" f8 O: I
o memcached是如何分配内存的?为什么不用malloc/free!?究竟为什么使用slab呢?4 A7 Y/ z. |6 l3 W: M
o memcached能保证数据存储的原子性吗?
& j' M$ N( K1 }; K, K
集群架构方面的问题
  ?) P7 _% i# K5 H/ |0 t; p  Q
memcached是怎么工作的?
; c$ b6 N# B0 ~8 E
Memcached的神奇来自两阶段哈希(two-stage hash)。Memcached就像一个巨大的、存储了很多<key,value>对的哈希表。通过key,可以存储或查询任意的数据。# \; F0 R7 g" F* O' P

客户端可以把数据存储在多台memcached上。当查询数据时,客户端首先参考节点列表计算出key的哈希值(阶段一哈希),进而选中一个节点;客户端 将请求发送给选中的节点,然后memcached节点通过一个内部的哈希算法(阶段二哈希),查找真正的数据(item)。

举个列子,假设有3个客户端1, 2, 3,3台memcached A, B, C:
Client 1想把数据”barbaz”以key “foo”存储。Client 1首先参考节点列表(A, B, C),计算key “foo”的哈希值,假设memcached B被选中。接着,Client 1直接connect到memcached B,通过key “foo”把数据”barbaz”存储进去。Client 2使用与Client 1相同的客户端库(意味着阶段一的哈希算法相同),也拥有同样的memcached列表(A, B, C)。# I( A4 m+ j; p' P$ ^$ l
于是,经过相同的哈希计算(阶段一),Client 2计算出key “foo”在memcached B上,然后它直接请求memcached B,得到数据”barbaz”。
各种客户端在memcached中数据的存储形式是不同的(perl Storable, php serialize, java hibernate, JSON等)。一些客户端实现的哈希算法也不一样。但是,memcached服务器端的行为总是一致的。

最后,从实现的角度看,memcached是一个非阻塞的、基于事件的服务器程序。这种架构可以很好地解决C10K problem ,并具有极佳的可扩展性。

可以参考A Story of Caching ,这篇文章简单解释了客户端与memcached是如何交互的。1 E/ P+ V3 S1 J( F2 L
7 y  D* q- e0 z5 \' G  [9 t7 n
memcached最大的优势是什么?+ }8 Q/ F1 M% @' q  L, i; p
1 H' G/ \* H  J+ T2 `* C  N2 Q9 x7 n
请仔细阅读上面的问题(即memcached是如何工作的)。Memcached最大的好处就是它带来了极佳的水平可扩展性,特别是在一个巨大的系统中。 由于客户端自己做了一次哈希,那么我们很容易增加大量memcached到集群中。memcached之间没有相互通信,因此不会增加 memcached的负载;没有多播协议,不会网络通信量爆炸(implode)。memcached的集群很好用。内存不够了?增加几台 memcached吧;CPU不够用了?再增加几台吧;有多余的内存?在增加几台吧,不要浪费了。

基于memcached的基本原则,可以相当轻松地构建出不同类型的缓存架构。除了这篇FAQ,在其他地方很容易找到详细资料的。
% c% `, t2 i' S! X, n
看看下面的几个问题吧,它们在memcached、服务器的local cache和MySQL的query cache之间做了比较。这几个问题会让您有更全面的认识。% F6 o. p) G2 h6 ~6 @7 Y$ l5 ?
5 K* S* c! W! `) K
memcached和MySQL的query cache相比,有什么优缺点?

把 memcached引入应用中,还是需要不少工作量的。MySQL有个使用方便的query cache,可以自动地缓存SQL查询的结果,被缓存的SQL查询可以被反复地快速执行。Memcached与之相比,怎么样呢?MySQL的query cache是集中式的,连接到该query cache的MySQL服务器都会受益。
* 当您修改表时,MySQL的query cache会立刻被刷新(flush)。存储一个memcached item只需要很少的时间,但是当写操作很频繁时,MySQL的query cache会经常让所有缓存数据都失效。

* 在多核CPU上,MySQL的query cache会遇到扩展问题(scalability issues)。在多核CPU上,query cache会增加一个全局锁(global lock), 由于需要刷新更多的缓存数据,速度会变得更慢。$ i5 y# i' h( c' a' \; z0 }: G

* 在MySQL的query cache中,我们是不能存储任意的数据的(只能是SQL查询结果)。而利用memcached,我们可以搭建出各种高效的缓存。比如,可以执行多个独立 的查询,构建出一个用户对象(user object),然后将用户对象缓存到memcached中。而query cache是SQL语句级别的,不可能做到这一点。在小的网站中,query cache会有所帮助,但随着网站规模的增加,query cache的弊将大于利。" v7 Y  h, t; E+ P* S# S2 I
) C% G( X$ j" y% E& c# l; b. I1 Y
* query cache能够利用的内存容量受到MySQL服务器空闲内存空间的限制。给数据库服务器增加更多的内存来缓存数据,固然是很好的。但是,有了 memcached,只要您有空闲的内存,都可以用来增加memcached集群的规模,然后您就可以缓存更多的数据。
, o! U' H' {# Y: H  \- [& z
memcached和服务器的local cache(比如PHP的APC、mmap文件等)相比,有什么优缺点?2 Q9 ^4 ]# Q5 t4 L

首先,local cache有许多与上面(query cache)相同的问题。local cache能够利用的内存容量受到(单台)服务器空闲内存空间的限制。不过,local cache有一点比memcached和query cache都要好,那就是它不但可以存储任意的数据,而且没有网络存取的延迟。
* X8 B- b: K8 J
* local cache的数据查询更快。考虑把highly common的数据放在local cache中吧。如果每个页面都需要加载一些数量较少的数据,考虑把它们放在local cached吧。3 ^' u# e  M' k+ ^7 E: {

* local cache缺少集体失效(group invalidation)的特性。在memcached集群中,删除或更新一个key会让所有的观察者觉察到。但是在local cache中, 我们只能通知所有的服务器刷新cache(很慢,不具扩展性),或者仅仅依赖缓存超时失效机制。

* local cache面临着严重的内存限制,这一点上面已经提到。  n; Q- G$ U$ Q: P, P) n( f

memcached的cache机制是怎样的?

Memcached主要的cache机制是LRU(最近最少用)算法+超时失效。当您存数据到memcached中,可以指定该数据在缓存中可以呆多久 Which is forever, or some time in the future。如果memcached的内存不够用了,过期的slabs会优先被替换,接着就轮到最老的未被使用的slabs。) \# G: _9 C9 F! T
- c5 {* Y  b5 p& _
memcached如何实现冗余机制?
不实现!我们对这个问题感到很惊讶。Memcached应该是应用的缓存层。它的设计本身就不带有任何冗余机制。如果一个memcached节点失去了所 有数据,您应该可以从数据源(比如数据库)再次获取到数据。您应该特别注意,您的应用应该可以容忍节点的失效。不要写一些糟糕的查询代码,寄希望于 memcached来保证一切!如果您担心节点失效会大大加重数据库的负担,那么您可以采取一些办法。比如您可以增加更多的节点(来减少丢失一个节点的影 响),热备节点(在其他节点down了的时候接管IP),等等。

memcached如何处理容错的?  _( x' `2 ]. \' r4 o" X1 o
不处理!:) 在memcached节点失效的情况下,集群没有必要做任何容错处理。如果发生了节点失效,应对的措施完全取决于用户。节点失效时,下面列出几种方案供您选择:2 n$ F' v) l) s; R" B/ u# |+ i

* 忽略它! 在失效节点被恢复或替换之前,还有很多其他节点可以应对节点失效带来的影响。
7 ^+ }6 e1 M, J( Y6 |
* 把失效的节点从节点列表中移除。做这个操作千万要小心!在默认情况下(余数式哈希算法),客户端添加或移除节点,会导致所有的缓存数据不可用!因为哈希参照的节点列表变化了,大部分key会因为哈希值的改变而被映射到(与原来)不同的节点上。; C" `  Z+ w/ _+ G* X

* 启动热备节点,接管失效节点所占用的IP。这样可以防止哈希紊乱(hashing chaos)。; }* e- N5 M: ]/ S0 H- E2 \! Y' h

* 如果希望添加和移除节点,而不影响原先的哈希结果,可以使用一致性哈希算法(consistent hashing)。您可以百度一下一致性哈希算法。支持一致性哈希的客户端已经很成熟,而且被广泛使用。去尝试一下吧!2 ?* q$ a: U8 `- [2 ]+ @4 B
0 o: I' X, U# J9 p  X
* 两次哈希(reshing)。当客户端存取数据时,如果发现一个节点down了,就再做一次哈希(哈希算法与前一次不同),重新选择另一个节点(需要注意 的时,客户端并没有把down的节点从节点列表中移除,下次还是有可能先哈希到它)。如果某个节点时好时坏,两次哈希的方法就有风险了,好的节点和坏的节 点上都可能存在脏数据(stale data)。9 G- e( j2 }: }  ]
5 j1 o1 E# ~) u2 I& c) N
如何将memcached中item批量导入导出?, M" I' X1 j* F
4 S$ X& C! @$ [- A% {: t, ]6 X2 r
您不应该这样做!Memcached是一个非阻塞的服务器。任何可能导致memcached暂停或瞬时拒绝服务的操作都应该值得深思熟虑。向 memcached中批量导入数据往往不是您真正想要的!想象看,如果缓存数据在导出导入之间发生了变化,您就需要处理脏数据了;如果缓存数据在导出导入 之间过期了,您又怎么处理这些数据呢?5 |. F5 L$ C) m
0 [4 F# O: V( F2 p& N" W( j! W- A# d
因此,批量导出导入数据并不像您想象中的那么有用。不过在一个场景倒是很有用。如果您有大量的从不变化 的数据,并且希望缓存很快热(warm)起来,批量导入缓存数据是很有帮助的。虽然这个场景并不典型,但却经常发生,因此我们会考虑在将来实现批量导出导入的功能。

Steven Grimm,一如既往地,,在邮件列表中给出了另一个很好的例子:http://lists.danga.com/pipermail/memcached/2007-July/004802.html

但是我确实需要把memcached中的item批量导出导入,怎么办??

好吧好吧。如果您需要批量导出导入,最可能的原因一般是重新生成缓存数据需要消耗很长的时间,或者数据库坏了让您饱受痛苦。
% d1 b' D4 W* k7 {* L! _7 {) _2 Y
如果一个memcached节点down了让您很痛苦,那么您还会陷入其他很多麻烦。您的系统太脆弱了。您需要做一些优化工作。比如处理”惊群”问题(比 如 memcached节点都失效了,反复的查询让您的数据库不堪重负…这个问题在FAQ的其他提到过),或者优化不好的查询。记住,Memcached 并不是您逃避优化查询的借口。
. F) r8 Y# w6 U% I; d! G, G
如果您的麻烦仅仅是重新生成缓存数据需要消耗很长时间(15秒到超过5分钟),您可以考虑重新使用数据库。这里给出一些提示:+ _) Y- X3 _  X

* 使用MogileFS(或者CouchDB等类似的软件)在存储item。把item计算出来并dump到磁盘上。 MogileFS可以很方便地覆写item,并提供快速地访问。.您甚至可以把MogileFS中的item缓存在memcached中,这样可以加快读 取速度。 MogileFS+Memcached的组合可以加快缓存不命中时的响应速度,提高网站的可用性。& W7 m7 s7 G5 Q# T& r
* 重新使用MySQL。 MySQL的 InnoDB主键查询的速度非常快。如果大部分缓存数据都可以放到VARCHAR字段中,那么主键查询的性能将更好。从memcached中按key查询 几乎等价于MySQL的主键查询:将key 哈希到64-bit的整数,然后将数据存储到MySQL中。您可以把原始(不做哈希)的key存储都普通的字段中,然后建立二级索引来加快查询…key被 动地失效,批量删除失效的key,等等。( q, E1 y% x  m3 {- }

上面的方法都可以引入memcached,在重启memcached的时候仍然提供很好的性能。由于您不需要当心”hot”的item被 memcached LRU算法突然淘汰,用户再也不用花几分钟来等待重新生成缓存数据(当缓存数据突然从内存中消失时),因此上面的方法可以全面提高性能。
( i4 U9 E8 ^/ e' k3 P
关于这些方法的细节,详见博客:http://dormando.livejournal.com/495593.html

memcached是如何做身份验证的?' J  t" D  g# Q7 k; r1 B3 X3 t; s6 m
没有身份认证机制!memcached是运行在应用下层的软件(身份验证应该是应用上层的职责)。memcached的客户端和服务器端之所以是轻量级的,部分原因就是完全没有实现身份验证机制。这样,memcached可以很快地创建新连接,服务器端也无需任何配置。

如果您希望限制访问,您可以使用防火墙,或者让memcached监听unix domain socket。

memcached的多线程是什么?如何使用它们?
线 程就是定律(threads rule)!在Steven Grimm和Facebook的努力下,memcached 1.2及更高版本拥有了多线程模式。多线程模式允许memcached能够充分利用多个CPU,并在CPU之间共享所有的缓存数据。memcached使 用一种简单的锁机制来保证数据更新操作的互斥。相比在同一个物理机器上运行多个memcached实例,这种方式能够更有效地处理multi gets。

如果您的系统负载并不重,也许您不需要启用多线程工作模式。如果您在运行一个拥有大规模硬件的、庞大的网站,您将会看到多线程的好处。: K' f. R# L# ~8 j( U
: w2 r" N& e; L/ v: T5 I  b
更多信息请参见:http://code.sixapart.com/svn/mem ... ver/doc/threads.txt4 D. t( K+ U2 I8 Y) `1 L

简单地总结一下:命令解析(memcached在这里花了大部分时间)可以运行在多线程模式下。memcached内部对数据的操作是基于很多全局锁的 (因此这部分工作不是多线程的)。未来对多线程模式的改进,将移除大量的全局锁,提高memcached在负载极高的场景下的性能。. \1 U% T& T% w4 e
) z  U1 T# x, ~1 [) }$ v
memcached能接受的key的最大长度是多少?5 M0 X/ `4 I5 g' W1 f
key 的最大长度是250个字符。需要注意的是,250是memcached服务器端内部的限制,如果您使用的客户端支持”key的前缀”或类似特性,那么 key(前缀+原始key)的最大长度是可以超过250个字符的。我们推荐使用使用较短的key,因为可以节省内存和带宽。
; [6 C( y- n. O2 I
memcached对item的过期时间有什么限制?
过期时间最大可以达到30天。memcached把传入的过期时间(时间段)解释成时间点后,一旦到了这个时间点,memcached就把item置为失效状态。这是一个简单但obscure的机制。

memcached最大能存储多大的单个item?) M& \' D( f$ o/ i+ q6 U
1MB。如果你的数据大于1MB,可以考虑在客户端压缩或拆分到多个key中。) T% a2 h! U0 \- ?

为什么单个item的大小被限制在1M byte之内?
啊…这是一个大家经常问的问题!, b6 D+ n! u  x8 N; P8 y, O
+ E' o  `+ O& m8 W
简单的回答:因为内存分配器的算法就是这样的。

详 细的回答:Memcached的内存存储引擎(引擎将来可插拔…),使用slabs来管理内存。内存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每个slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小 依次从一个最小数开始,按某个因子增长,直到达到最大的可能值。

如果最小值为400B,最大值是1MB,因子是1.20,各个slab的chunk的大小依次是:slab1 - 400B slab2 - 480B slab3 - 576B …
4 V* J2 `5 M) H6 r  Q
slab中chunk越大,它和前面的slab之间的间隙就越大。因此,最大值越大,内存利用率越低。Memcached必须为每个slab预先分配内存,因此如果设置了较小的因子和较大的最大值,会需要更多的内存。

还有其他原因使得您不要这样向memcached中存取很大的数据…不要尝试把巨大的网页放到mencached中。把这样大的数据结构load和unpack到内存中需要花费很长的时间,从而导致您的网站性能反而不好。

如果您确实需要存储大于1MB的数据,你可以修改slabs.c:POWER_BLOCK的值,然后重新编译memcached;或者使用低效的malloc/free。其他的建议包括数据库、MogileFS等。

我可以在不同的memcached节点上使用大小不等的缓存空间吗?这么做之后,memcached能够更有效地使用内存吗?6 p* K7 E) \" O6 ~8 {' {' M: q2 b% t
Memcache 客户端仅根据哈希算法来决定将某个key存储在哪个节点上,而不考虑节点的内存大小。因此,您可以在不同的节点上使用大小不等的缓存。但是一般都是这样做 的:拥有较多内存的节点上可以运行多个memcached实例,每个实例使用的内存跟其他节点上的实例相同。) ?) }+ Q3 E5 g" M8 B+ A9 G7 x9 n4 Y

什么是二进制协议,我该关注吗?$ m4 L7 F- }& l7 h- `* y
关于二进制最好的信息当然是二进制协议规范:http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol( b. X" J4 L! y) \7 i# ]

二进制协议尝试为端提供一个更有效的、可靠的协议,减少客户端/服务器端因处理协议而产生的CPU时间。5 n6 P9 M2 F/ [9 `- ?4 H% f
根据Facebook的测试,解析ASCII协议是memcached中消耗CPU时间最多的环节。所以,我们为什么不改进ASCII协议呢?1 K: l9 |/ f: w' A" e, }! ^' n

在这个邮件列表的thread中可以找到一些旧的信息:http://lists.danga.com/pipermail/memcached/2007-July/004636.html8 S; p# I4 Q; O; W# o3 r* W
+ U  H3 I& B+ l
memcached的内存分配器是如何工作的?为什么不适用malloc/free!?为何要使用slabs?8 p, \9 x& E, c$ H8 V8 T6 e% o
实际上,这是一个编译时选项。默认会使用内部的slab分配器。您确实确实应该使用内建的slab分配器。最早的时候,memcached只使用 malloc/free来管理内存。然而,这种方式不能与OS的内存管理以前很好地工作。反复地malloc/free造成了内存碎片,OS最终花费大量 的时间去查找连续的内存块来满足malloc的请求,而不是运行memcached进程。如果您不同意,当然可以使用malloc!只是不要在邮件列表中 抱怨啊:)  z% n8 Z- z. C/ i% [% u
* V+ W- T; W. f. u
slab分配器就是为了解决这个问题而生的。内存被分配并划分成chunks,一直被重复使用。因为内存被划分成大小不等的 slabs,如果item的大小与被选择存放它的slab不是很合适的话,就会浪费一些内存。Steven Grimm正在这方面已经做出了有效的改进。
( x, r0 f$ ?  w
邮件列表中有一些关于slab的改进(power of n 还是 power of 2)和权衡方案:http://lists.danga.com/pipermail/memcached/2006-May/002163.html http://lists.danga.com/pipermail/memcached/2007-March/003753.html/ I  H; {" ?% L+ A. _

如果您想使用malloc/free,看看它们工作地怎么样,您可以在构建过程中定义USE_SYSTEM_MALLOC。这个特性没有经过很好的测试,所以太不可能得到开发者的支持。2 f: `/ _) C( g& y& W+ j
8 E  n2 S* M9 o: J0 r
更多信息:http://code.sixapart.com/svn/mem ... mory_management.txt

8 B8 F/ d9 B( H" m7 e4 m1 w7 y/ \
memcached是原子的吗?
当然!好吧,让我们来明确一下:
所有的被发送到memcached的单个命令是完全原子的。如果您针对同一份数据同时发送了一个set命令和一个get命令,它们不会影响对方。它们将被串行化、先后执行。即使在多线程模式,所有的命令都是原子的,除非程序有bug:)( {8 Z% y7 C( s& e3 U* }) M) E. S
命令序列不是原子的。如果您通过get命令获取了一个item,修改了它,然后想把它set回memcached,我们不保证这个item没有被其他进程(process,未必是操作系统中的进程)操作过。在并发的情况下,您也可能覆写了一个被其他进程set的item。4 W; O. G" S) @  x& A: F& @6 B
5 g5 K2 V& s! ]7 k" i! [8 [
memcached 1.2.5以及更高版本,提供了gets和cas命令,它们可以解决上面的问题。如果您使用gets命令查询某个key的item,memcached会 给您返回该item当前值的唯一标识。如果您覆写了这个item并想把它写回到memcached中,您可以通过cas命令把那个唯一标识一起发送给 memcached。如果该item存放在memcached中的唯一标识与您提供的一致,您的写操作将会成功。如果另一个进程在这期间也修改了这个 item,那么该item存放在memcached中的唯一标识将会改变,您的写操作就会失败。* b& D, m9 J  ^3 _4 s) p
( o) ~3 D, B2 o; S6 E
通常,基于memcached中item的值来修改item,是一件棘手的事情。除非您很清楚自己在做什么,否则请不要做这样的事情。

你可能感兴趣的:(memcache)