今天研究了一下memcache的内存分配策略,发现其和redis的确是有很大不同的,很多人知道memcache是提前预占用内存的,并且,其内存的占用是定长的,所以其内存的占用是可以被计算出来的。但是,其分配策略不是那么简单的,其合理的设置调优,也并不是那么容易的事情。下面就讲一下我的理解。以及-m参数认为无效的原因。
理解memcache的确必须先理解 slab、page、chunk 三个概念。page组成了slab,chunk组成了page,chunk是数据真实存储的位置,默认情况下,page的大小是1M,当然这个可以通过参数修改,chunk默认从96B开始,以1.25的倍数在slab区间增长。即,slab1 的chunk为96B,那slab2的所有chunk都是120B,那slab3的所有chunk就是150B,依次类推。因为page大小都是1m,那么相当于chunk的个数就确定了,如果slab1有一个page,那么就应该有10922个chunk,在内存未达到配置内存上限的时候,这个page是可以增加的,但是这里增加的单位就是page,也就是1M,比如已经存了10922个96B以下数据的key,再存一个新的,内存又没有达到配置上限,就会page增加,相应的也就再增加10922个chunk。下面具体代码看一下
启动命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
[root@localhost shell]# memcached -d -p 11212 -u memcached -m 2 -c 1024 -vv 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 |
启动命令就定死了其slab的分配规则,这样就能看到数据616944以上的,一个page也就只能存一个了,也就是就会占用1M的内存,其最大是不能存储1M以上的,除非你将page的配置设置1M以上,或者数据压缩。
这个时候看一下telnet localhost 11212 stats slabs
1 2 3 4 5 6 |
telnet localhost 11212 stats slabs STAT active_slabs 0 STAT total_malloced 0 END |
可以看到,当其没有数据的时候,规则虽然确定,但是没有真的去分配内存的。
使用php脚本存入一个值
1 2 3 4 5 6 7 8 9 |
<?php $memc= new Memcached(); $memc->addServer('127.0.0.1', '11212'); for($i=1;$i<=1;$i++){ $mykey = 'mykey'.$i; $memc->set($mykey,$i); } $value = $memc->get($mykey); print_r($value) |
再次运行stats slabs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 STAT 1:total_pages 1 STAT 1:total_chunks 10922 STAT 1:used_chunks 1 STAT 1:free_chunks 0 STAT 1:free_chunks_end 10921 STAT 1:mem_requested 72 STAT 1:get_hits 1 STAT 1:cmd_set 1 STAT 1:delete_hits 0 STAT 1:incr_hits 0 STAT 1:decr_hits 0 STAT 1:cas_hits 0 STAT 1:cas_badval 0 STAT active_slabs 1 STAT total_malloced 1048512 END |
发现其开始分配内存,并且直接占用了1M内存(total_malloced 1048512),used_chunks 1
我们试试存储超过10922个key
1 2 3 4 5 6 7 8 9 10 |
<?php $memc= new Memcached(); $memc->addServer('127.0.0.1', '11212'); for($i=1;$i<=20922;$i++){ $mykey = 'mykey'.$i; $memc->set($mykey,$i); //$memc->set($mykey,str_repeat('1',130)); } $value = $memc->get($mykey); print_r($value); |
发现结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 STAT 1:total_pages 2 STAT 1:total_chunks 21844 STAT 1:used_chunks 20922 STAT 1:free_chunks 0 STAT 1:free_chunks_end 922 STAT 1:mem_requested 1651548 STAT 1:get_hits 2 STAT 1:cmd_set 20923 STAT 1:delete_hits 0 STAT 1:incr_hits 0 STAT 1:decr_hits 0 STAT 1:cas_hits 0 STAT 1:cas_badval 0 STAT active_slabs 1 STAT total_malloced 2097024 END |
page变成了2, total_malloced也为2M,我们启动的时候-m为2也就是说运行其使用的内存就是2M,那试一下超过2M
1 2 3 4 5 6 7 8 9 |
$memc= new Memcached(); $memc->addServer('127.0.0.1', '11212'); for($i=1;$i<=50922;$i++){ $mykey = 'mykey'.$i; $memc->set($mykey,$i); //$memc->set($mykey,str_repeat('1',130)); } $value = $memc->get($mykey); print_r($value); |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 STAT 1:total_pages 2 STAT 1:total_chunks 21844 STAT 1:used_chunks 21844 STAT 1:free_chunks 0 STAT 1:free_chunks_end 0 STAT 1:mem_requested 1747520 STAT 1:get_hits 3 STAT 1:cmd_set 71845 STAT 1:delete_hits 0 STAT 1:incr_hits 0 STAT 1:decr_hits 0 STAT 1:cas_hits 0 STAT 1:cas_badval 0 STAT active_slabs 1 STAT total_malloced 2097024 END |
我们发现所有的21844个chunk全被占用,内存没有增加,这个时候肯定就有数据比淘汰了,也就是被新的key替换了。
但是如果我们测试96B以上的数据
1 2 3 4 5 6 7 8 9 10 |
<?php $memc= new Memcached(); $memc->addServer('127.0.0.1', '11212'); for($i=1;$i<=1;$i++){ $mykey = 'newmykey'.$i; //$memc->set($mykey,$i); $memc->set($mykey,str_repeat('1',130)); } $value = $memc->get($mykey); print_r($value); |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 STAT 1:total_pages 2 STAT 1:total_chunks 21844 STAT 1:used_chunks 21844 STAT 1:free_chunks 0 STAT 1:free_chunks_end 0 STAT 1:mem_requested 1747520 STAT 1:get_hits 3 STAT 1:cmd_set 71845 STAT 1:delete_hits 0 STAT 1:incr_hits 0 STAT 1:decr_hits 0 STAT 1:cas_hits 0 STAT 1:cas_badval 0 STAT 5:chunk_size 240 STAT 5:chunks_per_page 4369 STAT 5:total_pages 1 STAT 5:total_chunks 4369 STAT 5:used_chunks 1 STAT 5:free_chunks 0 STAT 5:free_chunks_end 4368 STAT 5:mem_requested 206 STAT 5:get_hits 1 STAT 5:cmd_set 1 STAT 5:delete_hits 0 STAT 5:incr_hits 0 STAT 5:decr_hits 0 STAT 5:cas_hits 0 STAT 5:cas_badval 0 STAT active_slabs 2 STAT total_malloced 3145584 END |
其占用的slab5,内存突破了2M,又加了1M,我换了key,所以肯定是新增,slab1的没有动,还是占满的。这个数据为什么占用的比实际130B大,使用的是slab5应该是key等也需要内存的占用,这些不管,这也说明了-m参数的原则是在slab不变的情况下,不能突破限制,但是如果遇到数据有其他slab,那么他就会再增加一个page的大小,这也就是有的人说-m配置没有作用的原因,实际上不是没有作用。我们测试能否这个slab5出现两个page,我只要循环大于4369即可,因为240一个chunk这里STAT 5:chunks_per_page 4369
1 2 3 4 5 6 7 8 9 10 |
<?php $memc= new Memcached(); $memc->addServer('127.0.0.1', '11212'); for($i=1;$i<=5000;$i++){ $mykey = 'newmykey'.$i; //$memc->set($mykey,$i); $memc->set($mykey,str_repeat('1',130)); } $value = $memc->get($mykey); print_r($value); |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 STAT 1:total_pages 2 STAT 1:total_chunks 21844 STAT 1:used_chunks 21844 STAT 1:free_chunks 0 STAT 1:free_chunks_end 0 STAT 1:mem_requested 1747520 STAT 1:get_hits 3 STAT 1:cmd_set 71845 STAT 1:delete_hits 0 STAT 1:incr_hits 0 STAT 1:decr_hits 0 STAT 1:cas_hits 0 STAT 1:cas_badval 0 STAT 5:chunk_size 240 STAT 5:chunks_per_page 4369 STAT 5:total_pages 1 STAT 5:total_chunks 4369 STAT 5:used_chunks 4369 STAT 5:free_chunks 0 STAT 5:free_chunks_end 0 STAT 5:mem_requested 912753 STAT 5:get_hits 2 STAT 5:cmd_set 5001 STAT 5:delete_hits 0 STAT 5:incr_hits 0 STAT 5:decr_hits 0 STAT 5:cas_hits 0 STAT 5:cas_badval 0 STAT active_slabs 2 STAT total_malloced 3145584 |
内存没有再增加,slab5的所有chunk被占满了,page没有增加,这里也就是说,其做大就是内存达到-m限制后,再新增的slab区间,每增加一个,就加一个page的大小,比如-m 2,如果slab1已经占满了2M,那其极限内存,就是2M+42*page的大小,不会再变化。所以其策略就是,一旦slab被分配是不会再变化的
程序本天成,妙手偶得之!我们只是代码的搬运工!
转载请注明:http://www.521php.com/archives/2039/
2016年12月10日 下午 3:16 广告任务网 | 引用 | #1
日复一日,年复一年,你的博客,让人流连!