要想用活Redis,Lua脚本是绕不过去的坎
前言
作为一台服务器来说,内存并不是无限的,所以总会存在内存耗尽的情况,那么当 Redis
服务器的内存耗尽后,如果继续执行请求命令,Redis
会如何处理呢?
内存回收
使用Redis
服务时,很多情况下某些键值对只会在特定的时间内有效,为了防止这种类型的数据一直占有内存,我们可以给键值对设置有效期。Redis
中可以通过 4
个独立的命令来给一个键设置过期时间:
expire key ttl
:将key
值的过期时间设置为ttl
秒。pexpire key ttl
:将key
值的过期时间设置为ttl
毫秒。expireat key timestamp
:将key
值的过期时间设置为指定的timestamp
秒数。pexpireat key timestamp
:将key
值的过期时间设置为指定的timestamp
毫秒数。
PS:不管使用哪一个命令,最终 Redis
底层都是使用 pexpireat
命令来实现的。另外,set
等命令也可以设置 key
的同时加上过期时间,这样可以保证设值和设过期时间的原子性。
设置了有效期后,可以通过 ttl
和 pttl
两个命令来查询剩余过期时间(如果未设置过期时间则下面两个命令返回 -1
,如果设置了一个非法的过期时间,则都返回 -2
):
ttl key
返回key
剩余过期秒数。pttl key
返回key
剩余过期的毫秒数。
过期策略
如果将一个过期的键删除,我们一般都会有三种策略:
- 定时删除:为每个键设置一个定时器,一旦过期时间到了,则将键删除。这种策略对内存很友好,但是对
CPU
不友好,因为每个定时器都会占用一定的CPU
资源。 - 惰性删除:不管键有没有过期都不主动删除,等到每次去获取键时再判断是否过期,如果过期就删除该键,否则返回键对应的值。这种策略对内存不够友好,可能会浪费很多内存。
- 定期扫描:系统每隔一段时间就定期扫描一次,发现过期的键就进行删除。这种策略相对来说是上面两种策略的折中方案,需要注意的是这个定期的频率要结合实际情况掌控好,使用这种方案有一个缺陷就是可能会出现已经过期的键也被返回。
在 Redis
当中,其选择的是策略 2
和策略 3
的综合使用。不过 Redis
的定期扫描只会扫描设置了过期时间的键,因为设置了过期时间的键 Redis
会单独存储,所以不会出现扫描所有键的情况:
typedef struct redisDb {
dict *dict; //所有的键值对
dict *expires; //设置了过期时间的键值对
dict *blocking_keys; //被阻塞的key,如客户端执行BLPOP等阻塞指令时
dict *watched_keys;