Home >Java >javaTutorial >Analysis of Java Redis usage scenarios
Data is stored in memory and data query speed is fast. Database pressure can be shared.
The query frequency is relatively high and the modification frequency is relatively low.
Data with low security factor
Note that the entity class must be serialized:
@Data @AllArgsConstructor @NoArgsConstructor @TableName(value = "tb_dept") public class Dept implements Serializable { @TableId(value = "id",type = IdType.AUTO) private Integer id; private String name; private String realname; }
Corresponding dependencies:
<dependencies> <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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mp的依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Controller layer corresponding code:
@RestController @RequestMapping("order") public class DeptController { @Resource private DeptService deptService; @GetMapping("getById/{id}") //order/getById/1 //{}可以放多个,由下面的传参函数对应 //@PathVariable:获取请求映射中{}的值 public Dept getById(@PathVariable Integer id){ return deptService.findById(id); } @GetMapping("deleteById/{id}") public String deleteById(@PathVariable Integer id){ int i = deptService.deleteById(id); return i>0?"删除成功":"删除失败"; } @GetMapping("insert") public Dept insert(Dept dept){ Dept insert = deptService.insert(dept); return insert; } @GetMapping("update") public Dept update(Dept dept){ Dept update = deptService.update(dept); return update; } }
Service layer corresponding code:
@Service public class DeptService { @Resource private DeptMapper deptMapper; //当存储的value类型为对象类型使用redisTemplate //存储的value类型为字符串。StringRedisTemplate @Autowired private RedisTemplate redisTemplate; //业务代码 public Dept findById(Integer id){ ValueOperations forValue = redisTemplate.opsForValue(); //查询缓存 Object o = forValue.get("dept::" + id); //缓存命中 if(o!=null){ return (Dept) o; } Dept dept = deptMapper.selectById(id); if(dept!=null){ //存入缓存中 forValue.set("dept::"+id,dept,24, TimeUnit.HOURS); } return dept; } public int deleteById(Integer id){ redisTemplate.delete("dept::"+id); int i = deptMapper.deleteById(id); return i; } public Dept insert(Dept dept){ int insert = deptMapper.insert(dept); return dept; } public Dept update(Dept dept){ redisTemplate.delete("dept::"+dept.getId()); int i = deptMapper.updateById(dept); return dept; } }
Configuration source:
# Configure data source
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydb? serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#sql log
mybatis-plus.configuration.log-impl=org.apache.ibatis. logging.stdout.StdOutImpl
#Connect redis
spring.redis.host=192.168.22*.1**
spring.redis.port=6379
View cache : The first part of the code is the same @before notification, and the second part of the code is also the same post notification. We can use AOP to separate caching code and business code.
The spring framework should also be able to think of it. --This can be done using annotations. Parse the annotation.
(1) Add the cached configuration class
@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;
(2) Use the enable cache annotation
(3) Usage annotations
//业务代码 //使用查询注解:cacheNames表示缓存的名称 key:唯一标志---dept::key //先从缓存中查看key为(cacheNames::key)是否存在,如果存在则不会执行方法体,如果不存在则执行方法体并把方法的返回值存入缓存中 @Cacheable(cacheNames = {"dept"},key="#id") public Dept findById(Integer id){ Dept dept = deptMapper.selectById(id); return dept; } //先删除缓存在执行方法体。 @CacheEvict(cacheNames = {"dept"},key = "#id") public int deleteById(Integer id){ int row = deptMapper.deleteById(id); return row; } //这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。 @CachePut(cacheNames = "dept",key="#dept.id") public Dept update(Dept dept){ int insert = deptMapper.updateById(dept); return dept; }
Use stress testing tools to test thread safety issues caused by high concurrency
@RestController @RequestMapping("bucket") public class BucketController { @Autowired private BucketService bucketService; @GetMapping("update/{productId}") public String testUpdate(@PathVariable Integer productId){ String s = bucketService.updateById(productId); return s; } }
//此处写就不需要在启动类使用注解 @Mapper public interface BucketMapper extends BaseMapper<Bucket> { public Integer updateBucketById(Integer productId); }
@Data @AllArgsConstructor @NoArgsConstructor public class Bucket { @TableId(value = "productId",type = IdType.AUTO) private Integer productId; private Integer num; }
@Service public class BucketService { @Resource private BucketMapper bucketMapper; public String updateById(Integer productId){ //查看该商品的库存数量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改库存每次减1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣减成功!剩余库存数:"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣减失败!库存数不足"); return "fail"; } } }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qy151wd.dao.BucketMapper"> <update id="updateBucketById" parameterType="int"> update bucket set num=num-1 where productId=#{productId} </update> </mapper>
<dependencies> <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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mp的依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
@Service public class BucketService { @Resource private BucketMapper bucketMapper; public String updateById(Integer productId){ //加自动锁 synchronized (this){ //查看该商品的库存数量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改库存每次减1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣减成功!剩余库存数:"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣减失败!库存数不足"); return "fail"; } } } }If a project cluster is built, then the The lock is invalid. 2.3.2 Using redisTemplate(1) Use idea to open a cluster project ( 2) Use nginx (3) Test results and find that there are repeated numbers and negative inventory. (4) Solution service corresponding code modification
@Service public class BucketService { @Resource private BucketMapper bucketMapper; @Autowired private RedisTemplate redisTemplate; public String updateById(Integer productId){ ValueOperations<String,String> forValue = redisTemplate.opsForValue(); Boolean flag = forValue.setIfAbsent("aaa::" + productId, "-----------------"); if(flag){ try{ //查看该商品的库存数量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改库存每次减1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣减成功!剩余库存数:"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣减失败!库存数不足"); return "fail"; } }finally { redisTemplate.delete("aaa::"+productId); } } return "服务器正忙,请稍后再试......."; } }Note that the pressure measurement speed here is not easy to be too fast (recommended 100 threads in 5 seconds) After the pressure test, the result is:
The above is the detailed content of Analysis of Java Redis usage scenarios. For more information, please follow other related articles on the PHP Chinese website!