设置过期时间
通过expire或者pexpire命令可以为Redis的键以秒或者毫秒级别设置生存时间(Time To Live,TTL)。在经过相应的秒或者毫秒之后,服务器会自动删除键:
redis> set msg "hello"
OK
redis> expire msg 5
(integer) 1
redis> get msg //5秒内
"hello"
redis> get msg //5秒后
(nil)
通过expireat或者pexpireat命令可以为Redis的键以秒或者毫秒级别设置过期时间(expire time)。过期时间是一个Unix时间戳,当键的时间戳来临,服务器会自动删除键:
redis> set msg "hello"
OK
redis> expireat msg 1562986813
(integer) 1
redis> get msg //1562986813前
"hello"
redis> get msg //1562986813后
(nil)
所以,Redis有四个命令来设置键的生存时间或者过期时间。
- expire <key> <ttl> 将键key的生存时间设置为ttl秒。
- pexpire <key> <ttl> 将键key的生存时间设置为ttl毫秒。
- expireat <key> <timestamp> 将键key的过期时间设置为timestamp指定的秒级时间。
- pexpireat <key> <timestamp> 将键key的过期时间设置为timestamp指定的毫秒级时间。
命令的单位和格式虽然不同,但是所有命令最终都是转换成pexpireat命令来实现的:
RedisDb使用过期字典保存了数据库中所有的过期时间
- 过期字典的键的值是一个指针,指向键空间中的某个键对象。
- 过期字典的值是一个long类型的整数,保存了所指向的键的过期时间(一个以毫秒精度的时间戳)。
过期键的删除策略
Redis中,过期键的删除有三种策略:
- 定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临是,立即执行键的删除操作。
- 惰性删除:放任过期键不管,每次从键空间获取键时,都要对该键进行检查是否过期,如果过期,则删除该键,如果没有过期,则返回该键。
- 定期删除:每隔一段时间,程序对数据库进行一次检查,删除库中的过期键。
定时删除
定时删除策略是对内存最友好的策略。通过创建定时器,保证过期键尽可能快的被删除,并释放过期键所占有的内存。
但是,定时删除策略确实对CPU最不友好的策略,如果过期键比较多,删除过期键这一行为可能占据大量的CPU时间。如果当前有大量的客户端命令请求在服务器等待处理,而且服务器内存情况并不紧张,那么服务器应该将CPU时间优先用在相应客户端命令上面,而不是用在和当前任务无关的删除过期键任务上,这样会对服务器的响应时间和吞吐量造成影响。
惰性删除
惰性删除策略是对CPU时间最友好的策略,程序只会在取出键的时候才会对键做过期检验,这个策略不会在删除其他无关过期键上浪费任何CPU时间。
惰性删除的缺点是对内存最不友好,如果一个键已经过期,然而一直没有读取该键,会导致该过期键一直留在数据库当中,如果这个键一直不被删除,那它所占用的内存将永远不会被释放。如果有大量的过期键存在,而这些过期键恰好又打不到访问,那么它们将永远不会被删除,这种垃圾数据占据大量的内存,而服务器又不会主动去删除的情况,可以说是一种内存泄露了。
惰性删除策略流程:
定期删除
从上面两种删除策略来看,都存在单一使用是的明线缺陷。
- 定时删除占用太多CPU时间,影响服务器的吞吐量和相应时间。
- 惰性删除浪费太多的内存,有内存泄露的风险。
定期删除则是一种这种的策略:
- 没隔一段时间执行一次过期键的删除操作,并控制操作执行的时间和频率来减少对CPU时间的影响。
- 通过定期删除过期键,有效的减少了因过期键带来的内存浪费。
定期删除的难点:频率和时长
- 如果删除操作执行太过频繁,并且操作时间太长,定期删除则和定时删除一样,将太多的CPU时间浪费在删除过期键操作上面。
- 如果执行的太少,或者执行时间太短,定期删除则和惰性删除一样,出现内存浪费的情况。
因此,服务器应该根据具体情况,合理的制定定期删除的频率和时长。
Redis服务器实际上使用的是惰性删除和定期删除这两种策略,通过配合,可以很好的在CPU使用时间和内存占用两个方面取得平衡。