❑PEXPIRE<key><ttl>命令用于将键key的生存时间设置为ttl毫秒。
❑EXPIREAT<key><timestamp>命令用于将键key的过期时间设置为timestamp所指定的秒数时间戳。
❑PEXPIREAT<key><timestamp>命令用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳。
例如
redis对存储值的过期处理实际上是针对该值的键(key)处理的,即时间的设置也是设置key的有效时间。Expires字典保存了所有键的过期时间,Expires也被称为过期字段。
四种处理策略
EXPIRE 将key的生存时间设置为ttl秒
PEXPIRE 将key的生成时间设置为ttl毫秒
EXPIREAT 将key的过期时间设置为timestamp所代表的的秒数的时间戳
PEXPIREAT 将key的过期时间设置为timestamp所代表的的毫秒数的时间戳
其实以上几种处理方式都是根据PEXPIREAT来实现的,设置生存时间的时候是redis内部计算好时间之后在内存处理的,最终的处理都会转向PEXPIREAT。
1、2两种方式是设置一个过期的时间段,就是咱们处理验证码最常用的策略,设置三分钟或五分钟后失效,把分钟数转换成秒或毫秒存储到redis中。
3、4两种方式是指定一个过期的时间 ,比如优惠券的过期时间是某年某月某日,只是单位不一样。
@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 配置序列化
// 统一默认配置,TTL为60秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(60))
// 针对不同key可以个性化设置
Set<String>cacheNames = new HashSet<>()
cacheNames.add("account")
cacheNames.add("post")
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration>configMap = new HashMap<>()
configMap.put("account", config.entryTtl(Duration.ofSeconds(30)))
configMap.put("post", config)
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build()
}
自定义每个缓存的TTL和自动刷新
上面的设置虽然可以提前设定每个cacheName的ttl,但是不够细,虽然Cacheable的sync可以防止缓存击穿,但是无法防止缓存雪崩。
我们可以使用AOP来完成自动刷新和自定义TTL。
代码写的比较糙,只是个例子,可以自己修改。
自己写TTL注解
import org.springframework.core.annotation.AliasFor
import java.lang.annotation.*
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface CacheTTL {
@AliasFor("ttl")
long value() default 60
@AliasFor("value")
long ttl() default 60
long preExpireRefresh() default 10
}
AOP实现
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.After
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.cache.annotation.Cacheable
import org.springframework.cache.interceptor.SimpleKeyGenerator
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.core.annotation.Order
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Component
import java.lang.reflect.Method
import java.util.Arrays
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
//这里必须要比Cacheable的Aspect优先级要高
@Order(value=1)
@Aspect
@Component
public class CustomCacheAspect {
private final RedisTemplate<String,Object>redisTemplate
private final SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator()
private final AtomicInteger asyncLock = new AtomicInteger(0)
public CustomCacheAspect(RedisTemplate<String, Object>redisTemplate) {
this.redisTemplate = redisTemplate
}
@After(value="execution(* *..*.*(..))")
public void after(JoinPoint point)
{
Object target = point.getTarget()
MethodSignature signature = (MethodSignature) point.getSignature()
Method method = signature.getMethod()
try {
if (method.isAnnotationPresent(CacheTTL.class) &&method.isAnnotationPresent(Cacheable.class)) {
CacheTTL ttlData = AnnotationUtils.getAnnotation(method, CacheTTL.class)
Cacheable cacheAbleData = AnnotationUtils.getAnnotation(method, Cacheable.class)
long ttl = ttlData.ttl()
long preExpireRefresh = ttlData.preExpireRefresh()
String[] cacheNames = cacheAbleData.cacheNames()
//默认的keyGenerator生成,如果自定义了自己改一下
Object key = keyGenerator.generate(target, method, point.getArgs())
updateExpire(cacheNames,key,preExpireRefresh,ttl)
}
} catch (Exception e) {
e.printStackTrace()
}
}
public void updateExpire(String[] cacheNames,Object key,long preExpireRefresh,long ttl){
if (asyncLock.compareAndSet(0,1)){
Arrays.stream(cacheNames).parallel().forEach(cacheName->{
cacheName = cacheName + "::" + key
long expire = redisTemplate.getExpire(cacheName,TimeUnit.SECONDS)
if (expire>0 &&expire<=preExpireRefresh || expire >ttl || expire == -1){
redisTemplate.expire(cacheName, ttl, TimeUnit.SECONDS)
}
})
asyncLock.set(0)
}
}
}
使用
@Cacheable(cacheNames = "account",key="#id",sync = true)
@CacheTTL(ttl = 60,preExpireRefresh = 10)
public Account findAccountByID(int id) {
return accountDao.findAccountByID(id)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)