Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。
上面是 Redis 官网对 Redis 的总结性描述,很直观的看到 Redis 的优势及功能组成,后面打算对这些知识点去做些梳理和总结,而这里重点梳理一下 Redis 中的键过期,这也是实际工作中最常见的问题了。
键过期这一部分会涉及到:如何设置、查询、删除过期键等基本操作?过期键删除策略有哪些及Redis使用何种策略?持久化功能和复制功能中是如何处理键过期问题的?Redis未通过 过期键删除策略 删除过期键,有可能导致内存满了的问题,该怎么解决?下面简单扼要的小结下,开始吧。
一、设置/查询/取消过期键
经常使用Redis的话,命令很简单,如下:
// 设置键在指定时间 秒数 或 毫秒数 之后到期/过期
SET key value ex seconds
EXPIRE key seconds
PEXPIRE key milliseconds
// 设置在指定 时间戳(Unix时间戳格式) 之后键到期/过期
EXPIREAT key seconds-timestamp
PEXPIREAT key milliseconds-timestamp
// 获取键的剩余到期时间(秒数 或 毫秒数)
TTL key
PTTL key
// 取消或删除 指定键的过期时间,永不过期
PERSIST key
二、过期键策略
Redis数据库的键过期时间到了,会有三种删除策略可供选择:(优缺点分析,从内存和CPU时间角度考虑)
1、定时删除
创建定时器timer,键过期时间到来时,立即删除过期的键。这种方式的优点很明显,会尽快释放内存,然而,在不要求内存但要求CPU时间的情况下,创建timer并执行删除过期键的操作会占用CPU时间,对服务器响应时间和吞吐量造成影响。(如:有大量的命令请求等待服务器处理时,服务器会优先将CPU的时间放在处理请求上面,而通过这种策略删除数量比较多的过期键时,明显是不合理的)
2、惰性删除
只有get键时才去检查这个键是否过期,过期的话会被删除。这种方式对CPU较友好,但是,很明显对内存不友好,大量过期无用的键被积压会占用大量内存,对于依赖内存的Redis服务器来说是很严重的问题,甚至可能会造成内存泄露。
3、定期删除
可以理解为介于定时删除和惰性删除的一种折中策略。定期删除策略要控制好执行时长和频率,否则可以会退化成定时删除策略或者惰性策略。
小结
第一、三种是一种主动删除策略,而第二种是被动删除策略,Redis服务器使用的是第二、三种键过期删除策略。定期删除时,Redis 默认每隔100ms检查一次,有过期 Key 则去删除。需要注意的是,Redis 不是每隔100ms将所有的 Key 检查一次,而是随机抽取进行检查。如果只采用定期删除策略,会导致很多 Key 到时间没有删除,此时惰性删除就会派上用场了。
三、持久化功能和复制功能中是如何处理键过期问题的?
Redis支持RDB和AOF两种方式的持久化,同时也支持主从模式,而主从服务器之间就是通过复制方式同步数据的。
1、RDB方式处理键过期问题
这里需要考虑两种情况,RDB文件生成时和RDB文件载入时。
- 生成RDB文件:通过SAVE或BGSAVE命令生成该文件时,会检查键是否过期,过期的键不会存放到RDB文件中。
- 载入RDB文件:载入该文件时也会检查键是否过期,过期不会被放到RDB文件中。
2、AOF方式处理键过期问题
这里也需要考虑两种情况,AOF文件写入时 和 AOF文件重写时。
- AOF文件写入:若没有通过 过期删除策略 删除过期的键,该过期键会被写入到AOF文件;若 过期删除策略 删除了过期键,AOF文件写入时通过del命令显式的记录并放到AOF文件中。
- AOF文件重写:检查所有的键是否存在过期,过期的键不会被重写到AOF文件中。
3、复制方式处理键过期问题
比如:存在一对主从服务器,在进行交互的时候,过期键的删除是主服务器控制的。
- 主服务器的数据库存在过期键时会被删除,并通过del命令通知所有的从服务器,需要删除哪些过期键;
- 从服务器存在过期键,没有收到主服务器的告知,会无视过期键,将它们等同于正常的键对待,如果有客户端请求从服务器,会正常操作该过期键;
- 从服务器接收到主服务器del命令告知后,才会删除过期键。
四、内存淘汰策略
如果Redis的过期键删除策略未生效,会导致内存中的数据越来越多,可能出现内存爆满的情况,内存满了之后该怎么办呢?Redis提供了以下六种内存淘汰策略:
设置过期时间的键空间移除
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。(不推荐)
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。(不推荐)
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。(不推荐)
全局性的键空间移除
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(推荐使用,目前项目在用这种)(最近最少使用算法)
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。(不推荐)
在配置文件配置,内容如下:
#在 redis.conf 中开启内存淘汰策略配置:
maxmemory-policy volatile-lru
五、总结
键过期的增删改查命令看似很简单,但可以贯穿的东西还挺多的,比如Redis的使用键过期策略、其他功能中涉及到的键过期问题、键过期策略不足带来的内存爆满情况及Redis提供的六种内存淘汰策略。当然,键过期在实际场景使用中,需要注意不要过于集中设置,因为这可能会引发缓存雪崩等问题,下一篇打算将对Redis常见的缓存异常进行梳理和总结。不积硅步无以至千里,共同学习进步吧~