naotang的测试成长空间,记录工作中的问题,学习中的心得。 个人网站:www.naotang.com

Memcached了解

上一篇 / 下一篇  2007-11-16 12:44:50 / 个人分类:Web测试

51Testing软件测试网M}*{.JH*_/_

51Testing软件测试网*y3w N;Y!mpo`

51Testing软件测试网4X(S;v|]D

Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能51Testing软件测试网BW S,W9H^Aj$Cx

a] B`xc zuO2m01、 Memcached是什么

F ]'W9bAl-P t0很多人把它当作和SharedMemory那种形式的存储载体来使用,虽然memcached使用了同样的“Key=>Value”方式组织数据,但是它和共享内存、APC(Alternative PHP Cache)等本地缓存有非常大的区别。Memcached是分布式的,也就是说它不是本地的。它基于网络连接(当然它也可以使用localhost)方式完成服务,本身它是一个独立于应用的程序或守护进程(Daemon方式)。51Testing软件测试网hI#{~Oh{Y1N}

XK%]mM%\0Memcached使用libevent库实现网络连接服务,理论上可以处理无限多的连接,但是它和Apache不同,它更多的时候是面向稳定的持续连接的,所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200,这和Linux线程能力有关系,这个数值是可以调整的。Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的,memcachd有自己的内存分配算法和管理方式,它和共享内存没有关系,也没有共享内存的限制,通常情况下,每个memcached进程可以管理2GB的内存空间,如果需要更多的空间,可以增加进程数。

4t ]FrT};M Ia0

