这里介绍一下,redis整合Springboot的简单使用
代码如下(示例):
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
代码如下(示例):
spring:
#redis配置
redis:
host: localhost
port: 6379
代码如下(示例):
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ProductApplicationTest {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
public void test02(){
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
valueOperations.set("name","bgg");
System.out.println("-----:"+valueOperations.get("name"));
}
}
代码如下(示例):
<!-- 使用redis的分布式锁 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.4</version>
</dependency>
<!-- boot整合更方便 但是为了学习先使用上面-->
<!-- <dependency>-->
<!-- <groupId>org.redisson</groupId>-->
<!-- <artifactId>redisson-spring-boot-starter</artifactId>-->
<!-- <version>3.16.4</version>-->
<!-- </dependency>-->
代码如下(示例):
package com.bgg.gulimall.product.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置redisson
* @author lmc
* @date 2021/11/19 14:07
*/
@Configuration
public class MyRedissonConfig {
/**
* 所有redisson的使用,都通过RedissonClient对象
* @return
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson(){
Config config = new Config();
//协议 redis:/rediss:
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
代码如下(示例):使用redis缓存数据,并加锁
/**
* redis中查分类信息
* bug: springboot整合redis,默认使用的是lettuce,会出现堆外内存不足的异常,因为没有清理
* 解决:1.升级lettuce客户端 2.使用jedis(这是旧的,长时间未更新,暂时如此 pom.xml中修改)
* @return
*/
//TODO redis bug待解决
public Map<String, List<Catalog2Vo>> getCatelogJson1() {
/**
* 1.空结果缓存:解决缓存穿透
* 2.设置过期时间(加随机值):解决缓存雪崩
* 3.加锁:解决缓存击穿
*/
//1.加入缓存逻辑,存json字符串 json跨语言跨平台 序列化与反序列化
String catelogJson = redisTemplate.opsForValue().get("catelogJson");
if(StringUtils.isBlank(catelogJson)){
//2.缓存中没有,再从数据库查,并放入缓存
Map<String, List<Catalog2Vo>> map = this.getCatelogJsonFromRedissonLock();
// //3.放入缓存
// catelogJson = JSON.toJSONString(map);
// redisTemplate.opsForValue().set("catelogJson",catelogJson);
return map;
}
return JSON.parseObject(catelogJson, new TypeReference<Map<String, List<Catalog2Vo>>>(){});
}
/**
* 在数据库中查分类信息
* 加锁:redisson分布式锁
* @return
*/
public Map<String, List<Catalog2Vo>> getCatelogJsonFromRedissonLock() {
Map<String, List<Catalog2Vo>> map = null;
RLock lock = redisson.getLock("catelogJson-lock");
lock.lock(40, TimeUnit.SECONDS);
try {
Thread.sleep(20000);
map = this.getDataFromDB();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return map;
}
/**
* 在数据库中查分类信息
* 加锁:单体应用
* @return
*/
public synchronized Map<String, List<Catalog2Vo>> getCatelogJsonFroDB() {
//先确认缓存中有无
String catelogJson = redisTemplate.opsForValue().get("catelogJson");
if(StringUtils.isNotBlank(catelogJson)){
log.info("缓存命中--直接返回");
return JSON.parseObject(catelogJson,new TypeReference<Map<String, List<Catalog2Vo>>>(){});
}
log.info("缓存未命中--进行查库");
//代码性能优化:先查出所有分类数据,再进行筛选,减少与数据库的交互,进而提高性能
List<CategoryEntity> all = this.baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>());
if(all == null){
return null;
}
//1.先查出所有的一级分类
List<CategoryEntity> level1Category = getLevel1Category();
//2.封装
Map<String, List<Catalog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//2.1查出二级分类
List<CategoryEntity> catelog2List = all.stream().filter(i -> i.getParentCid().longValue() == v.getCatId().longValue()).collect(Collectors.toList());
// List catelog2List = this.baseMapper.selectList(new LambdaQueryWrapper().eq(CategoryEntity::getParentCid, v.getCatId()));
List<Catalog2Vo> collect = null;
if(null != catelog2List){
collect = catelog2List.stream().map(i -> {
//3.1查出三级分类
List<CategoryEntity> catelog3List = all.stream().filter(e -> e.getParentCid().longValue() == i.getCatId().longValue()).collect(Collectors.toList());
// List catelog3List = this.baseMapper.selectList(new LambdaQueryWrapper().eq(CategoryEntity::getParentCid, i.getCatId()));
List<Catalog2Vo.Catalog3Vo> catelog3Vos = null;
if(null != catelog3List){
catelog3Vos = catelog3List.stream().map(j -> {
Catalog2Vo.Catalog3Vo catelog3Vo = new Catalog2Vo.Catalog3Vo();
catelog3Vo.setCatalog2Id(i.getCatId().toString());
catelog3Vo.setId(j.getCatId().toString());
catelog3Vo.setName(j.getName());
return catelog3Vo;
}).collect(Collectors.toList());
}
Catalog2Vo catalog2Vo = new Catalog2Vo();
catalog2Vo.setCatalog1Id(v.getCatId().toString());
catalog2Vo.setCatalog3List(catelog3Vos);
catalog2Vo.setId(i.getCatId().toString());
catalog2Vo.setName(i.getName());
return catalog2Vo;
}).collect(Collectors.toList());
}
return collect;
}));
//数据加入缓存
redisTemplate.opsForValue().set("catelogJson",JSON.toJSONString(map));
return map;
}
代码如下(示例):
<!--使用springCache缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
代码如下(示例):
#缓存配置
spring:
cache:
type: redis #缓存使用redis
redis:
time-to-live: 3600000 #缓存存活时间1h
# key-prefix: CACHE_ #设置缓存前缀名
use-key-prefix: true #是否使用前缀
cache-null-values: true #是否缓存null值 可防止缓存穿透
代码如下(示例):
package com.bgg.gulimall.product.config;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
*自定义缓存配置
* @author lmc
* @date 2021/11/25 16:22
*/
@EnableConfigurationProperties(CacheProperties.class)//开启配置文件
@Configuration
@EnableCaching
public class MyCacheConfig {
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//springCach默认redis缓存的是java序列化 现在自定义为json串
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
//将配置文件的内容生效
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
代码如下(示例):
/**
* @CacheEvict: 缓存失效
* @param category
*/
//@CacheEvict(value = "category",key = "'getLevel1Category'")
// @Caching(evict = { //指定删除多个缓存
// @CacheEvict(value = "category",key = "'getLevel1Category'"),
// @CacheEvict(value = "category",key = "'getCatelogJson'")
// })
@CacheEvict(value = "category",allEntries = true) //删除category分区的所有缓存
@Transactional
@Override
public void updateCascade(CategoryEntity category) {
this.updateById(category);
if(!StringUtils.isBlank(category.getName())){
categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
}
}
/**
* 1.每一个需要缓存的数据我们都来指定一个名字,【缓存的分区--根据业务类型区分】
* @Cacheable("category") //代表当前方法的结果需要缓存,若缓存中有,则不调用方法,否者调用
*
* 2.默认行为:
* 若缓存中有,则不调用方法
* key默认生成名字:category::SimpleKey []
* 缓存的值默认java序列后话的数据
* 默认过期时间-1:永不过期
*
* 3.自定义行为:
* 指定key名字 key属性指定,接收一个spEL或者直接单引号名 'level1Category'
* 指定过期时间 yml配置
* 缓存值改为json字符串格式
*
* 4. * 1.空结果缓存:解决缓存穿透 (yml配置文件)
* * 2.设置过期时间:解决缓存雪崩 (yml配置文件)
* * 3.加锁:解决缓存击穿 sync = true:这是一个单机同步锁,足以解决缓存击穿的问题
* @return
*/
@Cacheable(value = "category",key = "#root.methodName",sync = true)
@Override
public List<CategoryEntity> getLevel1Category() {
System.out.println("调用了方法getLevel1Category");
List<CategoryEntity> categoryEntityList = this.baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>()
.eq(CategoryEntity::getParentCid, 0));
return categoryEntityList;
}
@Cacheable(value = "category",key = "#root.methodName")
@Override
public Map<String, List<Catalog2Vo>> getCatelogJson() {
//代码性能优化:先查出所有分类数据,再进行筛选,减少与数据库的交互,进而提高性能
List<CategoryEntity> all = this.baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>());
if(all == null){
return null;
}
//1.先查出所有的一级分类
List<CategoryEntity> level1Category = getLevel1Category();
//2.封装
Map<String, List<Catalog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//2.1查出二级分类
List<CategoryEntity> catelog2List = all.stream().filter(i -> i.getParentCid().longValue() == v.getCatId().longValue()).collect(Collectors.toList());
// List catelog2List = this.baseMapper.selectList(new LambdaQueryWrapper().eq(CategoryEntity::getParentCid, v.getCatId()));
List<Catalog2Vo> collect = null;
if(null != catelog2List){
collect = catelog2List.stream().map(i -> {
//3.1查出三级分类
List<CategoryEntity> catelog3List = all.stream().filter(e -> e.getParentCid().longValue() == i.getCatId().longValue()).collect(Collectors.toList());
// List catelog3List = this.baseMapper.selectList(new LambdaQueryWrapper().eq(CategoryEntity::getParentCid, i.getCatId()));
List<Catalog2Vo.Catalog3Vo> catelog3Vos = null;
if(null != catelog3List){
catelog3Vos = catelog3List.stream().map(j -> {
Catalog2Vo.Catalog3Vo catelog3Vo = new Catalog2Vo.Catalog3Vo();
catelog3Vo.setCatalog2Id(i.getCatId().toString());
catelog3Vo.setId(j.getCatId().toString());
catelog3Vo.setName(j.getName());
return catelog3Vo;
}).collect(Collectors.toList());
}
Catalog2Vo catalog2Vo = new Catalog2Vo();
catalog2Vo.setCatalog1Id(v.getCatId().toString());
catalog2Vo.setCatalog3List(catelog3Vos);
catalog2Vo.setId(i.getCatId().toString());
catalog2Vo.setName(i.getName());
return catalog2Vo;
}).collect(Collectors.toList());
}
return collect;
}));
return map;
}