memcache redis mysql 过期策略和内存淘汰机制对比

memcache 和 redis 作为时下大热的缓存,当然是高并发项目必用的,而使用过程中难免出现内存溢出的情况,最近看了很多资料整理出该篇文章,同时和mysql对比做个详细总结。

内存未满时

  • memcache
    1.4.25及以前版本使用 惰性删除 机制,不会自动清理,page一旦被分配在重启前也不会被回收或者重新分配,直到访问发现item 已过期 才删除。
    之后版本会隔指定时间进行查找是否有过期item,如果有过期会进行删除处理。

  • redis
    定期调用databasesCron()函数触发 清理策略 ,频率由配置文件中的hz参数决定,代表了一秒钟内,后台任务被调用的次数,通常100ms执行一次,同时设定一个阈值,比如25%,当抽取到100条数据中超过25条过期,就清理后继续抽取,若小于25条则休眠100ms。

    redis为了防止占用,对每次淘汰任务执行的最大时长也有一个限定,这样保证了每次主动淘汰不会过多阻塞应用请求,不得不说这是个很好的解决办法。

内存溢出时

  • memcache
    当memcache的总内存达到了设置的最大内存,表示所有的slab能够使用的page都已经固定,这时如果还有数据放入,将导致memcache使用 LRU策略 剔除数据。这里的LRU策略不是针对所有的slabs,而是只针对新数据应该被放入的slab,并不是发生了LRU就代表memcache超载。
    当你chunk大小设置不合理的时候,比如slab20 chunk大小非常大,一开始占用了很多内存,但是之后不论是否过期,不被再次利用到的时候就一直处于内存中,这样,当比较小的slab1中的chunk 满了,也没有内存新建slab并分割和slab1同样规格的chunk的时候,memcache就会启动LRU,来清理这个slab下的数据。这种情况就会造成内存的 极大浪费命中率下降,参见我的另一篇博客《memcache存储原理和命中率》
    LRU原理是根据访问时间把key放入一张链表,不断更新,这样队尾就是最长时间没有使用的。

  • redis
    (1)volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。

    (2)volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。

    (3)volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。

    (4)volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。

    (5)allkeys-lru:从数据集中挑选最近最少使用的数据淘汰

    (6)allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。

    (7)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

    (8) no-enviction(驱逐):禁止驱逐数据,这也是默认策略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失。

注意这个清理过程是 阻塞 的,直到清理出足够的内存空间。所以如果在达到maxmemory并且调用方还在不断写入的情况下,可能会反复触发主动清理策略,导致请求会有一定的延迟。

  • mysql
    有人问mysql有内存淘汰吗?当然有,mysql可以缓存查询的结果集,但是这个很鸡肋,因为一旦有某一行的增删改就全表缓存失效了,相比之下sql server缓存的是不带参数的中间结果集,相当于mysql的存储过程语句,不过它们共同点都是 按时间顺序越早出场淘汰
    mysql非常牛逼的是它的 缓冲池 淘汰机制,就是在查询过程中产生的临时表等数据,因为和memcache和redis不一样的是,mysql是按页读取数据,不都是要用到的,所以参考Java的GC垃圾回收机制,把缓存数据分为新生代和老生代,数据使用放到老生代头部,达到一定时间才放到新生代里,这样有效防止同时读取大量数据把原有热数据清理掉的情况。
发布了5 篇原创文章 · 获赞 0 · 访问量 76

猜你喜欢

转载自blog.csdn.net/jy615183000/article/details/105334830