Vt(T[Wl02、 Memcached适合什么场合

$z ]'n TxT'q3\#o0

"^Rfhs)u'Y8S0memcached不是万能的,它也不是适用在所有场合。51Testing软件测试网|nZ2A{;WF

51Testing软件测试网vj(]/de f,M1g5e H

Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应用,memcached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源,即使是UNIX本地连接也一样。 在我之前的测试数据中显示,memcached本地读写速度要比直接PHP内存数组慢几十倍,而APC、共享内存方式都和直接数组差不多。可见,如果只是本地级缓存,使用memcached是非常不划算的。51Testing软件测试网.N5` Q`Rt

+I \%h0k ^'H'LF0Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销,而且它是使用内存来管理数据的,所以它可以提供比直接读取数据库更好的性能,在大型系统中,访问同样的数据是很频繁的,memcached可以大大降低数据库压力,使系统执行效率提升。另外,memcached也经常作为服务器之间数据共享的存储媒介,例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached中,被多个应用共享。51Testing软件测试网o_yB)Z(X

gD*wf-G0需要注意的是,memcached使用内存管理数据,所以它是易失的,当服务器重启,或者memcached进程中止,数据便会丢失,所以memcached不能用来持久保存数据。很多人的错误理解,memcached的性能非常好,好到了内存和硬盘的对比程度,其实memcached使用内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了,memcached进程本身并不占用多少CPU资源的情况。51Testing软件测试网Z'[BN RC

51Testing软件测试网bTs6I ?4V-d C7K9_6z

51Testing软件测试网/R8rfr5sG%a6V

3、 Memcached的工作机制

Yl;_4f6F*Hd0

o2aa7z.HYwRF s0通过在内存中开辟一块区域来维持一个大的hash表来加快页面访问速度,和数据库是独立的。允许多个server通过网络形成一个大的hash,用户不必关心数据存放在哪,只调用相关接口就可以了。存放在内存的数据通过LRU算法进行淘汰出内存。同时可以通过删除和设置失效时间来淘汰存放在内存的数据。 【注:LRU(Least recently used最近最少算法):当内存的剩余的可用空间不够时,缓冲区尽可能的先保留使用者最常使用的数据,换句话说就是优先清除较不常使用的数据,并释放其空间】

-VJ^Ya051Testing软件测试网e i9T,e#p_\/{

4、 Memcached的工作方式51Testing软件测试网j3X Fe.{0k|

首先,memcached是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端API包括Perl/PHP/Python/Ruby/Java/C#/C等等。PHP等客户端在与memcached服务建立连接之后,开始存取对象(如图Fig 2),每个被存取的对象都有一个唯一的标志符key,存取操作均通过这个key进行,保存到memcached中的对象实际上是放置在内存中的,并不是保存在cache文件中的,这也是为什么memcached能够如此高效快捷的原因。注意,memcached使用内存管理数据,所以它是易失的,当服务器重启,或者memcached进程中止,数据便会消失,所以memcached不能用来长久保存数据。51Testing软件测试网k_0OFUG^\ L-xE

;t7S yT,y7T:xS/mAF)T`&f0用户访问网页时,查看memcached中是否有当前用户的session数据,使用session_id()做为唯一标识符;如果数据存在,则直接返回,如果不存在,再进行数据库连接,获取session数据,并将此数据保存到memcached中,供下次使用。

-v3@ z$]dI;u(f0

,vfl6}'s)pT\051Testing软件测试网X+ZBo*d m

以下的部分中,读者最好能准备一份memcached的源代码。
]!lqD&U;Hj#c0Memcached是传统的网络服务程序,如果启动的时候使用了-d参数,它会以守护进程的方式执行。创建守护进程由daemon.c完成,这个程序只有一个daemon函数,这个函数很简单(如无特殊说明,代码以1.2.1为准):51Testing软件测试网2@qcB/}}3d

51Testing软件测试网3T;GXjY

51Testing软件测试网)C]?4g7e6YG

CODE:
B6\n];~:tW0#include51Testing软件测试网vh.sBW
#include
8`,`n2]5Nb V8W0#include51Testing软件测试网^S q.]2B

51Testing软件测试网Tg'CpHZ3xPc

int
8]2Kw)c&V3Q.{g,V0daemon(nochdir, noclose)51Testing软件测试网@BQ-x`q
    int nochdir, noclose;51Testing软件测试网~n? E#uT
{51Testing软件测试网#c3@j*o_*x_:lf6j
    int fd;51Testing软件测试网:N[d0I"o
    switch (fork()) {51Testing软件测试网-OHs$Lt`(Ev}
    case -1:
9m+? xMq FW7r5z0        return (-1);
`+U5`S:m^]0    case 0:51Testing软件测试网5T.}z,z-w1z cr5O%C
        break;  51Testing软件测试网gAq#o$f%bqo_D'DIr
    default:
d2\2Qk9R\0        _exit(0);51Testing软件测试网qun3k^!s `
    }51Testing软件测试网7V2A3feAz'd
    if (setsid() == -1)51Testing软件测试网o$nzx#GpY"Z
        return (-1);
o'B5s@2Hn5c N0    if (!nochdir)
%n_9?IF[3Z0        (void)chdir("/");
w+D2Ml {0GmIE@0    if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {51Testing软件测试网DC*rL(f T0\/l
        (void)dup2(fd, STDIN_FILENO);
|g7n-},b u0        (void)dup2(fd, STDOUT_FILENO);
Mh"y@x/~0        (void)dup2(fd, STDERR_FILENO);51Testing软件测试网fCv2a8P-v7{3b"KY
        if (fd > STDERR_FILENO)
D)y\UQD0            (void)close(fd);
7v+n Z%lPB/Q0    }51Testing软件测试网B,T,we,DTV*IC)}z
    return (0);51Testing软件测试网 }9IUB@!i.@Fn
}51Testing软件测试网Y4q\oL'l

