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

查询频率比较高,修改频率比较低。
安全系数低的数据
注意要将实体类实现序列化:
- @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.bootgroupId>
- <artifactId>spring-boot-starter-data-redisartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-jdbcartifactId>
- dependency>
-
-
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>3.4.2version>
- dependency>
-
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <optional>trueoptional>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-testartifactId>
- <scope>testscope>
- 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.Driver
- spring.datasource.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai
- spring.datasource.username=root
- spring.datasource.password=root
-
- #sql日志
- mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
- #连接redis
- spring.redis.host=192.168.22*.1**
- spring.redis.port=6379
查看的缓存: 前部分代码相同@before通知,后部分代码也相同后置通知。 我们可以AOP完成缓存代码和业务代码分离。
spring框架它应该也能想到。--使用注解即可完成。解析该注解。
(1)把缓存的配置类加入
- @Bean
- public CacheManager cacheManager(RedisConnectionFactory factory) {
- RedisSerializer
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
{ - 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";
- }
- }
- }
- 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.bootgroupId>
- <artifactId>spring-boot-starter-data-redisartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-jdbcartifactId>
- dependency>
-
-
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>3.4.2version>
- dependency>
-
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <optional>trueoptional>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-testartifactId>
- <scope>testscope>
- 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
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个线程)
经过测压测试后,结果为:
