• 记录一次 memcache缓存 数据被踢现象


    最近我们广告的曝光量有点少,于是开始查找原因,曝光量少就是广告没有下发,广告没有下发无非就是接口超时和缓存没数据
     
    首先,我们的前端机针对评论流的广告位有六台,在查某条广告的缓存时发现,其中一台可以获取到,其余的几台都获取不到mc的
     
    数据,但是我们连到这台没有缓存数据的机器上发现,可以正常的get或者set数据。那就开始看mc服务器的参数,于是发现正常的
     
    机器参数如下 : echo -e "stats quit " | /usr/bin/nc 127.0.0.1 11512

    中的evictions为0,其他出现缓存数据没有的机器这个值是:

    对 evictions的值达到了接近3亿,也就是mc内存不够,启动了lru策略,值就是LRU释放的对象数目
     
    其中详细解释一下其实的参数和作用
    pid:mc服务器的进程id
    uptime:服务器已经运行的秒数(可以知道mc运行多久了)
    time:服务器的当前时间戳
    version:mc版本
    pointer_size:当前操作系统指针大小,反映了操作系统的位数,64意味着mc服务器是64位的
    rusage-user:进程的累积用户时间
    rusage_system:进程的累积系统时间
    curr_connections:当前打开着的连接数
    total_connections:当服务器启动以后曾经打开过的连接数
    connection_structures:服务器分配的连接构造数
    cmd_get:get命令总请求次数
    cmd_set:set命令总请求次数
    cmd_flush:flush_all命令总请求次数
    get_hit:总命中次数,重要,缓存最重要的参数就是缓存命中率,以get_hits/(git_hits + get_misses)表示
    get_misses:总未命中次数
    auth_cmds:认证命令的处理次数
    auth_errors:认证失败的处理次数
    bytes_read:总读取的字节数
    bytes_written:总发送的字节数
    limit_maxbytes:分配的内存总大小(字节)
    accepting_conns:服务器是否达到过最大连接
    threads:当前线程数
    bytes:当前存储占用的字节数
    curr_items:当前存储的数据总数
    total_items:启动以来存储的数据总数
    evictions:LRU释放的对象数目(如果还有足够内存,那么这个值应该是0或者不再增长) 为了给新的数据项目释放空间,从缓存移除的缓存对象的数目,比如超过缓存大小时根据LRU算法移除的对象以及过期的对象。
     
    以上就是需要知道的使用stats命令查看mc的缓存相关参数
     
    因为我们看到经常丢失的mc的服务器evictions参数都是大于1,说明内存不足
    在看一下运维给memcache是实例分配的大小是:

    一个G的内存,-m表示所以slab class 可用内存的上限,以MB为单位,现在1024MB也就是分配了1G的内存给MC
     
    现在得出的结果就是内存不够导致的mc使用lru策略删除最近最少使用的key的value值。
    那为什么最近刚写入的数据就被清空了呢,按照策略难道不应该是很久之前的数据被清空么?
    借鉴网上的一个设计场景:
    1、部署一个memcached测试环境,分配比较小的内存
    2、设置一条永远不过期的数据到memcached中,然后再get一次,这条数据后续应该存在LRU队尾
    3、每隔1s向memcached set并get一次 10000条数据,过期时间设置为3s
    4、一段时间后,stats命令现实evictions=1
     
    按照之前我的理解,2步的数据永远不会被踢,因为有足够过期的数据空间可以给新来的数据用,lru淘汰算法应该跳过没过期的数据,但结果证实是错误的,以上业务
    的服务器发生被踢的现象是由于保存了大量存活期短的keyvalue,且key是不重复的,另外又有一业务保存了小量不过期的数据,因此导致不过期的数据惨遭被挤到队列踢出
    这是网上找到的一段测试用例。
     
    针对上面那个场景总结以下几条解决方式:
    • 过期的数据如果没被显式调用get,则也要占用空间。
    • 过期的不要和不过期的数据存在一起,否则不过期的可能被踢。
    • 从节约内存的角度考虑,即使数据会过期,也不要轻易使用随机字符串作为key,尽量使用定值如uid,这样占用空间的大小相对固定。
    • 估算空间大小时候请用slab size计算,不要按value长度去计算。
    • 不要把cache当作更快的key value store来用, cache不是storage。
     
    而我遇到的就是因为内存不够而mc使用LRU策略来解决内存不够,因此导致的新写的key也被踢出去了。因为存储已经满了,那旧数据即使没有到过期时间,也会被移除。
     
    下面分别说一下mc的工作原理和工作流程以及优化建议:
    mc工作原理:
    基本概念:
    slab:它是在启动mc实例的时候预处理好的,每个slab对应一个chunk size
    page:可以理解为内存页,大小固定事1M,slab会在存储请求时向系统申请page,并将page按照chunk size进行切割
    chunk:保存用户数据的最小单位,用户数据item(包括key,value)最终会保存到chunk内。chunk规格时固定的,如果用户数据放进来后还有剩余则剩余部分不做其他用途
     
    mc工作流程:
    mc实例启动,根据-f和-n来进行预分配slab,然后每个slab分配一个page。当用户发来存储请求时(key、value)mc会计算key+value的大小,看看属于那个slab,确定slab后看里面
    的释放有空闲chunk放key+value。如果不够就再向系统申请一个page,申请后将page按本slab的chunk size进行切割,然后分配一个存放用户数据
    注:
    1、chunk是在page里面划分的,而page时固定1M,所以chunk最大不能超过1M
    2、chunk实际占用内存要加48b,因为chunk数据结构本身需要占用48B
    3、如果用户数据大于1M,mc会将其切割,放到多个chunk内
    4、已分配出去的page不能回收
     
    优化建议:
    1)-n 参数的设置,注意将此参数设置为1024可以整除的数(还要考虑48B的差值),否则余下来的部分就浪费了。
    2)不要存储超过1m的数据。因为要拆成多个chunk,计算和时间成本都成倍增加。
    3)善用stats命令查看memcached状态。
    4)消灭eviction(被删除的数据)。
    造成eviction是因为内存不够,有三个思路:
    (1)在CPU有余力的情况下开启压缩(PHP扩展);
    (2)增加内存;
    (3)调整-f参数,减少内存浪费。
    5)调整业务代码,提高命中率。
    6)缓存小数据。省带宽,省网络I/O时间,省内存。
    7)根据业务特点,为数据尺寸区间小的业务分配专用的memcached实例。这样可以调小 -f 参数,使数据集中存在少数几个slab上,内存浪费较少。
    启动时最重要的参数:
    -m 整个memcached最大内存
    -f chunk大小增长因子
    -n chunk最小分配空间
    -C 禁用CAS
    -vvv 打印详细信息
    我们通过计算可以看出,每个slab的chunk size大小都是上一个大小的1.25倍,1.25就是memcached启动时制定的-f的值,所以,要根据不同的业务场景调整,既要尽可能少的减少内存浪费,又要存得下我们业务中的数据,再举个例子,加入我们的业务很BT,大小都是200kb,我们该怎么做?那当然是要保证我们申请的chunk大小都是200kb了,虽然memcached并不支持这么做。假如创建了38个slabs,最后数据全都落到了第20-25个slab中,那0-19和26-38的内存就被浪费了,这只是能评估出来的,还有一种情况,加入我们的chunk大小是500kb,数据才200kb,因为每个chunk都只能存储一个,所以一个chunk就会有500kb-200kb的空间浪费,如果有一千万个chunk要存,那将会浪费多少空间?所以,在使用memcached之前,先要评估你的数据,根据它去调整-f因子。
     
    memcached删除机制
    memcached通过slab机制不会释放已分配的内存,记录超时或删除后不会释放内存,而是重复利用
    mc优先使用超时的记录的内存空间
     
    下面说一下LRU策略底层
    当缓存没有内存可以分配给新的元素的时候,memcached会从LRU链表的尾部开始淘汰一个ITEM,不管这个ITEM是否还在有效期都将会面临淘汰。LRU链表插入缓存ITEM的时候有先后顺序,所以淘汰一个ITEM也是从尾部进行 也就是先淘汰最早的ITEM。
     
    这就解释了 我们刚写入的数据,在陆续写入更多后,最先被写入的就被淘汰了,因为内存不够,而LRU策略也是从队尾开始淘汰。
     
    最后总结:很久之前在学习memcache时,也知道LRU策略,因为在面试的时候会问哈哈,但是只是按照书面的理解了一下,在实战中没有遇到,就算遇到之前也是有运维。
    但是你这样永远不知道咋回事。所以后来的我每次遇到这些问题,就会在屁股后面跟着
    运维,一边时配合运维查问题,确保不是业务逻辑带来的,另一方面也是在学习遇到这种问题看看别人是怎么解决的,然后再网上查一下为什么会这样,最后总结整理一下知识点,加深印象。感谢身边优秀的同学们。
     

  • 相关阅读:
    WebService-CXF 学习笔记
    Java中对于ClassLoader类加载器 嵌套了深度技术的价值
    WebService学习笔记
    MongoDB学习笔记
    java.io.IOException: java.io.FileNotFoundException: /tmp/tomcat.2457258178644046891.8080/work/Tomcat/localhost/innovate-admin/C:/up/154884318438733213952/sys-error.log (没有那个文件或目录)
    Node Sass does not yet support your current environment: Linux 64-bit with Unsupported runtime
    Mysql优化
    SI架构设计与实践
    高并发高可用处理大数据量
    虚拟机安装CentOS详细操作
  • 原文地址:https://www.cnblogs.com/weiluoyan/p/11855876.html
Copyright © 2020-2023  润新知