最近入职后一直很忙,今天国庆值班,把前段时间遇到的问题抛出来总结一下。
1、背景
在项目中需要记录用户的相关状态,这些状态会在一天内清除,且又不止0/1这两种状态,于是考虑将其放在一个hash的key当中。
但是redis对于hash类型,并没有提供直接设置超时时间的支持,于是设计采用expire一个key的过期的时间方式来实现当日过期。
2、问题
通过观察发现,隔天的数据,在共用一个key的情况下并没有如预估的一样过期。
3、原因
因为所有的hash都共用一个key,比如hermes:newest,这样当设置了hash的field之后,会紧接着通过expire设置过期时间,如
// 代码经过改写过,大意就是这样,把公司的包替换了
template.execute(jedis -> {
Pipeline pipeline = bedis.pipelined();
pipeline.hset(keyHash, String.valueOf(uid), JsonUtils.toJson(userInfo));
pipeline.expire(keyHash, seconds);
return pipeline.sync(false);
});
A、过期时间是通过计算到下一天0点时的剩余时间。
B、expire的删除方式为定期删除+懒删除,不一定会立即删除掉这个key。每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。或者定期扫描去删除。
这样一来,当11点59分59秒设置了失效时间为1秒时,再接下来00点00分00秒时,设置的失效时间并不是0秒,而是到后一天的24小时,所以会在删除前,直接覆盖掉之前的key的过期时间,使前一天的field结果不被删除。
4、解决方法
A、采用定时任务,每天0点对这个key做一次主动删除操作。
B、hash的key上带有时间标记,如加上每一天,这样就会使得key的过期时间不被覆盖,到了定时删除的时间,前一天的key自然会被删除。
C、采用setx的方式来存储key-value
目前在项目中采用的为B方法。