memcached 深度解剖

:o [color=red][b]memcached 简介[/b][/color]
 memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,最初为了加速 LiveJournal 访问速度而开发的,后来被很多大型的网站采用。目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力。起初作者编写它可能是为了提高动态网页应用,为了减轻数据库检索的压力,来做的这个缓存系统。它的缓存是一种分布式的,也就是可以允许不同主机上的多个用户同时访问这个缓存系统,这种方法不仅解决了共享内存只能是单机的弊端,同时也解决了数据库检索的压力,最大的优点是提高了访问获取数据的速度!基于memcache作者对分布式 cache的理解和解决方案。 memcache完全可以用到其他地方 比如分布式数据库, 分布式计算等领域。

[color=red][b]memcached 协议[/b][/color]

memcached 协议简单的说有一组规定好的字符串组成,例如:

socket.write "get #{cache_key}\r\n"
socket.write "gets #{cache_key}\r\n"
socket.write "incr #{cache_key} #{amount}#{noreply}\r\n"


[color=red][b] memcached 和nginx 结合使用:[/b][/color]
原文地址:
http://www.igvita.com/2008/02/11/nginx-and-memcached-a-400-boost/
配置例子:
  location /dynamic_request {
# append an extenstion for proper MIME type detection
if ($args ~* format=json) { rewrite ^/dynamic_request/?(.*)$ /dynamic_request.js$1 break; }
if ($args ~* format=xml) { rewrite ^/dynamic_request/?(.*)$ /dynamic_request.xml$1 break; }

memcached_pass 127.0.0.1:11211;
error_page 404 = @dynamic_request;
}

[color=red] [b]架构图:[/b][/color]
[img]http://www.igvita.com/posts/02-08/nginx-memcached.png[/img]

[color=red][b] memcached 内部数据存储分析:[/b][/color]
memcached 的数据结构可以简单的有slab -> chunks 来描述,slab 可以理解为一个内存块,一个slab是memcached一次申请内存的最小单位,在memcache中,一个slab大小默认为1M,每个slab被划分为若干个chunk,每个chunk里保存一个item,slab安装自己的id分别组成链表,这些链表又按id挂在一个slabclass数组上,整个结构看起来有点像二维数组。slabclass的长度在memcached 1.1中是21,在memcached 1.2中是200。
slab有一个初始chunk大小,memcached1.1中是1字节,memcached1.2中是80字节,
memcached 1.2中有一个factor值,默认为1.25, memcached -help 查看一下参数factor

SlabClass(1.2版本)
id 从 0 开始
|------| 0 slab
| id1 | ------->|-------|
| id2 | |chunk| -> item {key:value} 这个根据slabId 计算chunk大小
| .. | | .... |
|------| 199


class SlabClass
{
int size #chunk size
int perslab chunk个数
Array *slot #空闲槽
int slabs #slab 数量
Array * slab_list 指针数值 1M slab #不够就申请slab,并加入slab_list 中。
}



[b][color=red]find slab id[/color][/b]



unsigned int slabs_clsid(const size_t size) {
int res = POWER_SMALLEST;

if (size == 0)
return 0;
while (size > slabclass[res].size)
if (res++ == power_largest) /* won't fit in the biggest slab */
return 0;
return res;
}


[color=red][b]以下是根据slabid计算chunk大小的公式(1.2版本):[/b][/color]
chunk 大小 = 初始大小 * factor ^ id 
slab_id = 0
chunk 大小 = 80字节 * 1.25 ^ 0
slab_id = 1
chunk 大小 = 80字节 * 1.25 ^ 1

id = 0 的slab 永远都是 80字节每个chunk,除非你重编译源码
那么 id为0的slab有 (1024* 1024)/80= 13107 个chunk
memcached1.2版本 会初始化 id 到 40.


:~> memcached -vvv
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 5: chunk size 240 perslab 4369
slab class 6: chunk size 304 perslab 3449
slab class 7: chunk size 384 perslab 2730
slab class 8: chunk size 480 perslab 2184
slab class 9: chunk size 600 perslab 1747
slab class 10: chunk size 752 perslab 1394
slab class 11: chunk size 944 perslab 1110
slab class 12: chunk size 1184 perslab 885
slab class 13: chunk size 1480 perslab 708
slab class 14: chunk size 1856 perslab 564
slab class 15: chunk size 2320 perslab 451
slab class 16: chunk size 2904 perslab 361
slab class 17: chunk size 3632 perslab 288
slab class 18: chunk size 4544 perslab 230
slab class 19: chunk size 5680 perslab 184
slab class 20: chunk size 7104 perslab 147
slab class 21: chunk size 8880 perslab 118
slab class 22: chunk size 11104 perslab 94
slab class 23: chunk size 13880 perslab 75
slab class 24: chunk size 17352 perslab 60
slab class 25: chunk size 21696 perslab 48
slab class 26: chunk size 27120 perslab 38
slab class 27: chunk size 33904 perslab 30
slab class 28: chunk size 42384 perslab 24
slab class 29: chunk size 52984 perslab 19
slab class 30: chunk size 66232 perslab 15
slab class 31: chunk size 82792 perslab 12
slab class 32: chunk size 103496 perslab 10
slab class 33: chunk size 129376 perslab 8
slab class 34: chunk size 161720 perslab 6
slab class 35: chunk size 202152 perslab 5
slab class 36: chunk size 252696 perslab 4
slab class 37: chunk size 315872 perslab 3
slab class 38: chunk size 394840 perslab 2
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
<26 server listening (auto-negotiate)
<27 send buffer was 124928, now 268435456
<28 send buffer was 124928, now 268435456


