Memcached存储机制
主要通过测试,推理memcached的存储机制。
平台 windows7
版本 memcached-1.2.6-win32
启动日志:
E:\memcached\memcached-1.2.6-win32-bin>memcached -m 32 -p 12001 -vv
slab class 1: chunk size 88 perslab 11915 slab class 2: chunk size 112 perslab 9362 slab class 3: chunk size 144 perslab 7281 slab class 4: chunk size 184 perslab 5698 slab class 5: chunk size 232 perslab 4519 slab class 6: chunk size 296 perslab 3542 slab class 7: chunk size 376 perslab 2788 slab class 8: chunk size 472 perslab 2221 slab class 9: chunk size 592 perslab 1771 slab class 10: chunk size 744 perslab 1409 slab class 11: chunk size 936 perslab 1120 slab class 12: chunk size 1176 perslab 891 slab class 13: chunk size 1472 perslab 712 slab class 14: chunk size 1840 perslab 569 slab class 15: chunk size 2304 perslab 455 slab class 16: chunk size 2880 perslab 364 slab class 17: chunk size 3600 perslab 291 slab class 18: chunk size 4504 perslab 232 slab class 19: chunk size 5632 perslab 186 slab class 20: chunk size 7040 perslab 148 slab class 21: chunk size 8800 perslab 119 slab class 22: chunk size 11000 perslab 95 slab class 23: chunk size 13752 perslab 76 slab class 24: chunk size 17192 perslab 60 slab class 25: chunk size 21496 perslab 48 slab class 26: chunk size 26872 perslab 39 slab class 27: chunk size 33592 perslab 31 slab class 28: chunk size 41992 perslab 24 slab class 29: chunk size 52496 perslab 19 slab class 30: chunk size 65624 perslab 15 slab class 31: chunk size 82032 perslab 12 slab class 32: chunk size 102544 perslab 10 slab class 33: chunk size 128184 perslab 8 slab class 34: chunk size 160232 perslab 6 slab class 35: chunk size 200296 perslab 5 slab class 36: chunk size 250376 perslab 4 slab class 37: chunk size 312976 perslab 3 slab class 38: chunk size 391224 perslab 2 slab class 39: chunk size 489032 perslab 2 <96 server listening <112 server listening <116 send buffer was 8192, now 268435456 <116 server listening (udp) <120 new client connection <120 exit >120 ERROR <120 quit <120 connection closed. <120 new client connection
日志只是显示了将来的分配策略,并未真正分配内存。
Slab表示块大小的级别,chunk表示slab中的一个块,还有个比较重要的概念就是page,page表示同一级slab大小的块的集合。
查看数据状态:
stats STAT pid 12212 STAT uptime 10 STAT time 1367559145 STAT version 1.2.6 STAT pointer_size 32 STAT curr_items 0 STAT total_items 0 STAT bytes 0 --已存数据大小 STAT curr_connections 3 STAT total_connections 4 STAT connection_structures 4 STAT cmd_get 0 STAT cmd_set 0 STAT get_hits 0 STAT get_misses 0 STAT evictions 0 STAT bytes_read 11 STAT bytes_written 7 STAT limit_maxbytes 33554432 ---最大容量 STAT threads 1 END
Slab的大小是怎么算出来的呢?
slab大小的计算方式: 从0M到1M,按等比数列划分,默认比例因子是1.25.例如上例中:88*1.25=112.
存数据,测试代码:
public static void main(String[] arg){ byte[] arr=new byte[1024*6]; //使用7k的块 int i=0; int n=i+140; for(;i<n;i++){ boolean status=MemcachedUtil.getInstance().set(String.valueOf(System.currentTimeMillis()+""+i), arr,60*60); if(!status){ System.out.println("------------------------------"); } System.out.println(status); } }
结果:
stats slabs STAT 20:chunk_size 7040 --块大小 STAT 20:chunks_per_page 148 ----每个page中块数量 STAT 20:total_pages 1 --------只有一个page STAT 20:total_chunks 148 ---第20个slab级别的块数量总和= total_pages* chunks_per_page STAT 20:used_chunks 148 STAT 20:free_chunks 0 STAT 20:free_chunks_end 8 STAT active_slabs 1 STAT total_malloced 1041920 ---所有数据占用内存=140*(7040+key所占内存) END stats items STAT items:20:number 140 STAT items:20:age 241 STAT items:20:evicted 0 STAT items:20:outofmemory 0 END stats sizes 6240 140 END
使用了第20个slab的块(7k的块),使用了140个,还剩8个。
再执行8次:
int i=0; int n=i+8;
结果:
stats slabs STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 1 STAT 20:total_chunks 148 STAT 20:used_chunks 148 STAT 20:free_chunks 0 STAT 20:free_chunks_end 0 ---第一个page中148个chunk全部占用 STAT active_slabs 1 STAT total_malloced 1041920 END stats items STAT items:20:number 148 STAT items:20:age 407 STAT items:20:evicted 0 STAT items:20:outofmemory 0 END
那么再放一个
7k的块会怎么分配呢?
测试:
int i=0; int n=i+1;
结果:
stats slabs STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 2 -----新加了一个page STAT 20:total_chunks 296 STAT 20:used_chunks 296 STAT 20:free_chunks 0 STAT 20:free_chunks_end 147 STAT active_slabs 1 STAT total_malloced 2083840 END stats items STAT items:20:number 149 ---总过有149个块被使用 STAT items:20:age 1053 STAT items:20:evicted 0 STAT items:20:outofmemory 0 END
那么我用7k的块,把32M内存占完,之后再存数据看会是什么情况:
先测一下多少数据可以把内存沾满,经测试,当到以下状态时,再放数据,total_chunks不会增加,所以可见4736个7k的块可以把内存放满:
stats slabs STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 32 STAT 20:total_chunks 4736 STAT 20:used_chunks 4736 STAT 20:free_chunks 0 STAT 20:free_chunks_end 0 STAT active_slabs 1 STAT total_malloced 33341440 END
修改测试程序:
byte[] arr=new byte[1024*6]; //使用7k的块 MemcachedUtil.getInstance().set("first1", arr,60*60); MemcachedUtil.getInstance().set("first2", arr,60*60); MemcachedUtil.getInstance().set("first3", arr,60*60); MemcachedUtil.getInstance().set("first4", arr,60*60); int i=1; int n=i+4732; for(;i<n;i++){ boolean status=MemcachedUtil.getInstance().set(String.valueOf(i), arr,60*60); } MemcachedUtil.getInstance().set("last1", arr,60*60); MemcachedUtil.getInstance().set("last2", arr,60*60); MemcachedUtil.getInstance().set("last3", arr,60*60); MemcachedUtil.getInstance().set("last4", arr,60*60); System.out.println(MemcachedUtil.getInstance().get("first1")); System.out.println(MemcachedUtil.getInstance().get("first2")); System.out.println(MemcachedUtil.getInstance().get("first3")); System.out.println(MemcachedUtil.getInstance().get("first4")); System.out.println(MemcachedUtil.getInstance().get("last1")); System.out.println(MemcachedUtil.getInstance().get("last2")); System.out.println(MemcachedUtil.getInstance().get("last3")); System.out.println(MemcachedUtil.getInstance().get("last4"));
输出:
null null null null [B@46a5c4 [B@2d09e0 [B@e38fca [B@1f528ab
数据状态:
stats items STAT items:20:number 4736 STAT items:20:age 87 STAT items:20:evicted 4 STAT items:20:outofmemory 0 END
可见,
last把first全部替换出去了。
那么再放一个其他大小块的数据呢?
byte[] arr=new byte[1024*8]; //使用8k的块 MemcachedUtil.getInstance().set("new1", arr,60*60); System.out.println(MemcachedUtil.getInstance().get("new1"));
输出:
[B@1ff0a34
数据状态:
stats slabs =================slab20的状态没变化 STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 32 STAT 20:total_chunks 4736 STAT 20:used_chunks 4736 STAT 20:free_chunks 0 STAT 20:free_chunks_end 0 ============数据被放在了slab21中,新开了一个slab21的page STAT 21:chunk_size 8800 STAT 21:chunks_per_page 119 STAT 21:total_pages 1 STAT 21:total_chunks 119 STAT 21:used_chunks 119 STAT 21:free_chunks 0 STAT 21:free_chunks_end 118 STAT active_slabs 2 STAT total_malloced 34388640 END stats items STAT items:20:number 4736 STAT items:20:age 283 STAT items:20:evicted 4 STAT items:20:outofmemory 0 STAT items:21:number 1 STAT items:21:age 22 STAT items:21:evicted 0 STAT items:21:outofmemory 0 END
那么把slab21的这个page放满之后,如果再放数据会是什么情况呢?是继续新增slab21的page还是做替换呢?:
byte[] arr=new byte[1024*8]; //使用8k的块 int i=1; int n=i+118; for(;i<n;i++){ boolean status=MemcachedUtil.getInstance().set("newnew"+String.valueOf(i), arr,60*60); } System.out.println(MemcachedUtil.getInstance().get("newnew118"));
输出:
[B@c0fc8e
stats slabs STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 32 STAT 20:total_chunks 4736 STAT 20:used_chunks 4736 STAT 20:free_chunks 0 STAT 20:free_chunks_end 0 STAT 21:chunk_size 8800 STAT 21:chunks_per_page 119 STAT 21:total_pages 1 STAT 21:total_chunks 119 STAT 21:used_chunks 119 STAT 21:free_chunks 0 STAT 21:free_chunks_end 0 ---已全部被占用 STAT active_slabs 2 STAT total_malloced 34388640 END stats items STAT items:20:number 4736 STAT items:20:age 579 STAT items:20:evicted 4 STAT items:20:outofmemory 0 STAT items:21:number 119 STAT items:21:age 318 STAT items:21:evicted 0 STAT items:21:outofmemory 0 END
再放一个:
MemcachedUtil.getInstance().set("newnewnew0", arr,60*60); System.out.println(MemcachedUtil.getInstance().get("newnewnew0"));
输出:
[B@1ff0a34
stats slabs STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 32 STAT 20:total_chunks 4736 STAT 20:used_chunks 4736 STAT 20:free_chunks 0 STAT 20:free_chunks_end 0 STAT 21:chunk_size 8800 STAT 21:chunks_per_page 119 STAT 21:total_pages 1 -----page没有增加,还是1 STAT 21:total_chunks 119 STAT 21:used_chunks 119 STAT 21:free_chunks 0 STAT 21:free_chunks_end 0 STAT active_slabs 2 STAT total_malloced 34388640 END stats items STAT items:20:number 4736 STAT items:20:age 725 STAT items:20:evicted 4 STAT items:20:outofmemory 0 STAT items:21:number 119 STAT items:21:age 162 STAT items:21:evicted 1 ----替换掉了一个slab21的块 STAT items:21:outofmemory 0 END
再放一个2k的块也一样:
byte[] arr=new byte[1024*2]; //使用2k的块 MemcachedUtil.getInstance().set("newnewnewnew0", arr,60*60); System.out.println(MemcachedUtil.getInstance().get("newnewnewnew0"));
stats slabs STAT 15:chunk_size 2304 STAT 15:chunks_per_page 455 STAT 15:total_pages 1 STAT 15:total_chunks 455 STAT 15:used_chunks 455 STAT 15:free_chunks 0 STAT 15:free_chunks_end 454 STAT 20:chunk_size 7040 STAT 20:chunks_per_page 148 STAT 20:total_pages 32 STAT 20:total_chunks 4736 STAT 20:used_chunks 4736 STAT 20:free_chunks 0 STAT 20:free_chunks_end 0 STAT 21:chunk_size 8800 STAT 21:chunks_per_page 119 STAT 21:total_pages 1 STAT 21:total_chunks 119 STAT 21:used_chunks 119 STAT 21:free_chunks 0 STAT 21:free_chunks_end 0 STAT active_slabs 3 STAT total_malloced 35436960 END stats items STAT items:15:number 1 STAT items:15:age 55 STAT items:15:evicted 0 STAT items:15:outofmemory 0 STAT items:20:number 4736 STAT items:20:age 1251 STAT items:20:evicted 4 STAT items:20:outofmemory 0 STAT items:21:number 119 STAT items:21:age 688 STAT items:21:evicted 1 STAT items:21:outofmemory 0 END
可见,当内存被沾满后,如果再存放一种新的slab大小的数据,那么memcached会新开一个且只开一个这种slab大小的page,当这个page被沾满后,就会执行数据替换。
这样算下来,memcached中的实际 数据总量可能就会超出启动时设置的32M,因为当一个slab大小的数据把内存沾满后,还可能为每个其他的slab大小的数据额为分配一个page,每个page的大小是1M。例如,如果有39个slab,那么就可能多占用38M的内存。而启动时 -f 因子的大小会决定slab数量的大小,所以也会影响到最后有多少数据会超出容量。