_ YBPts0这个函数 fork 了整个进程之后,父进程就退出,接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备, daemon 就建立成功了。
yY.E3U)~ Lnl2f;p0Memcached 本身的启动过程,在 memcached.c 的 main 函数中顺序如下:51Testing软件测试网S~1@x[#o
1 、调用 settings_init() 设定初始化参数
y L NN:m4toM/{02 、从启动命令中读取参数来设置 setting 值
.rH0n q&n'v03 、设定 LIMIT 参数
!V3H~L t&T:Qz{04 、开始网络 socket 监听(如果非 socketpath 存在)( 1.2 之后支持 UDP 方式)51Testing软件测试网 Y%k1d7VP o%P2Ah
5 、检查用户身份( Memcached 不允许 root 身份启动)
^|L7Yo@:FI)Ba06 、如果有 socketpath 存在,开启 UNIX 本地连接(Sock 管道)51Testing软件测试网n @}*g"R F-y l}|+l
7 、如果以 -d 方式启动,创建守护进程(如上调用 daemon 函数)
^K O Y@;pg08 、初始化 item 、 event 、状态信息、 hash 、连接、 slab51Testing软件测试网~(_%s6?9L
9 、如设置中 managed 生效,创建 bucket 数组51Testing软件测试网 j,l1a/P)L;[.`&h
10 、检查是否需要锁定内存页
_!^#xG6?mD)M `011 、初始化信号、连接、删除队列51Testing软件测试网gs5c8dv,H
12 、如果 daemon 方式,处理进程 ID51Testing软件测试网mo)Yq,C,Z
13 、event 开始,启动过程结束, main 函数进入循环。51Testing软件测试网 @+]X,j%H&q#{ R
在 daemon 方式中,因为 stderr 已经被定向到黑洞,所以不会反馈执行中的可见错误信息。
SQdX6WAS j6m|r8z YF0
9HW.Yc!l Y-|+D+f"Y@0memcached.c 的主循环函数是 drive_machine ,传入参数是指向当前的连接的结构指针,根据 state 成员的状态来决定动作。51Testing软件测试网6lL;T x]^5T9e"@5r`
Memcached 使用一套自定义的协议完成数据交换,它的 protocol 文档可以参考: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
;C/}oQWfR1S0在API中,换行符号统一为\r\n

)\ _pLT}-}P;b0

5n+^T!]0`X,{051Testing软件测试网 LMn8w:R8OM

5、 Memcached的内存管理方式

1Yb\&DS)F7h0Memcached有一个很有特色的内存管理方式,为了提高效率,它使用预申请和分组的方式管理内存空间,而并不是每次需要写入数据的时候去malloc,删除数据的时候free一个指针。Memcached使用slab->chunk的组织方式管理内存。51Testing软件测试网i[1E NQ4c J

j"`%Ii^l8d(im01.1和1.2的slabs.c中的slab空间划分算法有一些不同,后面会分别介绍。

2^D6A?'[w@A@|i$A0

[q%_p6xd0Slab可以理解为一个内存块,一个slab是memcached一次申请内存的最小单位,在memcached中,一个slab的大小默认为1048576字节(1MB),所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk,每个chunk里保存一个item,每个item同时包含了item结构体、key和value(注意在memcached中的value是只有字符串的)。slab按照自己的id分别组成链表,这些链表又按id挂在一个slabclass数组上,整个结构看起来有点像二维数组。slabclass的长度在1.1中是21,在1.2中是200。51Testing软件测试网5ZOXd+ThrL

51Testing软件测试网2CI\r6}Bn

slab有一个初始chunk大小,1.1中是1字节,1.2中是80字节,1.2中有一个factor值,默认为1.2551Testing软件测试网Dg8M2r.uR!rA.b

1T5],[%N"e_0在1.1中,chunk大小表示为初始大小*2^n,n为classid,即:id为0的slab,每chunk大小1字节,id为1的slab,每chunk大小2字节,id为2的slab,每chunk大小4字节……id为20的slab,每chunk大小为1MB,就是说id为20的slab里只有一个chunk:51Testing软件测试网@9[[8h|w(|/X~

$h1p+Ui ZL[[ MW0CODE:

#VDh@F.K:@:Tz051Testing软件测试网$?'n`3lAn

void slabs_init(size_t limit) {51Testing软件测试网4{9O?0Q8N uN0eF ieH
    int i;51Testing软件测试网+Oa X-h:@$h7?k$i Z!uL
    int size=1;

Z I!`8|.c0

1i(VEF V c T0mem_limit = limit;51Testing软件测试网VwHd$h$Rj} S
    for(i=0; i<=POWER_LARGEST; i++, size*=2) {51Testing软件测试网2F:f4R3N'["}!z/[[2u
        slabclass[i].size = size;51Testing软件测试网{[ m1Uc.U#Q
        slabclass[i].perslab = POWER_BLOCK / size;
n!N*zp Wd-z0        slabclass[i].slots = 0;
tM!oW]K6?j*S0        slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;51Testing软件测试网+?&w`9R1@x!Z5R;rD5gq |
        slabclass[i].end_page_ptr = 0;
w'{+``(\\2ca%D0        slabclass[i].end_page_free = 0;51Testing软件测试网G7J5R/^z
        slabclass[i].slab_list = 0;51Testing软件测试网(|L9QIJL
        slabclass[i].list_size = 0;51Testing软件测试网1g_a'b FS0y6_Q
        slabclass[i].killing = 0;51Testing软件测试网;V$a O:BXs9o8H
    }
2V9F%}HlI0    /* for the test suite:  faking of how much we've already malloc'd */51Testing软件测试网B*\)l b m e)V9T?g
    {
Wz'^H-[4i d0        char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
_b o;j#v8o`8zpB0        if (t_initial_malloc) {51Testing软件测试网lku"l.J
            mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC"));
$Olo(Q6T+K0        }
1~L4Q? p0    }
s7? KBq#Y0    /* pre-allocate slabs by default, unless the environment variable
0n7i2cJEc0       for testing is set to something non-zero */
;G*?,qN_"_o~p2o0    {51Testing软件测试网|R8J|@'A:Nq
        char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
,|i7r C3~U:Lq(Nh6T0        if (!pre_alloc || atoi(pre_alloc)) {
^7tv(~9t1j3K0            slabs_preallocate(limit / POWER_BLOCK);
1O H7i3GjO#}/b0        }51Testing软件测试网p LP;Q5FO
    }51Testing软件测试网 x lZAq
}51Testing软件测试网wbqMT9?` BF

;A)F$`Jf.s0在1.2中,chunk大小表示为初始大小*f^n,f为factor,在memcached.c中定义,n为classid,同时,201个头不是全部都要初始化的,因为factor可变,初始化只循环到计算出的大小达到slab大小的一半为止,而且它是从id1开始的,即:id为1的slab,每chunk大小80字节,id为2的slab,每chunk大小80*f,id为3的slab,每chunk大小80*f^2,初始化大小有一个修正值CHUNK_ALIGN_BYTES,用来保证n-byte排列 (保证结果是CHUNK_ALIGN_BYTES的整倍数)。这样,在标准情况下,memcached1.2会初始化到id40,这个slab中每个chunk大小为504692,每个slab中有两个chunk。最后,slab_init函数会在最后补足一个id41,它是整块的,也就是这个slab中只有一个1MB大的chunk:51Testing软件测试网/b_Q(O{qC`'as

51Testing软件测试网9M D2@6M[(F

CODE:51Testing软件测试网 fMD%{ yms2\

,o#B0q^1l9u#oi] H0void slabs_init(size_t limit, double factor) {51Testing软件测试网S2`}y)yr/kR:C(Z
    int i = POWER_SMALLEST - 1;
Q1pxuJ!VO+?_G+J0    unsigned int size = sizeof(item) + settings.chunk_size;51Testing软件测试网h;NhZ+R+x`
    /* Factor of 2.0 means use the default memcached behavīor */51Testing软件测试网*]2n:@4N7Tgz\/m
    if (factor == 2.0 && size < 128)51Testing软件测试网o Rq?X l k`%h F
        size = 128;
m_v:l6GK-V0F/Ey0    mem_limit = limit;
-A&t*Q L)R2T"Sg#J0D0    memset(slabclass, 0, sizeof(slabclass));
sXA ]'G%VK_(W0    while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) {
-o3@&R9B]a0        /* Make sure items are always n-byte aligned */51Testing软件测试网 ?V;l(\$[;}6X IEX
        if (size % CHUNK_ALIGN_BYTES)51Testing软件测试网0b,~%fy@-\
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);51Testing软件测试网9g h9am P:o ]
        slabclass[i].size = size;51Testing软件测试网3g#{ Ue8xIw
        slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;
