热点数据缓存
Redis的使用场景——热点数据的缓存
1.1 什么是缓存
为了把一些经常访问的数据,放入缓存中以减少对数据库的访问效率,从而减少数据库的压力,提高程序的性能。【在内存中存储】
1.2 缓存的原理
- 查询缓存中是否存在对应的数据
- 如果缓存中有,即命中,直接返回给程序
- 如果没有明中,访问查询数据库
- 把查询的数据返回给程序,并同时将查询的数据放入缓存
1.3 什么样的数据适合放入缓存中
- 查询频率高且修改频率低的
- 数据安全性低的
1.4 哪个组件可以作为缓存
- redis组件
- memory组件
- ehcache组件等
1.5 java使用redis如何实现缓存
准备
首先创建一个springboot项目
配置文件
server.port=端口号 #数据源 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/数据库名称?serverTimezone=Asia/Shanghai spring.datasource.username=用户名 spring.datasource.password=密码 #mybatis配置文件 mybatis.mapper-locations=classpath:mapper/*.xml #日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #redis spring.redis.host=IP地址 spring.redis.port=6379 spring.redis.database=4
修改mysql依赖,添加mybatis-plus的依赖
<!--mysql依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatisplus依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.4</version> </dependency>
创建实体类
@Data @AllArgsConstructor @NoArgsConstructor @TableName("class") public class Clazz { //设置为主键且自增 @TableId(type = IdType.AUTO) private Integer cid; private String cname; private String teacher; }
创建dao层接口
@Repository public interface ClazzDao extends BaseMapper<Clazz> { }
创建service层和业务实现类
接口
public interface ClazzService { //添加 public Clazz insert(Clazz clazz); //删除 public int del(Integer id); //修改 public Clazz updateById(Clazz clazz); //查询 public Clazz getById(Integer id); }
业务实现类
@Service public class ClazzServiceImpl implements ClazzService { @Autowired private ClazzDao clazzDao; //查询 @Override public Clazz getById(Integer id) { //查询数据库 Clazz clazz = clazzDao.selectById(id); return clazz; } //增加 @Override public Clazz insert(Clazz clazz) { clazzDao.insert(clazz); return clazz; } //删除 @Override public int del(Integer id) { int i = clazzDao.deleteById(id); return i; } @Override public Clazz updateById(Clazz clazz) { //修改数据库 int i = clazzDao.updateById(clazz); return clazz; }
controller控制层
@RestController @RequestMapping("/clazz") public class ClazzController { @Autowired private ClazzService clazzService; //添加 @PostMapping("/insert") public Clazz insert(@RequestBody Clazz clazz){ return clazzService.insert(clazz); } //根据id查询 @GetMapping("/getById/{id}") public Clazz getById(@PathVariable Integer id){ Clazz clazz = clazzService.getById(id); return clazz; } //删除 @DeleteMapping("/del/{id}") public Integer del(@PathVariable Integer id){ int del = clazzService.del(id); return del; } //编辑 @PutMapping("/update") public Clazz update(@RequestBody Clazz clazz){ return clazzService.updateById(clazz); } }
在main主函数中添加注入dao
@SpringBootApplication @MapperScan("com.zmq.dao") public class SpringbootRedis02Application { public static void main(String[] args) { SpringApplication.run(SpringbootRedis02Application.class, args); } }
缓存处理在service层处理优化。【优化查、改、删方法】
在service业务处理类中注入Redis对象
//在service层添加redis缓存 @Autowired private RedisTemplate<String,Object> redisTemplate;
因为使用redisTemplate,需要序列化,所以,配置序列化配置工具类
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(jackson2JsonRedisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
1.5.1 查询
- 获取redis操作字符串的对象
- 首先在Redis缓存中查询,如果有直接返回,不需要在访问数据库查询——get方法
- 如果Redis缓存中没有,再查询数据库,若在数据库中查询到,就将该数据添加到缓存中——set方法
//查询
@Override
public Clazz getById(Integer id) {
//获取redis操作字符串的对象
ValueOperations<String, Object> forValue = redisTemplate.opsForValue();
//1.查询redis缓存是否命中
Object o = forValue.get("clazz::" + id);
//表示缓存命中
if(o!=null){
return (Clazz) o;
}
//查询数据库
Clazz clazz = clazzDao.selectById(id);
//如果数据库存在,将该值添加到缓存中
if(clazz!=null){
forValue.set("clazz::" + id,clazz);
}
return clazz;
}
1.5.2 修改
若修改操作成功,返回值大于0,就将其数据同步到缓存中——set方法
@Override
public Clazz updateById(Clazz clazz) {
//修改数据库
int i = clazzDao.updateById(clazz);
if(i>0){
//修改缓存
redisTemplate.opsForValue().set("clazz::"+clazz.getCid(),clazz);
}
return clazz;
}
1.5.3 删除
若数据库删除操作成功,返回值大于0,就根据id删除缓存中该数据——delete方法
@Override
public int delete(Integer cid) {
int i = clazzDao.deleteById(cid);
if(i>0){
//删除缓存
redisTemplate.delete("clazz::"+cid);
}
return i;
}
1.6 使用缓存注解完成缓存功能
发现:业务层代码除了要维护核心业务功能外,额外还要维护缓存的代码
如何解决:使用AOP面向切面编程——注解
步骤:
- 添加配置spring使用的缓存组件
- 开启注解驱动——@EnableCaching
配置文件
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
开启注解驱动
@SpringBootApplication
@MapperScan("com.zmq.dao")
@EnableCaching
public class SpringbootRedis02Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootRedis02Application.class, args);
}
}
1.6.1 查询——@Cacheable
//查询
@Cacheable(cacheNames ={ "clazz"}, key = "#id")
@Override
public Clazz getById(Integer id) {
//查询数据库
Clazz clazz = clazzDao.selectById(id);
return clazz;
}
Cacheable:表示查询时使用的注解
cacheNames:缓存的名称
key:缓存的唯一标识
在方法体之前执行
- 查询缓存中是否存在名称为cacheNames::key的值
- 如果存在则方法不会执行
- 如果不存在,则执行方法体并把方法的返回结果放入缓存中cacheNames::key
1.6.2 修改——@CachePut
@CachePut(cacheNames = "clazz", key = "#clazz.cid")
@Override
public Clazz updateById(Clazz clazz) {
//修改数据库
int i = clazzDao.updateById(clazz);
return clazz;
}
CachePut:表示修改时使用的注解
- 先执行方法体
- 把方法的返回结果放入缓存中
1.6.3 删除——@CacheEvict
@CacheEvict(cacheNames = "clazz", key = "#id")
@Override
public int delete(Integer cid) {
int i = clazzDao.deleteById(cid);
return i;
}
CacheEvict:表示删除时使用的注解 Evict:驱逐
- 先执行方法体
- 把缓存中名称为cacheNames::key的值删除