redis、术语方案、Lettuce、cache.spring
1,缓存击穿是指当缓存中的某个热点数据过期时,大量的查询请求会直接穿透缓存查询数据库,导致数据库压力骤增,可能引发服务故障 互斥锁解决 2,缓存穿透,如果查询一个不存在的数据,数据库查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库,相当于缓存失效 布隆过滤器解决 3,缓存雪崩:Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库 设置有效期均匀分布 数据预热 布隆过滤器: 请求到达缓存前先在布隆过滤器预检查,如果布隆过滤器判断该键可能不存在,则直接返回请求不存在的结果,不再进行后续的缓存和数据库查询。如果布隆过滤器判断该键可能存在,则继续进行后续操作 用于快速判断一个元素是否属于一个集合。它以较小的空间占用和高效的查询时间著称 Bloom 过滤器基于哈希函数和位数组实现。它的核心思想是使用多个哈希函数将元素映射到位数组中,并将对应的位设置为1。当查询一个元素时,通过对该元素进行相同的哈希计算,检查对应的位是否都为1。如果其中有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中,但并不确定,存在一定的概率误判。
Lettuce
@Value("${spring.redis-sentinel.nodes}")
private String redisNodes; //192.xx.66:26379,192.xx.65:26379,192.xx.64:26379
@Value("${spring.redis-sentinel.master}")
private String master;
@Value("${spring.redis-sentinel.password}")
private String sentinelPassword;
@Value("${spring.redis-sentinel.database}")
private int sentinelDatabase ;
@Bean(name = "redisSentinelConfiguration")
public RedisSentinelConfiguration redisSentinelConfiguration(){
RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
String[] host = redisNodes.split(",");
for(String redisHost : host){
String[] item = redisHost.split(":");
String ip = item[0];
String port = item[1];
configuration.addSentinel(new RedisNode(ip, Integer.parseInt(port)));
}
configuration.setMaster(master);
configuration.setDatabase(sentinelDatabase);
configuration.setPassword(sentinelPassword);
return configuration;
}
@Bean(name = "sentinelLettuceConnectionFactory")
public LettuceConnectionFactory sentinelLettuceConnectionFactory(RedisSentinelConfiguration redisSentinelConfiguration) {
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisSentinelConfiguration);
return lettuceConnectionFactory;
}
@Primary
@Bean("lettuceConnectionFactory")
public LettuceConnectionFactory lettuceConnectionFactory(){
//redis配置
RedisConfiguration redisConfiguration = new RedisStandaloneConfiguration(host,port);
((RedisStandaloneConfiguration) redisConfiguration).setDatabase(database);
((RedisStandaloneConfiguration) redisConfiguration).setPassword(password);
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisConfiguration);
return lettuceConnectionFactory;
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisConnectionFactory redisConnectionFactory = (RedisConnectionFactory) applicationContext.getBean(RedisModeEnum.getValueByCode(redisMode));
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
public enum RedisModeEnum {
sentinel("sentinel","sentinelLettuceConnectionFactory"),standalone("standalone","lettuceConnectionFactory");
jedis连接池JedisPoolConfig配置
https://blog.csdn.net/u011271894/article/details/120787584
@Value("${spring.redis.host}")
private String host ;
@Value("${spring.redis.port}")
private String port ;
@Value("${spring.redis.password}")
private String password ;
@Value("${spring.redis.database}")
private String database ;
@Bean(name = "jedisConnectionFactory")
public JedisConnectionFactory jedisConnectionFactory() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(8);//资源池中的最大连接数 default 8
jedisPoolConfig.setMaxIdle(8);//资源池允许的最大空闲连接数 default 8
jedisPoolConfig.setMinIdle(0);//资源池确保的最少空闲连接数 default 0
jedisPoolConfig.setMaxWaitMillis(-1);//当资源池连接用尽后,调用者的最大等待时间 默认-1不超时
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(Integer.parseInt(port));
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setDatabase(Integer.parseInt(database));
JedisClientConfiguration.JedisClientConfigurationBuilder configurationBuilder = JedisClientConfiguration.builder();
JedisClientConfiguration jedisClientConfiguration = configurationBuilder.usePooling().poolConfig(jedisPoolConfig).build();
return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {//RedisConnectionFactory redisConnectionFactory
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory);
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.8</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.6.8</version>
</dependency>
cacheNames 与 @Cacheable(value = "user" 效果一致
类修饰 @CacheConfig(cacheNames = "xingMhqqqq")
@Cacheable(cacheNames = "xingMh2",key ="#name")// 方法上有cacheNames 覆盖类上的
public String xmhTest(String name) {
System.out.println("****************111111:"+name);
return "sysDicsXingmhvaluess";
}
@EnableCaching 开启cache管理
//设置RedisCacheManager
//使用cache注解管理redis缓存
@Bean
public CacheManager cacheManager(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory) {
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
defaultCacheConfig=defaultCacheConfig.entryTtl(Duration.ofHours(-1))
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
// 不缓存空值
.disableCachingNullValues();
// 对每个缓存空间应用不同的配置
// Set<String> cacheNames = new HashSet<>();
// cacheNames.add(userCacheName);
// Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
// configMap.put(userCacheName, defaultCacheConfig.entryTtl(Duration.ofSeconds(userCacheExpireTime)));
RedisCacheManager cacheManager = RedisCacheManager.builder(jedisConnectionFactory)
.cacheDefaults(defaultCacheConfig)
// .initialCacheNames(cacheNames)
// .withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
@Cacheable(value = "getCacheableTestxxxxx",key = "#code",unless="#result == null")
public String getCacheableTest(String code){
System.out.println("**********getCacheableTest");
return "getCacheableTest-value";
}
样例: getCacheableTestxxxxx::xing
org.springframework.cache.annotation.Cacheable
org.springframework.cache.CacheManager 使用cache注解管理redis缓存 mapi
org.springframework.data.redis.cache.RedisCacheManager 实现了 CacheManager
https://www.cnblogs.com/junzi2099/p/8301796.html
根据运行流程,如下@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存;
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id)
如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存
@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
public User conditionSave(final User user)
如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反)
@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
public User conditionSave2(final User user)
如下@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存;
@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
@Cacheable(value="accountCache",key="#accountName.concat(#password)")
通过少量的配置 annotation 凝视就可以使得既有代码支持缓存
支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件就可以使用缓存
支持 Spring Express Language,SpEL表达式,能使用对象的不论什么属性或者方法来定义缓存的 key 和 condition
支持 AspectJ,并通过事实上现不论什么方法的缓存支持
支持自己定义 key 和自己定义缓存管理者,具有相当的灵活性和扩展性
/**
* 基于SpringBoot2 对 RedisCacheManager 的自定义配置
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为json序列化
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
//设置默认超过时期是1天
defaultCacheConfig.entryTtl(Duration.ofDays(1));
//初始化RedisCacheManager
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:sentinel
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(PropertiesUtil.getInt("redis.cache.ttl", 10)));
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(configuration);
if (new Boolean(PropertiesUtil.getString("redis.cache.enableTransaction"))) {
builder.transactionAware();
}
RedisCacheManager cacheManager = builder.build();
return cacheManager;
}

浙公网安备 33010602011771号