Redis
Redis (全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它是一个运行在内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
针对类目信息发现一般很少修改,但是经常需要执行查询,例如添加商品等操作。比较适合使用redis缓存数据,通过浪费内存以减少数据库的查询此时,从而提供执行性能,应对更高的并发性需求。
redis缓存开发一般有2种方式,使用注解【推荐】和使用自定义编程实现。缓存可以添加在不同的层面上,一般针对controller缓存,多使用本地缓存Ehcache;如果针对业务层缓存,一般使用支持分布式的Redis;也可以在持久层添加缓存,例如开启的MyBatis的缓存。
Redis使用流程
1.添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2.添加Redis序列化器和反序列化器的配置
@SpringBootConfiguration
public class MyRedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory
factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>(); //创建模板
类对象
RedisSerializer<String> redisSerializer = new StringRedisSerializer();//
创建String序列化类
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class); //指定使用jackson工具负责具体的序列化操作
ObjectMapper om = new ObjectMapper(); //创建jackson的核心api的 ObjectMapper
,将bean,list,map,数组等等转成字符串
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 使用objectmapper设置bean的属性,修饰符
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //设置默认
类型
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); //设置将本地时
间转成字符串
jackson2JsonRedisSerializer.setObjectMapper(om); //将核心api objectmapper
设置给jackson
template.setConnectionFactory(factory); // 通过工厂得到连接对象
template.setKeySerializer(redisSerializer); //设置key序列化方式: 转成字符串
template.setValueSerializer(jackson2JsonRedisSerializer); // 设置value序列
化: 字符串
template.setHashValueSerializer(jackson2JsonRedisSerializer); // value
hashmap序列化
return template;
}
}
3.RedisTemplate调用API操作Redis数据库
通过spring框架提供的模板类RedisTemplate调用API操作Redis数据库。在使用redis缓存之前应该充分沟通,定义一个key的标准,或者查询公司旧有标准,注意不能随机起名
@Service
public class CatalogServiceImpl extends ServiceImpl<CatalogMapper, Catalog>
implements CatalogService {
@Autowired
private RedisTemplate redisTemplate;
private ThreadLocalRandom random = ThreadLocalRandom.current();
@Override
public List<Catalog> listByPage(PageBean pages) {
List<Catalog> res = new ArrayList<>();
9.5、使用postman测试,可以在控制台上查看是否有对应的SQL语句输出以判断缓存是否生效
注解开发
注解提供了更声明式和方便的方式,而编码方式允许更精细的控制和定制。
if (pages == null || pages.getRowsPerPage() < 1) {
//查询所有,避免缓存穿透,索引允许存储数据为空集
if (redisTemplate.hasKey("catalog::all")) {
//如果在具体开发种比较倾向于使用常量的方式
Object obj = redisTemplate.opsForValue().get("catalog::all");
if (obj != null && obj instanceof List)
res = (List<Catalog>) obj;
} else {
res = this.getBaseMapper().selectList(null);
//为了避免雪崩问题,所以生存周期引入随机数
int kk = 100 + random.nextInt(100);
redisTemplate.opsForValue().set("catalog::all", res,
Duration.ofSeconds(kk));
}
} else {
//分页查询。实际上具体应用中不一定针对分页数据进行缓存 catalog::page::size
if (pages.getPageNum() < 1)
pages.setPageNum(1);
String key = "catalog::" + pages.getPageNum() + "::" +
pages.getRowsPerPage();
if (this.redisTemplate.hasKey(key)) {
Object obj = redisTemplate.opsForValue().get(key);
if (obj != null && obj instanceof List)
res = (List<Catalog>) obj;
if(this.redisTemplate.hasKey(key+"::page")){
obj = redisTemplate.opsForValue().get(key+"::page");
if (obj != null && obj instanceof PageBean) {
PageBean tmp = (PageBean) obj;
BeanUtils.copyProperties(tmp,pages);
}
}
}
if (res == null || res.size() < 1) {
Page<Catalog> pageInfo = new Page<>(pages.getPageNum(),
pages.getRowsPerPage());
pageInfo = this.getBaseMapper().selectPage(pageInfo, null);
res = pageInfo.getRecords();
pages.setPageNum(pageInfo.getCurrent());
pages.setRowsNum(pageInfo.getTotal());
pages.setMaxPage(pageInfo.getPages());
int kk = 100 + random.nextInt(100); //随机数时长用于避免雪崩
redisTemplate.opsForValue().set(key, res,
Duration.ofSeconds(kk));
redisTemplate.opsForValue().set(key+"::page", pages,
Duration.ofSeconds(kk));
}
}
return res;
}
}