1]*i6c3R^.E#L v'C-o0        size *= factor;51Testing软件测试网mJHX/l3c _"j&~:\-T
        if (settings.verbose > 1) {51Testing软件测试网cijQ G3XZp&p X
            fprintf(stderr, "slab class %3d: chunk size %6d perslab %5d\n",51Testing软件测试网(R8O#RrwS.E2sV/Bj5\
                    i, slabclass[i].size, slabclass[i].perslab);51Testing软件测试网Di)k[n8Uc
        }      
%AG"{H!?o3\8`?0    }51Testing软件测试网-~5fVLg r
    power_largest = i;51Testing软件测试网 B9t(^a(z?
    slabclass[power_largest].size = POWER_BLOCK;
4A mT*~9H QQ+li2F0    slabclass[power_largest].perslab = 1;51Testing软件测试网o,CeyiYb}F#e

u^~5]M:]:ETg}0    /* for the test suite:  faking of how much we've already malloc'd */51Testing软件测试网 tZoy:LA W4_1o'c
    {51Testing软件测试网 F-Z8}/_2m
        char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");51Testing软件测试网)SG-d;zx2sh X7]
        if (t_initial_malloc) {
}3Aw/l|V G0            mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC"));51Testing软件测试网 o cPX6k!@,b
        }      51Testing软件测试网S.lN'^w2p&_p8w-`
    }51Testing软件测试网&[V:e.R*W6L9M
#ifndef DONT_PREALLOC_SLABS
0F a!|dw0    {51Testing软件测试网M Hn ^Ti*Z$} N$g6k0q
        char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
Hx@;a-f3RHgo&k[0        if (!pre_alloc || atoi(pre_alloc)) {
x`xL:R"{7Vt0            slabs_preallocate(limit / POWER_BLOCK);51Testing软件测试网&r1?f@ K[;e5CG
        }51Testing软件测试网"uW%_:zhkm\(B i$w
    }51Testing软件测试网w(z5Nx3b
#endif51Testing软件测试网#Y2b8Qkm iY;D
}

Xr v'cH,MEl M1V051Testing软件测试网3ypv#YR7D%ASt

由上可以看出,memcached的内存分配是有冗余的,当一个slab不能被它所拥有的chunk大小整除时,slab尾部剩余的空间就被丢弃了,如id40中,两个chunk占用了1009384字节,这个slab一共有1MB,那么就有39192字节被浪费了。51Testing软件测试网(qMBAj"h`b

2j)DtZ%? {0Memcached使用这种方式来分配内存,是为了可以快速的通过item长度定位出slab的classid,有一点类似hash,因为item的长度是可以计算的,比如一个item的长度是300字节,在1.2中就可以得到它应该保存在id7的slab中,因为按照上面的计算方法,id6的chunk大小是252字节,id7的chunk大小是316字节,id8的chunk大小是396字节,表示所有252到316字节的item都应该保存在id7中。同理,在1.1中,也可以计算得到它出于256和512之间,应该放在chunk_size为512的id9中(32位系统)。51Testing软件测试网+E*X0z;Z6}y R

Rtgzi2pB9zlp0Memcached初始化的时候,会初始化slab(前面可以看到,在main函数中调用了slabs_init())。它会在slabs_init()中检查一个常量DONT_PREALLOC_SLABS,如果这个没有被定义,说明使用预分配内存方式初始化slab,这样在所有已经定义过的slabclass中,每一个id创建一个slab。这样就表示,1.2在默认的环境中启动进程后要分配41MB的slab空间,在这个过程里,memcached的第二个内存冗余发生了,因为有可能一个id根本没有被使用过,但是它也默认申请了一个slab,每个slab会用掉1MB内存

,G:_gO9ore0

4H1m])\TXBc4g0当一个slab用光后,又有新的item要插入这个id,那么它就会重新申请新的slab,申请新的slab时,对应id的slab链表就要增长,这个链表是成倍增长的,在函数grow_slab_list函数中,这个链的长度从1变成2,从2变成4,从4变成8……:51Testing软件测试网f^'n4@-Qo5i4e3zsL3G$m}

