潍坊网络科技有限公司,宝塔wordpress优化,wordpress 多站,互联网站源码Redis之所以性能强#xff0c;最主要的原因就是基于内存存储#xff0c;然而单节点的Redis其内存大小不宜过大#xff0c;否则会影响持久化或主从同步的性能。
Redis内存满了#xff0c;会发生什么#xff1f;
在Redis的运行内存达到了某个阈值#xff0c;就会触发内存…
Redis之所以性能强最主要的原因就是基于内存存储然而单节点的Redis其内存大小不宜过大否则会影响持久化或主从同步的性能。
Redis内存满了会发生什么
在Redis的运行内存达到了某个阈值就会触发内存淘汰机制 防止把内存撑爆这个阈值就是我们设置的最大运行内存。
我们可以通过修改redis.conf配置文件来设置Redis的最大内存配置项为maxmemory
Redis默认情况下是没有对最大内存大小做限制的默认情况下Redis就是根据你当前的服务器里面当前最多可以申请到的内存大小来去做限制
# 格式
# maxmemory bytes
# 例如
maxmemory 1gb当内存使用达到上限就无法存储更多数据了因此为了解决这个问题Redis内部会有两套内存回收的策略
内存过期策略内存淘汰策略 内存过期策略 - 过期key处理 - 过期删除策略
如何设置过期时间
expire key n秒 pexpire key n毫秒 set key value ex n秒 set key value nx n毫秒
查看某个key剩余的存活时间TTL key
我们可以通过expire / EX命令给Redis的key设置TTL过期时间 / 存活时间单位秒当key的TTL到期以后即当过期时间到了以后再次访问该key时返回的是nil说明这个Key已经不存在了对应的内存也得到释放从而起到内存回收的目的。
# 写入一条数据
set num 123
# 设置20秒过期时间
expire num 20
# 写入一条数据并设置20s过期时间
set num EX 20
Redis是如何知道一个key是否过期呢 Redis本身是一个典型的key-value的键值型内存存储数据库因此所有的key-value都保存在Dict结构中在其redisDb结构体中有两个Dict也就是哈希表一个用来记录KEY-VALUE键值对当然存的不是真正的Key-Value存储的其实是RedisObject对应的内存地址的指针另一个用来记录key的TTL。
过期字典存储在 redisDb 结构中如下
来看下redisDb的底层源码
typedef struct redisDb {dict dict; / The keyspace for this DB , 也就是存放KEY和VALUE的哈希表*/dict *expires; /* 同样是哈希表但保存的是设置了TTL的KEY及其到期时间*/dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/dict *ready_keys; /* Blocked keys that received a PUSH */dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS /int id; / Database ID, 0 ~ 15 /long long avg_ttl; / Average TTL, just for stats /unsigned long expires_cursor; / Cursor of the active expire cycle. */list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
每当我们对一个key设置了过期时间后Redis会把该key带上过期时间存储到一个过期字典(expires dict)中也就是说「过期字典」保存了数据库中所有 key 的过期时间。
当我们查询一个 key 时Redis 首先检查该 key 是否存在于过期字典中
如果不在则正常读取键值如果存在则会获取该 key 的过期时间然后与当前系统时间进行比对如果比系统时间大那就没有过期否则判定该 key 已过期 查询到对应的TTL加以判断即可。
是不是TTL到期就立即删除了呢
TTLTime To Live的含义是存活时间。
Redis并不会在Key过期时立刻删除KEY因为要实现这样的效果就必须给每一个过期Key设置一个定时器并监控这些Key的过期状态然后去做判断在key过期的那一刻给它立刻删掉 定时删除它的优点就是保证过期key会被尽快删除也就是内存可以尽快得到释放因此定时删除对内存是最友好的如果说我们的key比较少那还好但是如果我们的key非常的多达到数十万甚至数百万那么这些key如果我们都给它设置这样一个定时器无论是对CPU还是对内存都会带来极大的负担这样一来就会严重影响到Redis服务本身的一个性能所以说这个是没有办法接受的因此我们在实际应用当中Redis采用的并不是立即删除而是惰性删除 周期删除 Redis 使用的过期删除策略是「惰性删除定期删除」这两种策略配和使用删除的对象是已过期的 key。 Redis的过期KEY删除策略有两种 惰性删除 周期删除或定期删除
惰性删除
顾名思义就是TTL过期后不会立刻删除惰性删除策略的做法是不主动删除过期键而是在访问使用一个key的时候判断当前key有没有设置TTL过期时间如果有则检查该key的存活时间如果发现key已经过期才执行删除操作如果没有过期不做任何处理然后返回正常的键值对给客户端。
惰性删除策略的优缺点 优点 因为每次访问时才会检查该key是否过期因此惰性删除策略可以节省CPU资源对CPU时间最友好。 缺点 如果一个key已经过期而这个key又仍然保留在数据库中那么只要这个过期key一致没有被访问它所占用的内存就不会释放会造成一定的内存空间浪费所以惰性删除策略对内存不友好 这不就是会导致内存泄漏吗 周期删除 / 定期删除策略
顾名思义就是通过一个定时任务每隔一段时间周期性的从数据库中抽取一定数量的key进行检查并删除其中的过期key。
Redis默认会每秒进行10次过期检查此配置可以通过Redis的配置文件redis.conf进行配置配置键为hz它的默认值是hz 10每次检查数据库并不是遍历过期字典中的所有key而是从数据库中随机抽取一定数量的key进行过期检查
从过期字典中随机抽取20个key检查这20个key是否过期并删除已过期的key如果本轮检查的已过期key的数量超过5个5 / 20 1 / 4 25%也就是「已过期 key 的数量」占比「随机抽取 key 的数量」大于 25%则继续随机抽查重复步骤1如果已过期的key的比例小于25%则停止继续删除过期key退出本轮检查然后等待下一轮再检查。
可以看到定期删除是一个循环的流程。
Redis为了保证定期删除不会出现循环过度导致线程卡死现象为此增加了定期删除循环流程的时间上限默认不会超过25ms超出时间限制则退出。
定期删除策略的优缺点 优点 定期删除是Redis的主动删除策略它可以确保过期key能够及时被删除 缺点 会占用CPU资源去扫描key可能会影响到Redis的性能 可以看到惰性删除策略和定期删除策略都有各自的优缺点所以Redis选择「惰性删除定期删除」这两种策略配和使用以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。 如果过期键没有被访问而定期删除又跟不上新键产生的速度内存不就慢慢耗尽了吗
内存淘汰策略
内存淘汰
就是当Redis的内存使用达到设置的阈值时Redis就会主动挑选部分key删除以释放更多内存的流程这就叫做内存淘汰机制。内存淘汰策略是解决内存过大的问题。
内存淘汰时机
当内存达到阈值时执行内存淘汰但问题是Redis什么时候会去判断内存是否达到阈值呢
Redis每次执行任何命令时都会判断内存是否达到阈值。
当Redis内存不足时会怎么做
这取决于配置的内存淘汰策略Redis支持很多种内存淘汰策略例如LRU、LFU、Random但默认的策略是直接决绝新的写入请求而如果设置了其它策略则会在每次执行命令后判断内存占用是否达到阈值如果达到阈值则会基于配置的内存淘汰策略尝试进行内存淘汰直到占用内存小于阈值为止。
Redis 内存淘汰策略有哪些
Redis支持内存淘汰配置参数maxmemory-policy决定了内存淘汰策略这个参数一共有8个枚举值也就是说Redis内存淘汰策略共有8种这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略 1. 不进行数据淘汰的策略
noevictionRedis3.0之后默认的内存淘汰策略 禁止删除数据它表示当Redis的运行内存超过最大设置内存时也就是当Redis内存满时不淘汰任何键值对数据而是不再提供服务不允许写入新数据Redis的写命令会直接返回错误信息但是读命令还是可以正常返回。 2. 进行数据淘汰的策略
针对「进行数据淘汰」这一类策略又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。 在设置了过期时间的数据中进行淘汰
volatile-random随机淘汰设置了过期时间的任意键值 - 从已设置过期时间的数据集中任意选择数据淘汰volatile-ttl从已设置过期时间的数据集中挑选将要过期的数据淘汰比较key的剩余TTL值TTL越小越先被淘汰。volatile-lruRedis3.0 之前默认的内存淘汰策略LRULeast Recently Used最近最久未使用利用LRU算法淘汰所有设置了TTL过期时间的键值中最久未使用的键值volatile-lfuRedis 4.0 后新增的内存淘汰策略LFULeast Frequently Used最少频率使用淘汰所有设置了TTL过期时间的键值中最少使用的键值
在所有数据范围内进行淘汰
allkeys-random对全体key随机进行淘汰;allkeys-lruLRULeast Recently Used最近最久未使用淘汰全体键值中最久未使用的键值allkeys-lfuRedis 4.0 后新增的内存淘汰策略LFULeast Frequently Used最少频率使用淘汰整个键值中最少使用的键值即访问频率最低的那个key-value。
比较容易混淆的有两个算法
LRULeast Recently Used最近最久未使用根据访问时间淘汰会选择淘汰最近最少使用的数据用当前时间减去最后一次访问时间这个值越大则淘汰优先级越高。LFULeast Frequently Used最少频率使用根据访问频率淘汰LFU 算法是根据数据访问次数来淘汰数据的它的核心思想是“如果数据过去被访问多次那么将来被访问的频率也更高”所以 LFU 算法会统计每个key的访问频率值越小淘汰优先级越高。
Redis 4.0开始支持基于LFU算法的淘汰策略
Redis为什么新增了LFU淘汰策略
为什么Redis 4.0有了LFU 比如Redis中的一个键之前一直都没有被访问过最近突然被访问了一次如果使用LRU淘汰策略就很难被淘汰因为LRU会把它定义为热键而使用LFU淘汰策略该key就可能很快被淘汰因为LRU优先淘汰最近未被使用的而LFU淘汰的是最近访问频率最低的。LFU比LRU淘汰更精确有助于提升Redis的缓存命中率。 Redis怎么知道某个KEY的最后一次访问时间或者是访问频率呢 redisObject结构体当中的lru就是记录最近一次访问时间和访问频率的以低8位无符号数字来记录逻辑访问次数。逻辑访问次数又是怎么回事呢8位无符号数字最大才255访问次数超过255怎么办
逻辑访问次数是如何计算的
由于记录访问次数的只有8bit即便是无符号数最大值只有255不可能记录真实的访问次数因此LFU的访问次数之所以叫做逻辑访问次数是因为并不是每次key被访问都计数Redis统计的其实是逻辑访问次数这其中是一个计算公式会根据当前的访问次数做计算结果要么是次数 1要么是次数不变且最大不超过255除此以外逻辑访问次数还有一个衰减周期访问次数会随时间衰减默认为1分钟即每隔1分钟逻辑访问次数会 -1这样逻辑访问次数就能基本反映出一个key的访问热度了。 显然LFU的基于访问频率的统计更符合我们的淘汰目标因此官方推荐使用LFU算法。
内存淘汰用到的是LRU算法吗
嗯...Redis使用的是近似LRU算法传统LRU算法的实现需要一个双向链表来记录数据最近被访问的顺序最新操作的键会被移动到表头当需要内存淘汰时只需要删除链表尾部的数据即可因为链表尾部的元素就代表最久未被使用的元素。 但是Redis中的KEY可能有数百万甚至更多出于节省内存的考虑Redis的LRU算法并非完整的实现Redis的算法并不是真正的LRU而是一种基于抽样的近似LRU算法Redis采用的是抽样法即每次抽样一定数量maxmemory-samples的key然后和目前维持的淘汰候选池综合比较然后基于内存策略做排序找出淘汰优先级最高的删除这个key这就使得算法的结果更接近于真正的LRU算法了特别是在抽样值较高的情况下例如10可以达到与真正的LRU接近的结果。
当Redis作为缓存使用的时候推荐使用allkeys-lru淘汰策略该策略会将最近最久未使用的key淘汰像这种key后期命中的概率也最低所以将其淘汰。