没有合适的资源?快使用搜索试试~ 我知道了~
Memcached深度分析.docx
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 117 浏览量
2023-09-17
23:49:47
上传
评论
收藏 47KB DOCX 举报
温馨提示
试读
19页
Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。关于这个东西,相信很多人都用过,本文意在通过对memcached的实现及代码分析,获得对这个出色的开源软件更深入的了解,并可以根据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析,加深对memcached的使用方式理解。
资源推荐
资源详情
资源评论
Memcached 深度分析
Memcached 是 danga.com(运营 LiveJournal 的技术团队)开发的一套分布式内存对象缓存系统,用于在
动态系统中减少数据库负载,提升性能。关于这个东西,相信很多人都用过,本文意在通过对 memcached
的实现及代码分析,获得对这个出色的开源软件更深入的了解,并可以根据我们的需要对其进行更进一步
的优化。末了将通过对 BSM_Memcache 扩展的分析,加深对 memcached 的使用方式理解。
本文的部分内容可能需要比较好的数学基础作为辅助。
◎Memcached 是什么
在阐述这个问题之前,我们首先要清楚它“不是什么”。很多人把它当作和 SharedMemory 那种形式的存储
载体来使用,虽然 memcached 使用了同样的“Key=>Value”方式组织数据,但是它和共享内存、APC 等本
地缓存有非常大的区别。Memcached 是分布式的,也就是说它不是本地的。它基于网络连接(当然它也可
以使用 localhost)方式完成服务,本身它是一个独立于应用的程序或守护进程(Daemon 方式)。
Memcached 使用 libevent 库实现网络连接服务,理论上可以处理无限多的连接,但是它和 Apache 不同,
它更多的时候是面向稳定的持续连接的,所以它实际的并发能力是有限制的。在保守情况下 memcached
的最大同时连接数为 200,这和 Linux 线程能力有关系,这个数值是可以调整的。关于 libevent 可以参考
相关文档。 Memcached 内存使用方式也和 APC 不同。APC 是基于共享内存和 MMAP 的,memcachd 有自
己的内存分配算法和管理方式,它和共享内存没有关系,也没有共享内存的限制,通常情况下,每个
memcached 进程可以管理 2GB 的内存空间,如果需要更多的空间,可以增加进程数。
◎Memcached 适合什么场合
在很多时候,memcached 都被滥用了,这当然少不了对它的抱怨。我经常在论坛上看见有人发贴,类似于
“如何提高效率”,回复是“用 memcached”,至于怎么用,用在哪里,用来干什么一句没有。memcached
不是万能的,它也不是适用在所有场合。
Memcached 是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干
脆规模小到只有一台服务器的应用,memcached 不会带来任何好处,相反还会拖慢系统效率,因为网络连
接同样需要资源,即使是 UNIX 本地连接也一样。 在我之前的测试数据中显示,memcached 本地读写速
度要比直接 PHP 内存数组慢几十倍,而 APC、共享内存方式都和直接数组差不多。可见,如果只是本地级
缓存,使用 memcached 是非常不划算的。
Memcached 在很多时候都是作为数据库前端 cache 使用的。因为它比数据库少了很多 SQL 解析、磁盘操
作等开销,而且它是使用内存来管理数据的,所以它可以提供比直接读取数据库更好的性能,在大型系统
中,访问同样的数据是很频繁的,memcached 可以大大降低数据库压力,使系统执行效率提升。另外,
memcached 也经常作为服务器之间数据共享的存储媒介,例如在 SSO 系统中保存系统单点登陆状态的数
据就可以保存在 memcached 中,被多个应用共享。
需要注意的是,memcached 使用内存管理数据,所以它是易失的,当服务器重启,或者 memcached 进程
中止,数据便会丢失,所以 memcached 不能用来持久保存数据。很多人的错误理解,memcached 的性能
非常好,好到了内存和硬盘的对比程度,其实 memcached 使用内存并不会得到成百上千的读写速度提高,
它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多
的开销和直接的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满
负荷了,memcached 进程本身并不占用多少 CPU 资源的情况。
◎Memcached 的工作方式
以下的部分中,读者最好能准备一份 memcached 的源代码。
Memcached 是传统的网络服务程序,如果启动的时候使用了-d 参数,它会以守护进程的方式执行。创建守
护进程由 daemon.c 完成,这个程序只有一个 daemon 函数,这个函数很简单(如无特殊说明,代码以
1.2.1 为准):
CODE:
[Copy to clipboard]
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int
daemon(nochdir, noclose)
int nochdir, noclose;
{
int fd;
switch (fork()) {
case -1:
return (-1);
case 0:
break;
default:
_exit(0);
}
if (setsid() == -1)
return (-1);
if (!nochdir)
(void)chdir("/");
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
(void)close(fd);
}
return (0);
}
这个函数 fork 了整个进程之后,父进程就退出,接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设
备, daemon 就建立成功了。
Memcached 本身的启动过程,在 memcached.c 的 main 函数中顺序如下:
1 、调用 settings_init() 设定初始化参数
2 、从启动命令中读取参数来设置 setting 值
3 、设定 LIMIT 参数
4 、开始网络 socket 监听(如果非 socketpath 存在)( 1.2 之后支持 UDP 方式)
5 、检查用户身份( Memcached 不允许 root 身份启动)
6 、如果有 socketpath 存在,开启 UNIX 本地连接(Sock 管道)
7 、如果以 -d 方式启动,创建守护进程(如上调用 daemon 函数)
8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab
9 、如设置中 managed 生效,创建 bucket 数组
10 、检查是否需要锁定内存页
11 、初始化信号、连接、删除队列
12 、如果 daemon 方式,处理进程 ID
13 、event 开始,启动过程结束, main 函数进入循环。
在 daemon 方式中,因为 stderr 已经被定向到黑洞,所以不会反馈执行中的可见错误信息。
memcached.c 的主循环函数是 drive_machine ,传入参数是指向当前的连接的结构指针,根据 state 成
员的状态来决定动作。
Memcached 使用一套自定义的协议完成数据交换,它的 protocol 文档可以参考:
http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
在 API 中,换行符号统一为\r\n
◎Memcached 的内存管理方式
Memcached 有一个很有特色的内存管理方式,为了提高效率,它使用预申请和分组的方式管理内存空间,
而并不是每次需要写入数据的时候去 malloc,删除数据的时候 free 一个指针。Memcached 使用 slab->chunk
的组织方式管理内存。
1.1 和 1.2 的 slabs.c 中的 slab 空间划分算法有一些不同,后面会分别介绍。
Slab 可以理解为一个内存块,一个 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。
slab 有一个初始 chunk 大小,1.1 中是 1 字节,1.2 中是 80 字节,1.2 中有一个 factor 值,默认为 1.25
在 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:
CODE:
[Copy to clipboard]
void slabs_init(size_t limit) {
int i;
int size=1;
mem_limit = limit;
for(i=0; i<=POWER_LARGEST; i++, size*=2) {
slabclass[i].size = size;
slabclass[i].perslab = POWER_BLOCK / size;
slabclass[i].slots = 0;
slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;
slabclass[i].end_page_ptr = 0;
slabclass[i].end_page_free = 0;
slabclass[i].slab_list = 0;
slabclass[i].list_size = 0;
slabclass[i].killing = 0;
}
/* for the test suite: faking of how much we've already malloc'd */
{
char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
if (t_initial_malloc) {
mem_malloced = atol(getenv("T_MEMD_INITIAL_MALLOC"));
}
}
/* pre-allocate slabs by default, unless the environment variable
for testing is set to something non-zero */
{
char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
if (!pre_alloc || atoi(pre_alloc)) {
slabs_preallocate(limit / POWER_BLOCK);
}
剩余18页未读,继续阅读
资源评论
小小哭包
- 粉丝: 1899
- 资源: 3854
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功