Jack__Frost的博客通过本文主要向大家介绍了redis,内存优化,内存淘汰机制等相关知识,希望本文的分享对您有所帮助
每台redis的服务器的内存都是有限的,而且也不是所有的内存都用来存储信息。而且redis的实现并没有在内存这块做太多的优化,所以实现者为了防止内存过于饱和,采取了一些措施来管控内存。
文章结构:(1)内存策略;(2)内存释放机制原理;(3)项目中如何合理应用淘汰策略;(4)单机版Redis内存优化注意点。
本系列:
(1) Redis系列(一)–安装、helloworld以及读懂配置文件
(2)Redis系列(二)–缓存设计(整表缓存以及排行榜缓存方案实现)
(3) Redis系列(三)–过期策略
一、内存策略:先来吃份官方文档
最大内存的设置是通过设置maxmemory来完成的,格式为maxmemory bytes ,当目前使用的内存超过了设置的最大内存,就要进行内存释放了, 当需要进行内存释放的时候,需要用某种策略对保存的的对象进行删除。Redis有六种策略(默认的策略是volatile-lru。)
redis中当内存超过限制时,按照配置的策略,淘汰掉相应的key-value,使得内存可以继续留有足够的空间保存新的数据。redis 确定驱逐某个键值对后,会删除这个数据并,并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。
(1)volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。
(2)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
(3)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
(4)allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
(5)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
(6)no-enviction:禁止淘汰数据
除此之外还有一个配置项,就是maxmemory-samples,默认值是3,因为上面的策略代码实现的都是近似算法,所以不管是lru算法,还是ttl,都并不是在数据库中所有的数据为基础的算法,因为当数据库的数据很多的时候,这样效率太低,所以代码中都是基于maxmemory-samples个数据的近似算法。详情请读下文。
置换策略是如何工作的:
1)客户端执行一条新命令,导致数据库需要增加数据(比如set key value)
2)Redis会检查内存使用,如果内存使用超过maxmemory,就会按照置换策略删除一些key
3)新的命令执行成功
注意:
如果我们持续的写数据会导致内存达到或超出上限maxmemory,但是置换策略会将内存使用降低到上限以下。
如果一次需要使用很多的内存(比如一次写入一个很大的set),那么,Redis的内存使用可能超出最大内存限制一段时间。
二、内存释放机制原理:
(1)概述:
当mem_used内存已经超过maxmemory的设定,对于所有的读写请求,都会触发redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。注意这个清理过程是阻塞的,直到清理出足够的内存空间。所以如果在达到maxmemory并且调用方还在不断写入的情况下,可能会反复触发主动清理策略,导致请求会有一定的延迟。
清理时会根据用户配置的maxmemory-policy来做适当的清理(一般是LRU或TTL),这里的LRU或TTL策略并不是针对redis的所有key,而是以配置文件中的maxmemory-samples个key作为样本池进行抽样清理。
maxmemory-samples在redis-3.0.0中的默认配置为5,如果增加,会提高LRU或TTL的精准度,redis作者测试的结果是当这个配置为10时已经非常接近全量LRU的精准度了,并且增加maxmemory-samples会导致在主动清理时消耗更多的CPU时间,有如下建议:
1)尽量不要触发maxmemory,最好在mem_used内存占用达到maxmemory的一定比例后,需要考虑调大hz以加快淘汰,或者进行集群扩容。
2)如果能够控制住内存,则可以不用修改maxmemory-samples配置;如果Redis本身就作为LRU cache服务(这种服务一般长时间处于maxmemory状态,由Redis自动做LRU淘汰),可以适当调大maxmemory-samples。
(2)内存管理源码解析:参考博文
Redis释放内存是由函数freeMemoryIfNeeded完成的,redis用processCommand函数处理每条命令,函数中在真正处理命令之前都会调用freeMemoryIfNeeded函数,这个函数会判断当前使用的内存是否超过了最大使用内存,如果超过,就会根据内存释放策略释放内存。
freeMemoryIfNeeded函数首先会计算出当前使用了多少内存,注意,这里并不会包括slaves 输出缓存以及AOF缓存,源码如下:
int freeMemoryIfNeeded(void) {
size_t mem_used, mem_tofree, mem_freed;
int slaves = listLength(server.slaves);
/* Remove the size