Java 中有 GC, 可以自动回收不再使用的 Java 对象. 同样, Redis 也是基于内存而运行的数据集合, 也存在着对内存垃圾的回收和管理的问题.
对于 Redis,del 命令可以删除一些键值对, 所以 Redis 比 JVM 更灵活. 于此同时, 当内存运行空间满了之后, 它还会按照回收机制去自动回收一些键值对, 这和 JVM 有一定相似之处. 但是当进行垃圾回收的时候, 又有可能引发系统停顿, 因此选择恰当的回收机制和回收时间将有利于系统性能的提高.
首先看看键值对的超时命令, 因为大部分情况下, 我们都想回收那些超时的键值对.
命令 | 说明 | 备注 |
---|---|---|
persist key | 持久化 key,取消超时时间 | 移除 key 的超时时间 |
ttl key | 查看 key 的超时时间 | 以秒计算,-1 代表没有超时时间,如果不存在 key 或者 key 已经超时则为 - 2 |
expire key seconds | 设置超时时间戳 | 以秒为单位 |
expireat key timestamp | 设置超时时间点 | 用 uninx 时间戳确定 |
pptl key milliseconds | 查看 key 的超时时间戳 | 以毫秒计算 |
pexpire key | 设置键值超时的时间 | 以毫秒为单位 |
pexpireat key stamptimes | 设置超时时间点 | 以毫秒为单位的 uninx 时间戳 |
下面使用 spring 来执行这个过程
- /**
- * 测试 Redis 的超时命令
- * @author liu
- */
- public class TestExpire {
- @SuppressWarnings({ "unchecked", "resource", "rawtypes", "unused" })
- @Test
- public void testExpire() {
- ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
- RedisTemplate rt = applicationContext.getBean(RedisTemplate.class);
- rt.execute((RedisOperations ops)->{
- ops.boundValueOps("key1").set("value1");
- String keyValue = (String)ops.boundValueOps("key1").get();
- long expSecond = ops.getExpire("key1");
- System.out.println(expSecond);
- boolean b = false;
- b = ops.expire("key1", 120L, TimeUnit.SECONDS);
- b = ops.persist("key1");
- long l = 0L;
- l = ops.getExpire("key1");
- long now = System.currentTimeMillis();
- Date date = new Date();
- date.setTime(now + 120000);
- ops.expireAt("key1", date);
- return null;
- }
- );
- }
- }
这里讨论一个问题: 如果 key 超时了, Redis 会回收 key 的存储空间吗?
答案是不会. Redis 的 Key 超时不会被其自动回收, 它只会标识哪些键值对超时了. 这样做的好处是如果一个很大的键值对超时, 比如一个列表或 hash 结构, 存着百万个元素, 要对其回收需要很长时间. 如果采用超时回收, 则可能会产生停顿. 坏处也很明显, 这些超时的键值对会浪费比较多的空间.
Redis 提供两种方式回收这些超时键值对, 分别是定时回收和惰性回收.
定时回收: 在确定的某个时间触发一次代码, 回收超时的键值对.
惰性回收: 当一个超时的键, 被再次使用 get 命令访问到时, 将触发 Redis 将其从内存中清空.
定时回收可以完全回收掉那些超时的键, 但是缺点也很明显, 如果这件键值对比较多, 那么就会耗费较多的时间, 从而导致卡顿.
对于惰性回收, 它的优势是可以指定回收的键值对, 它的缺点是要执行一个 get 操作进行触发, 或者在某些时候, 我们难以判断哪些键值对已经超时.
无论使用那一种回收策略, 都需要根据情况而定. 如果一个键值对存储着数亿千万的数据, 使用 expire 命令使其到达一个时间超时, 然后用 get 命令访问触发其回收, 显然会付出卡顿代价, 这是现实中需要考虑的.
来源: http://www.jianshu.com/p/ea04b972e708