直到 chunk 大小为1M,最有一个slab_id中对应的chunk只有一个。
那么memcached为什么使用这种方式存储那,是因为通过chunk的大小就能得到slab_id,从而找到slab,但是,这样做有一个缺点就是空间利用率下降了,chunk的大小可能不会是1M的整数倍.
[color=red][b]计算slab_id公式如下:[/b][/color]
factor^id = chunk 大小 /  初始大小
id = log(chunk 大小 / 初始大小 ) / log(factor)
这样通过chunk 大小就能得到 slab id 了.
例如:
id = log(80字节 / 80字节) / log(1.25) = 0


你知道了slab和chunk的关系,就可以改变factor 来优化你的memcached了。
需要提醒的是,因为id=0的chunk为80字节,如果你保存的数据超过80字节,性能会有一些损耗。

[b][color=red]修改chunk size[/color][/b]

/* 修改slab.c 文件中的 slabs_init方法 */

while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
/* Make sure items are always n-byte aligned */
if (size % CHUNK_ALIGN_BYTES)
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);

slabclass[i].size = 200 /*size;*/
slabclass[i].perslab = 5000 /*settings.item_size_max / slabclass[i].size;*/
size *= factor;
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
}
}



slab class 1: chunk size 200 perslab 5000
slab class 2: chunk size 200 perslab 5000
slab class 3: chunk size 200 perslab 5000
slab class 4: chunk size 200 perslab 5000
slab class 5: chunk size 200 perslab 5000
slab class 6: chunk size 200 perslab 5000
slab class 7: chunk size 200 perslab 5000
slab class 8: chunk size 200 perslab 5000
slab class 9: chunk size 200 perslab 5000


 
# Item_Size Max_age 1MB_pages Count Full?
1 200 B 245 s 2 5139 no
# Item_Size Max_age 1MB_pages Count Full?
1 200 B 245 s 5 21966 no


根据以上代码我发现,memcache slab 只取一个 slab_class ,需要如下修改

/* 修改slabs_clsid */
unsigned int slabs_clsid(size_t size) {
return 1;
}

/* 修改slab_init */
void slabs_init(const size_t limit, const double factor, const bool prealloc) {

//while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
// /* Make sure items are always n-byte aligned */
// if (size % CHUNK_ALIGN_BYTES)
// size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
//
// slabclass[i].size = 200;//size;
// slabclass[i].perslab = 5000;/*settings.item_size_max / slabclass[i].size;*/
// size *= factor;
// if (settings.verbose > 1) {
// fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
// i, slabclass[i].size, slabclass[i].perslab);
// }
// }
i = 1;
power_largest = i;

slabclass[1].size = 200; //settings.item_size_max;
slabclass[1].perslab = 5000;

//slabclass[power_largest].size = settings.item_size_max;
//slabclass[power_largest].perslab = 1;


如下结果

slab class 1: chunk size 200 perslab 5000

# Item_Size Max_age 1MB_pages Count Full?
1 200 B 7 s 3 10137 no



Memcached一些特性和限制
在 Memcached 中可以保存的item数据量是没有限制的,只有内存足够
最大30天的数据过期时间, 设置为永久的也会在这个时间过期,常量REALTIME_MAXDELTA 60*60*24*30 控制
最大键长为250字节,大于该长度无法存储,常量KEY_MAX_LENGTH 250 控制
单个item最大数据是1MB,超过1MB数据不予存储,常量POWER_BLOCK 1048576 进行控制,它是默认的slab大小
最大同时连接数是200,通过 conn_init()中的freetotal 进行控制,最大软连接数是1024,通过 settings.maxconns=1024 进行控制
跟空间占用相关的参数:settings.factor=1.25, settings.chunk_size=48, 影响slab的数据占用和步进方式

在新版本中可以使用 -I 来设置slab滴大小

-I            Override the size of each slab page. Adjusts max item size
(default: 1mb, min: 1k, max: 128m)



As far as this list is concerned, if you run a 32-bit
Linux OS, memcached can only use 2GB of memory for the entire process. That
includes the memory needed for connections. If you run a 64-bit Linux OS you do not have this limit
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值