热点数据缓存
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 {@Autowiredprivate ClazzDao clazzDao;//查询@Overridepublic Clazz getById(Integer id) {//查询数据库Clazz clazz = clazzDao.selectById(id);return clazz;}//增加@Overridepublic Clazz insert(Clazz clazz) {clazzDao.insert(clazz);return clazz;}//删除@Overridepublic int del(Integer id) {int i = clazzDao.deleteById(id);return i;}@Overridepublic Clazz updateById(Clazz clazz) {//修改数据库int i = clazzDao.updateById(clazz);return clazz;}
controller控制层
@RestController @RequestMapping("/clazz") public class ClazzController {@Autowiredprivate 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缓存@Autowiredprivate RedisTemplate<String,Object> redisTemplate;
因为使用redisTemplate,需要序列化,所以,配置序列化配置工具类
@Configuration public class RedisConfig {@Beanpublic 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方法
//查询@Overridepublic 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方法
@Overridepublic 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方法
@Overridepublic int delete(Integer cid) {int i = clazzDao.deleteById(cid);if(i>0){//删除缓存redisTemplate.delete("clazz::"+cid);}return i;}
1.6 使用缓存注解完成缓存功能
发现:业务层代码除了要维护核心业务功能外,额外还要维护缓存的代码
如何解决:使用AOP面向切面编程——注解
步骤:
- 添加配置spring使用的缓存组件
- 开启注解驱动——@EnableCaching
配置文件
@Beanpublic 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")@Overridepublic 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")@Overridepublic Clazz updateById(Clazz clazz) {//修改数据库int i = clazzDao.updateById(clazz);return clazz;}
CachePut:表示修改时使用的注解
- 先执行方法体
- 把方法的返回结果放入缓存中
1.6.3 删除——@CacheEvict
@CacheEvict(cacheNames = "clazz", key = "#id")@Overridepublic int delete(Integer cid) {int i = clazzDao.deleteById(cid);return i;}
CacheEvict:表示删除时使用的注解 Evict:驱逐
- 先执行方法体
- 把缓存中名称为cacheNames::key的值删除