数据存储在内存中,数据查询速度快。可以分摊数据库压力。

查询频率比较高,修改频率比较低。
安全系数低的数据
注意要将实体类实现序列化:
立即学习“Java免费学习笔记(深入)”;
@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;
}对应依赖:
<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层对应代码:
@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层对应代码:
@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;
}
}配置源:
# 配置数据源spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghaispring.datasource.username=rootspring.datasource.password=root#sql日志mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl#连接redisspring.redis.host=192.168.22*.1**spring.redis.port=6379
查看的缓存: 前部分代码相同@before通知,后部分代码也相同后置通知。 我们可以AOP完成缓存代码和业务代码分离。
spring框架它应该也能想到。--使用注解即可完成。解析该注解。
(1)把缓存的配置类加入
@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) 使用开启缓存注解

(3)使用注解
//业务代码
//使用查询注解: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;
}使用压测工具测试高并发下带来线程安全问题




内部配置:



@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>
我们看到同一个库存被使用了n次。以及数据库中库存为负数。 线程安全问题导致。
对应的service层修改为
@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";
}
}
}
}如果搭建了项目集群,那么该锁无效 。

(1)使用idea开集群项目

(2)使用nginx

(3)测试结果

发现又出现: 重复数字以及库存为负数。
(4)解决方法

service对应代码修改
@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 "服务器正忙,请稍后再试.......";
}
}注意此处的测压速度不易太快(推荐使用5秒100个线程)
经过测压测试后,结果为:

以上就是Java Redis使用场景实例分析的详细内容,更多请关注php中文网其它相关文章!
java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号