问题简介
:登录模块会将用户信息缓存到redis中,没有查到则存空。下次用到用户信息时,先从缓存中取,缓存中没有,再从数据库取,以减轻数据库的压力。该功能基于Sprng @cacheable实现。当用户用未注册的账号登录时,按照逻辑存入空,用户紧接着注册了账户,并尝试用该账户登录,按照逻辑,缓存中已有了为空的记录,此时不会再从数据库查一遍,这导致了该账号一直登陆失败,直到统一配置的时间到了,才会从redis清除。
问题解决
:
首先考虑使用@Cacheable的
unless
功能,将结果为空的不存入redis,这样,redis里的都是有效的用户信息:
@Cacheable(value="XXX",key="#info",unless = "#result==null")
刚刚的问题解决了,但是同时带来了新的问题:如果有人恶意的用未注册的账户访问,就会导致,每次的请求都直达数据库,给数据库带来压力隐患,也就是
数据穿透问题
。考虑当用户信息为空的时候,仍然存一份到redis里,并设置过期时间,如 5s,这样数据库的穿透问题可以得到解决。那么如何实现设置超时呢?我们知道,@Cacheable是不支持直接设置过期时间的。我考虑了三种方式:
第一种
:Spring提供了CacheManager类用来给指定的标签设置过期时间,一般自定义配置类(@Configuration)里统一配置。我们只想设置某一个具体的账号,只对空的数据设置5s过期,所以这个统一的设置不太适合。
第二种
:使用切面,在调用返回结果的地方设置切点。这里要注意内部调用的方法(就是用this或省略this来调用的方法),设置切点是无效的,详细分析可以参考我转载的《
震惊!Spring Aop不起作用可能是这个原因导致的
》
第三种
:使用redisTemplet的opForValue().set()方法。这里要注意,expire方法只能对已存在的记录设置超时时间。结合unless,对为空的情况特殊处理,解决问题。
如果您有其他思路,欢迎留言。