> Java > java지도 시간 > 본문

Java Redis 사용 시나리오 분석

王林
풀어 주다: 2023-05-09 13:07:17
앞으로
926명이 탐색했습니다.

1. 캐시로

1.1 사용 이유

데이터가 메모리에 저장되며 데이터 쿼리 속도가 빠릅니다. 데이터베이스 압력을 공유할 수 있습니다.

Java Redis 사용 시나리오 분석

1.2 어떤 종류의 데이터가 캐시에 적합합니까

쿼리 빈도가 상대적으로 높고 수정 빈도가 상대적으로 낮습니다.

보안 요소가 낮은 데이터

1.3 Redis를 캐시로 사용

1.3.1 구성 클래스가 사용되지 않습니다

엔티티 클래스를 직렬화해야 합니다.

@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>
로그인 후 복사

컨트롤러 레이어 해당 코드:

@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
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.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

본 캐시: 코드의 첫 부분은 동일합니다 @ 알림 전 코드의 뒷부분은 게시물 알림에도 동일하게 적용됩니다. AOP를 사용하여 캐싱 코드와 비즈니스 코드를 분리할 수 있습니다.

스프링 프레임워크도 생각해 볼 수 있어야 합니다. --이 작업은 주석을 사용하여 수행할 수 있습니다. 주석을 구문 분석합니다.

1.3.2 구성 클래스 사용

(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) 캐시 주석 사용

Java Redis 사용 시나리오 분석

(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;
    }
로그인 후 복사

2 스트레스 테스트 사용. 도구는 높은 동시성 테스트에서 스레드 안전 문제를 가져옵니다

2.1 스트레스 테스트 도구 사용

Java Redis 사용 시나리오 분석

Java Redis 사용 시나리오 분석

Java Redis 사용 시나리오 분석

Java Redis 사용 시나리오 분석내부 구성:

Java Redis 사용 시나리오 분석

Java Redis 사용 시나리오 분석

Java Redis 사용 시나리오 분석2.2 스톡 프로젝트

2.2.1 컨트롤러 레이어

@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;
    }
}
로그인 후 복사
2.2.2 dao 레이어
//此处写就不需要在启动类使用注解
@Mapper
public interface BucketMapper extends BaseMapper<Bucket> {
    public Integer updateBucketById(Integer productId);
}
로그인 후 복사
2.2.3 엔터티 레이어
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Bucket {
    @TableId(value = "productId",type = IdType.AUTO)
    private Integer productId;
    private Integer num;
}
로그인 후 복사
2.2.4 서비스 레이어
@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";
        }
    }
}
로그인 후 복사
2.2.5 mapper
<?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>
로그인 후 복사
2.2.6 dependency
    <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>
로그인 후 복사
2.2 .7 테스트 결과

Java Redis 사용 시나리오 분석 동일한 인벤토리가 n번 사용된 것을 볼 수 있습니다. 그리고 데이터베이스의 재고가 마이너스입니다. 스레드 안전 문제로 인해 발생합니다.

2.3 솔루션

2.3.1 동기화 또는 잠금 잠금 사용

해당 서비스 계층이
@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";
            }
        }
    }
}
로그인 후 복사

로 수정됩니다. 프로젝트 클러스터가 구축되면 잠금이 유효하지 않습니다.

Java Redis 사용 시나리오 분석2.3.2 redisTemplate 사용

(1) idea를 사용하여 클러스터 프로젝트 열기

Java Redis 사용 시나리오 분석 (2) nginx 사용

Java Redis 사용 시나리오 분석 (3) 테스트 결과

Java Redis 사용 시나리오 분석 귀: 반복 수치와 재고가 마이너스입니다.

(4) 솔루션

Java Redis 사용 시나리오 분석서비스 해당 코드 수정

@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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:yisu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