&@S h-q"q0CODE:

!d \+k/]M9Rg0

J,q[0C"L4w @P}0static int grow_slab_list (unsigned int id) {51Testing软件测试网,l)p$r4Z'{ Li,L
    slabclass_t *p = &slabclass[id];
7~:~ _ wC$[E*~0    if (p->slabs == p->list_size) {51Testing软件测试网RU){l/Rr'LK
        size_t new_size =  p->list_size ? p->list_size * 2 : 16;51Testing软件测试网$F|p0T/Q$gH
        void *new_list = realloc(p->slab_list, new_size*sizeof(void*));51Testing软件测试网d$A"s+q,ZP6p(_Xc
        if (new_list == 0) return 0;51Testing软件测试网/x L8k!dH{Z Th
        p->list_size = new_size;51Testing软件测试网V z*X-L*["~T
        p->slab_list = new_list;51Testing软件测试网a;pt)p l5j
    }51Testing软件测试网k/DVIs~l
    return 1;
Gb{4B"v8m0}

rm-Zbvw051Testing软件测试网*o4v9}(CmMG

51Testing软件测试网A(ON_~\c Z

在定位item时,都是使用slabs_clsid函数,传入参数为item大小,返回值为classid,由这个过程可以看出,memcached的第三个内存冗余发生在保存item的过程中,item总是小于或等于chunk大小的,当item小于chunk大小时,就又发生了空间浪费。51Testing软件测试网|KDg/ERL-J

pG${r2M4i)e1G06、 Memcached的NewHash算法

!r(?5}*R-s0Memcached的item保存基于一个大的hash表,它的实际地址就是slab中的chunk偏移,但是它的定位是依靠对key做hash的结果,在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。

5X;o Ec7i3bfV0

o:Bw6L \0Memcached使用了一个叫做NewHash的算法,它的效果很好,效率也很高。1.1和1.2的NewHash有一些不同,主要的实现方式还是一样的,1.2的hash函数是经过整理优化的,适应性更好一些。

0b-g,Y y k7e_051Testing软件测试网;p5ssaw+e

NewHash的原型参考:http://burtleburtle.net/bob/hash/evahash.html。51Testing软件测试网u x&h'XY+mn m

d,b+@A*j{ k;c0为了变换方便,定义了u4和u1两种数据类型,u4就是无符号的长整形,u1就是无符号char(0-255)。具体代码可以参考1.1和1.2源码包。

+]D#e!G7_-E\N051Testing软件测试网(K R#@pj5J

注意这里的hashtable长度,1.1和1.2也是有区别的,1.1中定义了HASHPOWER常量为20,hashtable表长为hashsize(HASHPOWER),就是4MB(hashsize是一个宏,表示1右移n位),1.2中是变量16,即hashtable表长65536:

lS'Tr~^4Y0

3b'I,K;hc C0CODE:

9M#lr1e)E6D+q)epG051Testing软件测试网/Do$HC3AYaMO

typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
!]+E:S,m1iN%g2K%p0typedef  unsigned       char ub1;   /* unsigned 1-byte quantities */
R$m;i0k$d^0#define hashsize(n) ((ub4)1<<(n))51Testing软件测试网K;pFz\*v W9st(y
#define hashmask(n) (hashsize(n)-1)51Testing软件测试网-G)C[,Y}P.L

}1`(\d:j!tIL0在assoc_init()中,会对primary_hashtable做初始化,对应的hash操作包括:assoc_find()、assoc_expand()、assoc_move_next_bucket()、assoc_insert()、assoc_delete(),对应于item的读写操作。其中assoc_find()是根据key和key长寻找对应的item地址的函数(注意在C中,很多时候都是同时直接传入字符串和字符串长度,而不是在函数内部做strlen),返回的是item结构指针,它的数据地址在slab中的某个chunk上。

a*H ];VX*?&kO051Testing软件测试网!NOn}*s ap1Qh

items.c是数据项的操作程序,每一个完整的item包括几个部分,在item_make_header()中定义为:51Testing软件测试网9YP^*Xm&Dx

51Testing软件测试网 HqV1j&P&|

key:键
(E?YYcEn;S0nkey:键长51Testing软件测试网%u @7m5N4D b:W S f!O6e+[
flags:用户定义的flag(其实这个flag在memcached中没有启用)
%eN*Co!F/QEOY0nbytes:值长(包括换行符号\r\n)
h_ YZ2H0suffix:后缀Buffer51Testing软件测试网+^e}1i {NC*i
nsuffix:后缀长

QXg6?Mp!~Me,P;^051Testing软件测试网}.[(H-n#gG;f

一个完整的item长度是键长+值长+后缀长+item结构大小(32字节),item操作就是根据这个长度来计算slab的classid的。

+E/p`P3XNCE+R051Testing软件测试网 W Tq/ym&TQ/v u&c

hashtable中的每一个桶上挂着一个双链表,item_init()的时候已经初始化了heads、tails、sizes三个数组为0,这三个数组的大小都为常量LARGEST_ID(默认为255,这个值需要配合factor来修改),在每次item_assoc()的时候,它会首先尝试从slab中获取一块空闲的chunk,如果没有可用的chunk,会在链表中扫描50次,以得到一个被LRU踢掉的item,将它unlink,然后将需要插入的item插入链表中。

ql$J6m6nl+O u l051Testing软件测试网a9k%?(I e;t,id

注意item的refcount成员。item被unlink之后只是从链表上摘掉,不是立刻就被free的,只是将它放到删除队列中(item_unlink_q()函数)。51Testing软件测试网*h-V;sMK

51Testing软件测试网]B2_x9o"Irg

item对应一些读写操作,包括remove、update、replace,当然最重要的就是alloc操作。51Testing软件测试网^,|k.V&yl%T ~

51Testing软件测试网8X/@(L9L4AA

item还有一个特性就是它有过期时间,这是memcached的一个很有用的特性,很多应用都是依赖于memcached的item过期,比如session存储、操作锁等。item_flush_expired()函数就是扫描表中的item,对过期的item执行unlink操作,当然这只是一个回收动作,实际上在get的时候还要进行时间判断:

'i I2J Ki*x^/?-]w051Testing软件测试网.Ep$mMGZ!p(r6vu

CODE:51Testing软件测试网;WHQ])~}b

51Testing软件测试网0v2L#d-n.@9Z v

/* expires items that are more recent than the oldest_live setting. */
/G?'BggqLL0void item_flush_expired() {51Testing软件测试网0|B$m2_5T }
    int i;  
7u:bH I&hW]P)|"\0    item *iter, *next;
t0k4}h:d'ku(g/]0    if (! settings.oldest_live)
9R7ihE*M|3C0        return;
9[4sk qe&j%L)f|0    for (i = 0; i < LARGEST_ID; i++) {
:g+Wo k|(Q0        /* The LRU is sorted in decreasing time order, and an item's timestamp51Testing软件测试网+M1S6pi!tC
         * is never newer than its last access time, so we only need to walk
$JU)G9Vm.RQ0         * back until we hit an item older than the oldest_live time.
:t![*HxD&e.g0         * The oldest_live checking will auto-expire the remaining items.51Testing软件测试网7r'N EraRiZ
         */
,A6[ VZBH{/dP0        for (iter = heads[i]; iter != NULL; iter = next) {
;p7^5Z@ V%f}z0            if (iter->time >= settings.oldest_live) {
8`1f!w$w0^z]0                next = iter->next;51Testing软件测试网v q7lA,T [VM
                if ((iter->it_flags & ITEM_SLABBED) == 0) {
v8C-i _e0                    item_unlink(iter);
2ljw3@"_zX+Qn0                }      
P1q1V4EK0            } else {
AI&n)Kh*~7r&x7~0                /* We've hit the first old item. Continue to the next queue. */51Testing软件测试网x aROUb8N*E;`E
                break;  51Testing软件测试网Uh8r:U*~.b
            }      51Testing软件测试网+l3M(GzXl*{0N
        }      
9E vm^IOY0    }51Testing软件测试网4h(Ra s3P
}

!ok]"Fgl5N:]0

Sx L5g'T?x(s&h"C0CODE:

? K}qD0

hzC8Bj W XJ ^ T Y0/* wrapper around assoc_find which does the lazy expiration/deletion logic */
9CM-vy_0item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) {
X{q+Jff3c0    item *it = assoc_find(key, nkey);51Testing软件测试网_w:z ?&NP
    if (delete_locked) *delete_locked = 0;51Testing软件测试网fK&gg#I
    if (it && (it->it_flags & ITEM_DELETED)) {
I@ ZnzM P0        /* it's flagged as delete-locked.  let's see if that condition51Testing软件测试网 k'Mq#^8L}
           is past due, and the 5-second delete_timer just hasn't
B`"Ms~'o{y@M]0           gotten to it yet... */
8rNtu?(a8q*w0        if (! item_delete_lock_over(it)) {
,X3\I#kk8y8k|&a"gU-E;n0            if (delete_locked) *delete_locked = 1;
(G'or4T,CN @0            it = 0;51Testing软件测试网 v8hU8ab_r4`%C
        }      
:n-tX$cRu0    }51Testing软件测试网l#Tki#}Y
    if (it && settings.oldest_live && settings.oldest_live <= current_time &&51Testing软件测试网k+e6bL.uXP@
        it->time <= settings.oldest_live) {51Testing软件测试网`Ud${7B-K [
        item_unlink(it);
1^jnQ-ow`2Q0        it = 0;
#I6a9pbN0    }
hB5`F8Z`6n0    if (it && it->exptime && it->exptime <= current_time) {
V9y W |'B$_6j9w0        item_unlink(it);51Testing软件测试网3a9F%c?#Hw~ _
        it = 0;51Testing软件测试网 \qUz-N{1]
    }51Testing软件测试网-gS.kWjg/JH0MV
    return it;
g/sdg0Q0}51Testing软件测试网to#p,H7wdL7Y

51Testing软件测试网7i^Zj!@J

Memcached的内存管理方式是非常精巧和高效的,它很大程度上减少了直接alloc系统内存的次数,降低函数开销和内存碎片产生几率,虽然这种方式会造成一些冗余浪费,但是这种浪费在大型系统应用中是微不足道的。51Testing软件测试网m.qf EO@ ]&s+n

51Testing软件测试网+j/x Aq`O2OPm}

Y%F#u(@|aXU&D051Testing软件测试网"y!J[ra QkK

~&[_$\8\r,`,J\)^*o07、 Memcached的理论参数计算方式51Testing软件测试网4Xu!x:d"J1v7_shxi

51Testing软件测试网8P)K&]&Jxga

影响 memcached 工作的几个参数有:
aQ#BEu"D G0
"b"wY6r8{0常量REALTIME_MAXDELTA 60*60*24*30
;V w e5d a0` h0v0最大30天的过期时间51Testing软件测试网e'wg@p w[i!{
51Testing软件测试网1J7MD6e+p
conn_init()中的freetotal(=200)
zRSyX)W*DW SrR0最大同时连接数51Testing软件测试网W4}'V}(F
51Testing软件测试网cH&c*N3P]0f.u
常量KEY_MAX_LENGTH 250
~pWF(HbU)~x0最大键长51Testing软件测试网h#{@ d0}"n:qi6{
51Testing软件测试网0YCk7P?$u1?#L7Vz
settings.factor(=1.25)
6md,e4at4Tv4vm.S0factor将影响chunk的步进大小
$B\r Gl5D&Di0
s0s;s+QH2dhk0settings.maxconns(=1024)
I+k&|u:H4f X&o0最大软连接51Testing软件测试网 ZK/B \)T\:g
51Testing软件测试网/HA6yx0I @d
settings.chunk_size(=48)51Testing软件测试网q5X.p)| tB*^:z
一个保守估计的key+value长度,用来生成id1中的chunk长度(1.2)。id1的chunk长度等于这个数值加上item结构体的长度(32),即默认的80字节。51Testing软件测试网2qcI8XE[Vpn
51Testing软件测试网.s$XS"vFA*r-n!},I"^^
常量POWER_SMALLEST 1
x S7N'kb#}'v0^r*Y:m0最小classid(1.2)
]0Y'b yhq K051Testing软件测试网b)E}#RB%QS4v
常量POWER_LARGEST 20051Testing软件测试网r'u9{n&|/| c
最大classid(1.2)
(u OS)e#^6Z051Testing软件测试网 Q y]r$?FV?
常量POWER_BLOCK 1048576
fs!v/l+ybO4Z0默认slab大小51Testing软件测试网pR%raKfZ} \:z"J
51Testing软件测试网4s6gh`3P
常量CHUNK_ALIGN_BYTES (sizeof(void *))
;ttN%b i.k*QHOy0保证chunk大小是这个数值的整数倍,防止越界(void *的长度在不同系统上不一样,在标准32位系统上是4)
7s,J(l{&P |O5Bo0
$ki-t W1LUt0常量ITEM_UPDATE_INTERVAL 60
{:["sc9G^0队列刷新间隔51Testing软件测试网aq fS|9}c

xcl.{]d0常量LARGEST_ID 25551Testing软件测试网|4}&L|1n7~6L4Fd0[$}
最大item链表数(这个值不能比最大的classid小)
7k F"azC2p051Testing软件测试网PJa%v$R7F]
变量hashpower(在1.1中是常量HASHPOWER)
i.dd an0决定hashtable的大小
`(Uml5l;{'Mc{0c051Testing软件测试网+mIH4|'pFTav2Q M}
根据上面介绍的内容及参数设定,可以计算出的一些结果:51Testing软件测试网6w9|P ] E }

N0_9e;R?UR z01、在memcached中可以保存的item个数是没有软件上限的,之前我的100万的说法是错误的。
vLTzBb J{d"m02、假设NewHash算法碰撞均匀,查找item的循环次数是item总数除以hashtable大小(由hashpower决定),是线性的。51Testing软件测试网Zm8S }Mp~p] `
3、Memcached限制了可以接受的最大item是1MB,大于1MB的数据不予理会。
aO3dg | i E04、Memcached的空间利用率和数据特性有很大的关系,又与DONT_PREALLOC_SLABS常量有关。 在最差情况下,有198个slab会被浪费(所有item都集中在一个slab中,199个id全部分配满)。51Testing软件测试网R r z$E~,Mt

51Testing软件测试网qE U {#Q~'Q S

 51Testing软件测试网4W,sN x u


TAG: memcache hash MemCache

玄大冰 引用 删除 welcome_zhang   /   2011-11-17 15:01:25
5
引用 删除 liuwenfangzzr   /   2008-03-04 17:19:31
很好,简单易懂.
 

评分:0

我来说两句

Open Toolbar