赞
踩
目录
为了把一些经常访问的数据,放入缓存中以减少对数据库的访问频率。从而减少数据库的压力,提高程序的性能。(内存中存储)
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.4.3</version>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <optional>true</optional>
- </dependency>
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
server.port=8080 #数据源 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/1suo?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=12345678 #mybatisplus的sql日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #mapper映射文件 mybatis-plus.mapper-locations=classpath*:mapper/*.xml #redis配置 spring.redis.host=172.16.7.192 spring.redis.port=6379 spring.redis.database=1
控制层
- package com.ls.controller;
-
- import com.ls.entity.Clazz;
- import com.ls.service.ClazzService;
- import com.ls.vo.R;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
-
- /**
- * @program: springboot-redis-cache
- * @description:
- * @author: 1suo
- * @create: 2024-07-24 19:56
- **/
- @RestController
- @RequestMapping("clazz")
- public class ClazzController {
- @Autowired
- private ClazzService clazzService;
-
- @DeleteMapping("delete")
- public R delete(Integer id) {
- return clazzService.delete(id);
- }
- @GetMapping("get")
- public R get(Integer id) {
- return clazzService.get(id);
- }
- @GetMapping("getAll")
- public R getAll() {
- return clazzService.getAll();
- }
- @PostMapping("save")
- public R save(@RequestBody Clazz clazz) {
- return clazzService.save(clazz);
- }
- @PutMapping("update")
- public R update(Clazz clazz) {
- return clazzService.update(clazz);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
业务层
- package com.ls.service.impl;
-
- import com.ls.dao.ClazzDao;
- import com.ls.entity.Clazz;
- import com.ls.service.ClazzService;
- import com.ls.vo.R;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Service;
- import sun.dc.pr.PRError;
-
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- * @program: springboot-redis-cache
- * @description:
- * @author: 1suo
- * @create: 2024-07-24 19:57
- **/
- @Service
- public class ClazzServiceImpl implements ClazzService {
- @Autowired
- private ClazzDao clazzDao;
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
- @Override
- public R save(Clazz clazz) {
- int insert = clazzDao.insert(clazz);
- return new R(200,"保存成功",clazz);
- }
-
- @Override
- public R update(Clazz clazz) {
- int update = clazzDao.updateById(clazz);
- if (update>0){
- redisTemplate.opsForValue().set("clazz::" + clazz.getCid(),clazz);
- }
- return new R(200,"更新成功",clazz);
- }
-
- @Override
- public R delete(Integer id) {
- int delete = clazzDao.deleteById(id);
- if (delete>0){
- redisTemplate.delete("clazz::" + id);
- }
- return new R(200,"删除成功",id);
- }
-
- @Override
- public R get(Integer id) {
- ValueOperations<String, Object> forValue = redisTemplate.opsForValue();
- Object o = forValue.get("clazz::" + id);
- if(o!=null){
- return new R(200,"查询成功",(Clazz)o);
- }
- Clazz clazz = clazzDao.selectById(id);
- if (clazz!=null){
- forValue.set("clazz::" + id,clazz);
- }
- return new R(500,"查询成功",clazz);
- }
-
- @Override
- public R getAll() {
- List<Clazz> list = clazzDao.selectList(null);
- return new R(200,"查询成功",list);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
dao层
- package com.ls.dao;
-
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.ls.entity.Clazz;
-
- /**
- * @program: springboot-redis-cache
- * @description:
- * @author: 1suo
- * @create: 2024-07-25 08:41
- **/
- public interface ClazzDao extends BaseMapper<Clazz> {
- }
实体类
- package com.ls.entity;
-
- import com.baomidou.mybatisplus.annotation.IdType;
- import com.baomidou.mybatisplus.annotation.TableId;
- import com.baomidou.mybatisplus.annotation.TableName;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- /**
- * @program: springboot-redis-cache
- * @description:
- * @author: 1suo
- * @create: 2024-07-24 19:56
- **/
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
-
- @TableName("tbl_clazz")
- public class Clazz {
- @TableId(type = IdType.AUTO)
- private Integer cid;
-
- private String cname;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
配置类
- package com.ls.config;
-
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
-
- @Configuration
- public class RedisConfig {
- @Bean
- public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
- RedisTemplate<String, Object> template = new RedisTemplate<>();//创建redisTemplate对象
- StringRedisSerializer serializer = new StringRedisSerializer();//字符串序列化对象
- Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//Jackson的序列化对象
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- template.setConnectionFactory(factory);
- // key序列化方式
- template.setKeySerializer(serializer);
- // value序列化
- template.setValueSerializer(jackson2JsonRedisSerializer);
- // value hashmap序列化
- template.setHashValueSerializer(jackson2JsonRedisSerializer);
- template.setKeySerializer(serializer);
- return template;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
@MapperScan("com.ls.dao") 注解,将dao层自动代理为Mapper代理对象。
业务层代码除了要维护核心业务功能外,额外还要维护缓存的代码。
解决: 使用AOP面向切面编程。(spring框架用aop切面实现)
必须spring缓存使用的组件。
config:为解决序列化的问题
- @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;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
开启缓存注解
使用
- package com.ls.service.impl;
-
- import com.ls.dao.ClazzDao;
- import com.ls.entity.Clazz;
- import com.ls.service.ClazzService;
- import com.ls.vo.R;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.cache.annotation.CacheEvict;
- import org.springframework.cache.annotation.CachePut;
- import org.springframework.cache.annotation.Cacheable;
- import org.springframework.cache.annotation.EnableCaching;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Service;
- import sun.dc.pr.PRError;
-
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- * @program: springboot-redis-cache
- * @description:
- * @author: 1suo
- * @create: 2024-07-24 19:57
- **/
- @Service
- public class ClazzServiceImpl implements ClazzService {
- @Autowired
- private ClazzDao clazzDao;
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
- @Override
- public R save(Clazz clazz) {
- int insert = clazzDao.insert(clazz);
- return new R(200,"保存成功",clazz);
- }
-
- @CachePut(cacheNames = "clazz", key = "#clazz.cid")
- @Override
- public Clazz update(Clazz clazz) {
- int update = clazzDao.updateById(clazz);
- return clazz;
- }
-
- @CacheEvict(cacheNames = "clazz", key = "#id")
- @Override
- public R delete(Integer id) {
- int delete = clazzDao.deleteById(id);
- return new R(200,"删除成功",id);
- }
-
- @Cacheable(cacheNames = "clazz", key = "#id")
- @Override
- public Clazz get(Integer id) {
- Clazz clazz = clazzDao.selectById(id);
- return clazz;
- }
-
- @Override
- public R getAll() {
- List<Clazz> list = clazzDao.selectList(null);
- return new R(200,"查询成功",list);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
使用jmeter压测工具:
第一步:
第二步:
第三步:
通过压测发现库存超卖和重卖了,解决办法使用锁。
- public String decrement(Integer productid) {
- //根据id查询商品的库存
- int num = stockDao.findById(productid);
- synchronized (this) {
-
- if (num > 0) {
- //修改库存
- stockDao.update(productid);
- System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
- return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
- } else {
- System.out.println("商品编号为:" + productid + "的商品库存不足。");
- return "商品编号为:" + productid + "的商品库存不足。";
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
上面使用syn和lock虽然解决了并发问题,但是我们未来项目部署时可能要部署集群模式。
点击增加一个服务器,同时启动后,实现模拟多服务器。 下面是服务器配置。
启动俩个服务器:
下载window版本的nginx实现代理集群。
nginx.conf配置文件:
通过压测发现本地锁 无效了,使用redis解决分布式锁文件。
核心代码:
- @Service
- public class StockService {
-
- @Autowired
- private StockDao stockDao;
- @Autowired
- private RedissonClient redisson;
-
- //
- public String decrement(Integer productid) {
- RLock lock = redisson.getLock("product::" + productid);
- lock.lock();
- try {
- //根据id查询商品的库存: 提前预热到redis缓存中
- int num = stockDao.findById(productid);
- if (num > 0) {
- //修改库存---incr---定时器[redis 数据库同步]
- stockDao.update(productid);
- System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
- return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
- } else {
- System.out.println("商品编号为:" + productid + "的商品库存不足。");
- return "商品编号为:" + productid + "的商品库存不足。";
- }
- }finally {
- lock.unlock();
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。