分享一个memcached丢失数据后的解决办法
在看这篇文章之前,最起码要知道memcached系列前三篇的知识,最重要的就是memcached的命令以及memcached内存管理策略,这是理解memcached为什么会丢数据的基础。
需要你知道的memcached的相关命令:
stats – 查看memcached状态信息的命令
stats slabs – 查看memcached各个slab的状态信息命令
需要你知道的memcached的相关知识:
1)Memcached的内存分配策略是按slab需求分配page,每一个page是1G,各slab按需使用chunk存储key。
2)各个slab之间chunk大小的增长因子倍数是1.25倍。
3)Key存储的方式是只会存储在离自己最近且大于或等于自己的slab中,不会存储到其他slab。
4)Memcached分配出去的page不会被回收或者重新分配的。
5)每一个slab空闲的chunk不会借给任何其他slab使用。
大概知道了上面这些,下面开始言归正传
由于公司使用了很多memcached缓存服务器,一直也都没有什么问题。最近业务出了一些问题,开发说是memcached中缓存的那个大key(大概25K)丢失了导致了业务出了问题(其实这里需要说一下,memcached只是缓存不可用来保证数据安全性的,开发使用姿势有问题)。当时跟他确认是否真的存进去了,根据开发的描述那个key肯定是存进去了(缓存是3天),但是不到5秒钟就消失了。。。。。。
当时我唯一能想到的就是内存满了,memcached使用了LRU算法把旧的key清除了(默认开启LRU算法),这是一个很合理的解释。于是看了对memcached的监控,一个memcached实例给的是18G内存。
晕菜了,最高只用了3点多个G而已,我的一个实例可是有18G内存可用的啊。既然内存没有用完,为什么会丢Key呢?我开始怀疑是不是memcached有问题了。网上找了一通也没有找到有说数据丢失的问题。
第二天,跟公司的几个同事一通讨论,由于从监控上面看到的内存确实没有用完,所以刚开始重心也就没有往LRU剔除数据上面想。想过说可能因为key太多,准备调整各个slabs之间的增长因子,但以我对memcached的简单了解,slabs的调整只是为了节省内存而已。如果你要存储的key都普遍很小,slabs就调小一点,但最小不能小于1;如果你的key普遍都很大,slabs就调大一点,都可能会节省内存。后来也想过说关闭memcached的LRU功能,不让剔除旧的key,都让key自动过期,但风险就是如果内存写满了,再次写入key时就会报错。
后来又是一番讨论,应了那句话人多力量大啊。既然memcached对于申请过的内存不会释放,最终决定先把memcached中所有的slabs的Page加起来,看一下到底用了多少内存。其实这里有一个非常简便的方法就是使用ps或top命令看memcached进程占用了多少内存,当时没有想到,后来才去看这个线索。
1 2 |
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND nobody 2453 10.3 14.3 19345724 19024324 ? Sl Jan22 19217:57 /usr/bin/memcached -m 18432 -p 11811 -u nobody |
VSZ:虚拟内存集
RSS:常驻内存集
1 2 |
19024324 / 1024 /1024 18 |
刚好使用了18G内存。
但是当时我没有看进程,而是用了最传统的方式(其实就算使用ps命令看到了memcached已经把内存占完了也不能够全部解决问题,因为还是要明白memcached的工作原理,为什么内存被全部占用了)。
首先用stats命令看了一下memcached实际占用内存大小就是3个多G。
1 2 |
root@shd:~ # echo "stats" | nc 127.0.0.1 11811 | grep "bytes" STAT bytes 3242867050 |
然后把所有slabs的Page占用给列出来(这里省略了一些),加起来刚好是18G,这就可以理解为什么memcached会丢数据了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
root@shd:~ # echo "stats slabs" | nc 127.0.0.1 11811 | grep "total_pages" .... STAT 11:total_pages 69 STAT 12:total_pages 170 STAT 13:total_pages 110 STAT 14:total_pages 118 STAT 15:total_pages 78 STAT 16:total_pages 434 STAT 17:total_pages 54 STAT 18:total_pages 77 STAT 19:total_pages 12033 STAT 20:total_pages 645 STAT 21:total_pages 227 STAT 22:total_pages 21 ..... |
从这个列出的pages可以看出,发现这个19号slabs居然申请过12G内存(12033X1M),然后又把这个19号slabs整体信息给提取出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
root@shd:~ # echo "stats slabs" | nc 127.0.0.1 11811 STAT 19:chunk_size 5680 STAT 19:chunks_per_page 184 STAT 19:total_pages 12033 STAT 19:total_chunks 2214072 STAT 19:used_chunks 97418 STAT 19:free_chunks 2116654 STAT 19:free_chunks_end 0 STAT 19:mem_requested 528177337 STAT 19:get_hits 1150123848 STAT 19:cmd_set 60974728 STAT 19:delete_hits 170666 STAT 19:incr_hits 0 STAT 19:decr_hits 0 STAT 19:cas_hits 0 STAT 19:cas_badval 0 STAT 19:touch_hits 0 |
从这个信息可以看出,19号slabs存储的key为5k左右大小,申请过12033个Page,一共有2214072个chunk。但是这些chunk只是使用了97418个,还有2116654个没有使用,比例占20/1左右。我去,也就是说这个存储5k左右key大小的slabs一共申请过12G的内存,但是现在只用了0.5G左右,其余的11.5G都处于某种程度的浪费阶段。
为什么会出现这种情况呢?这个19号slabs竟然申请过12G的内存,那么就说明在某一个阶段内就申请过这么多内存,这个slabs存的都是5k左右大小的key了(每个key加上67-69字节的开销)。由于memcached的一些机制,前面已经说了,对于每个slabs申请过的内存不会再释放。
那么,对于此次memcached丢key的原因简单阐述一下就是,在5k左右大小的key,在某一个时间段内(开发说都是缓存3天)使用到了12G内存,然后又释放了。但是后来就没有再使用了这个19 slabs,所以导致我们看到的实际内存大小一直在3个G左右。当这个18G内存申请完了之后,又由于19号slabs只能存储5k左右大小的key,导致新的稍微大一点的key存储到memcached之后,memcached就开始使用LRU算法剔除旧的Key了。明白了这些之后,回到开始的问题,就可以理解key存5秒就丢失的原因了。
这里最后再使用stats命令看看memcached状态。
1 2 3 4 5 6 7 8 9 10 |
root@shd:~ # echo "stats" | nc 127.0.0.1 11811 STAT curr_items 17336196 STAT total_items 3783232926 STAT expired_unfetched 163137387 STAT evicted_unfetched 137254305 STAT evictions 153844054 STAT reclaimed 193612638 STAT crawler_reclaimed 0 STAT lrutail_reflocked 500 END |
其实当你看到evictions参数有值得时候,就说明是memcached已经使用LRU开始剔除旧的key了,单位为字节,这里可以看出已经剔除了15M大小的key了。此参数有数值就是有问题,说明内存满过。
处理方式
1)把memcached重启,释放内存,重新分配slabs,但是注意会清除所有的缓存。
2)查看历史监控,查看在什么时间段写入了大量的key,导致19号slabs一直申请内存。
3)根据进程把memcached真实占用的内存给监控起来。
4)也是最重要的一点,memcached只是个缓存,尽量避免存储对安全性要求高的数据,这类数据可以用redis做缓存。
总结
我自己对于这个问题处理完了之后,对memcached的运行机制可以说是更加理解了,使用起来也更加得心应手了。就花了点时间赶快整理出来分享给大家,写完你看完也能对memcached更加了解,碰到此类问题也能够解决。最后想说,遇到事情多讨论,这是人多力量大。