Redis(三)
创始人
2024-11-20 08:04:47
0

1. java连接redis

java提高连接redis的方式jedis. 我们需要遵循jedis协议。

引入依赖

                      redis.clients             jedis             4.3.1          

2 java连接redis集群模式

 public static void main(String[] args) {         Set nodes=new HashSet<>();         nodes.add(new HostAndPort("192.168.111.188",7001));         nodes.add(new HostAndPort("192.168.111.188",7002));         nodes.add(new HostAndPort("192.168.111.188",7003));         nodes.add(new HostAndPort("192.168.111.188",7004));         nodes.add(new HostAndPort("192.168.111.188",7005));         nodes.add(new HostAndPort("192.168.111.188",7006));         JedisCluster jedisCluster=new JedisCluster(nodes);         jedisCluster.set("k5","666");         System.out.println(jedisCluster.get("k5"));     }

适合ssm项目。

3. springboot整合redis

starter启动依赖。---包含自动装配类---完成相应的装配功能。

引入依赖

 
       
            org.springframework.boot
            spring-boot-starter-data-redis
       

修改配置文件

#redis 配置 spring.redis.host=192.168.100.104 spring.redis.port=6379 spring.redis.database=1 spring.redis.jedis.pool.max-active=100 spring.redis.jedis.pool.max-idle=10 spring.redis.jedis.pool.max-wait=10000ms 

使用

springboot整合redis时封装了两个工具类:StringRedisTemplate和RedisTemplate.

StringRedisTemplate它是RedisTemplate的子类。StringRedisTemplate里面只能存放字符串的内容。

1. StringRedisTemplate

package com.zql;  import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations;  import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit;  /**  * Spring Boot Redis 应用的测试类,用于验证 Redis 的集成和操作。  */ @SpringBootTest class SpringBootRedisApplicationTests {     // 自动注入 StringRedisTemplate,用于进行 Redis 的字符串操作     @Autowired     private StringRedisTemplate stringRedisTemplate;      /**      * 测试 Redis Hash 的操作,包括添加、获取和删除数据。      */     //关于Hash的操作     @Test     void testHash(){         // 获取 Hash 操作实例         HashOperations ops = stringRedisTemplate.opsForHash();         // 向 Hash 中添加单个字段值         //存放数据 hset(k,f,v)         ops.put("user","name","zql");         // 向 Hash 中添加多个字段值         Map map=new HashMap<>();         map.put("name","zql");         map.put("age","18");         map.put("sex","男");         ops.putAll("user",map);         // 获取 Hash 中的单个字段值         //获取指定的元素         Object name = ops.get("user", "name");         // 获取 Hash 中的所有字段值         Map user = ops.entries("user");         // 获取 Hash 中的所有字段名         Set keys = ops.keys("user");         // 获取 Hash 中的所有字段值         List values = ops.values("user");     }     /**      * 测试 Redis 字符串操作,包括设置、获取和递增操作。      */     //关于String字符串的操作     @Test     void testString(){         // 获取字符串操作实例         //得到操作字符串的类对象         ValueOperations ops = stringRedisTemplate.opsForValue();         // 设置字符串值         //存放数据---set(k,v)         ops.set("k1","v1");         ops.set("k2","2");         // 为字符串设置过期时间并设置值         //存放数据--settex(k,second,v)         ops.set("k3","v3",30, TimeUnit.SECONDS);         // 如果键不存在,则设置键值并设置过期时间         Boolean aBoolean = ops.setIfAbsent("k4", "v4", 30, TimeUnit.SECONDS);         // 获取字符串值         String k1 = ops.get("k1");         // 对字符串进行递增操作         Long k2 = ops.increment("k2", 10);     }     /**      * 测试 Redis 关于键的操作,包括查找、删除、检查存在性和设置过期时间。      */     //关于key的操作     @Test     void testKeys() {         // 查找所有匹配的键         Set keys = stringRedisTemplate.keys("*");         // 删除指定的键         Boolean k1 = stringRedisTemplate.delete("k1");         // 检查键是否存在         Boolean k11 = stringRedisTemplate.hasKey("k1");         // 为键设置过期时间         Boolean k12 = stringRedisTemplate.expire("k4", 30, TimeUnit.SECONDS);     }  }  

2. RedisTemplate

它属于StringRedisTemplate的父类,它的泛型默认都是Object。它可以直接存储任意类型的key和value.

package com.zql;  import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; import com.zql.entity.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.StringRedisSerializer;   @SpringBootTest class SpringBootRedisApplicationTest02 { @Autowired private RedisTemplate redisTemplate; @Test     public void test01() {     //指定key的序列化方式     redisTemplate.setKeySerializer(new StringRedisSerializer());     //指定value的序列化方式     redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));      ValueOperations valueOperations = redisTemplate.opsForValue();     valueOperations.set("name","zhangsan");     System.out.println(valueOperations.get("name"));     valueOperations.set             ("k6",new User("zql",15));     JSONObject k6=(JSONObject) valueOperations.get("k6");      HashOperations forHash = redisTemplate.opsForHash();     forHash.put("u","n","zhangsan");     forHash.put("u","张三","张三杀回"); } } 

我们可以自己写个工具类

package com.zql.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.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-23 19:47  **/ @Configuration public class RedisConfig {      /**      * 配置Redis模板      *      * @param factory Redis连接工厂,用于创建Redis连接。      * @return RedisTemplate实例,配置了键值的序列化方式。      */     @Bean     public RedisTemplate redisTemplate(RedisConnectionFactory factory){         RedisTemplate template=new RedisTemplate<>();//创建redisTemplate对象         RedisSerializer redisSerializer=new StringRedisSerializer();//字符串序列化器         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer<>(Object.class);//json序列化器          // 配置ObjectMapper,用于序列化和反序列化Java对象为JSON。         ObjectMapper om=new ObjectMapper();         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);         jackson2JsonRedisSerializer.setObjectMapper(om);          // 配置Redis模板的序列化方式。         template.setConnectionFactory(factory);         //key序列化方式         template.setKeySerializer(redisSerializer);         //value序列化方式         template.setValueSerializer(jackson2JsonRedisSerializer);         //hash的key序列化方式         template.setHashKeySerializer(jackson2JsonRedisSerializer);         template.setHashValueSerializer(redisSerializer);         return template;     } } 

4. 集群模式

在配置文件里面配置集群的配置

#集群模式
spring.redis.cluster.nodes=192.168.111.188:7006,192.168.111.188:7001,192.168.111.188:7002,192.168.111.188:7003,192.168.111.188:7004,192.168.111.188:7005
 

5. 短信业务

项目结构

controller

package com.zql.controller;  import com.zql.SendMsgUtil; import com.zql.vo.LoginVo; import com.zql.vo.R; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*;  import java.util.concurrent.TimeUnit;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 18:35  **/ @RestController @RequestMapping("/msg") public class MsgController {     @Autowired     private StringRedisTemplate redisTemplate;      @GetMapping("send")     public R send(String phone) throws Exception {         //1.校验手机号是否存在 --连接数据库         if (phone.equals("13938300924")) {             if (redisTemplate.hasKey("code::" + phone)) {                 return new R(500, "验证码已发送", null);             }             //2. 发送验证码             String code = SendMsgUtil.sendCode(phone);             //3.将验证码存入redis             redisTemplate.opsForValue().set("code::" + phone, code, 5, TimeUnit.MINUTES);             return new R(200, "发送成功", null);         }         return new R(500, "手机号未注册", null);     }      @PostMapping("login")     public R login(@RequestBody LoginVo loginVo) {         //1.校验验证码         String code = redisTemplate.opsForValue().get("code::" + loginVo.getPhone());         String phone = loginVo.getPhone();         if (StringUtils.hasText(loginVo.getCode()) && loginVo.getCode().equals(code)) {             //2.登录成功             if (phone.equals("13938300924")) {                // redisTemplate.delete("code::" + phone);                 return new R(200, "登录成功", null);             } else {                 return new R(500, "手机号未注册", null);             }         }             return new R(500, "验证码错误", null);      } }  

entity

package com.zql.entity;  import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;  import java.io.Serializable;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-23 19:18  **/ @Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable {     private String name;     private int age; } 

vo

package com.zql.vo;  import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 18:46  **/ @Data @AllArgsConstructor @NoArgsConstructor public class LoginVo {     private String phone;     private String code; } 
package com.zql.vo;  import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 18:40  **/ @Data @AllArgsConstructor @NoArgsConstructor public class R {     private Integer code;     private String msg;     private Object data; } 

发送短信的配置类 里面带****的使用自己的配置就行

package com.zql;  import cn.hutool.captcha.generator.RandomGenerator; import com.aliyun.teaopenapi.Client; import org.springframework.beans.factory.annotation.Value;  /**  * @Author:   * @Description:  * @Date: Create in 17:30 2024/7/23  */ public class SendMsgUtil {     /**      * 使用AK&SK初始化账号Client      * @return Client      * @throws Exception      */     @Value("${aliyun.msg.accessKeyId}")     private static String accessKeyId;     public static Client createClient() throws Exception {         com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()                 // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。                 .setAccessKeyId("**************")                  // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。                 .setAccessKeySecret("****************");         // Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi         config.endpoint = "dysmsapi.aliyuncs.com";         return new com.aliyun.teaopenapi.Client(config);     }      /**      * API 相关      * @return OpenApi.Params      */     public static com.aliyun.teaopenapi.models.Params createApiInfo() throws Exception {         com.aliyun.teaopenapi.models.Params params = new com.aliyun.teaopenapi.models.Params()                 // 接口名称                 .setAction("SendSms")                 // 接口版本                 .setVersion("2017-05-25")                 // 接口协议                 .setProtocol("HTTPS")                 // 接口 HTTP 方法                 .setMethod("POST")                 .setAuthType("AK")                 .setStyle("RPC")                 // 接口 PATH                 .setPathname("/")                 // 接口请求体内容格式                 .setReqBodyType("json")                 // 接口响应体内容格式                 .setBodyType("json");         return params;     }      public static String sendCode(String phone) throws Exception {         com.aliyun.teaopenapi.Client client = createClient();         com.aliyun.teaopenapi.models.Params params = createApiInfo();         // query params         java.util.Map queries = new java.util.HashMap<>();         queries.put("PhoneNumbers", phone);         queries.put("SignName", "智友");         queries.put("TemplateCode", "SMS_173342144"); //您正在申请手机注册,验证码为:${code},5分钟内有效!         RandomGenerator randomGenerator=new RandomGenerator("0123456789",6);         String code= randomGenerator.generate();         queries.put("TemplateParam", "{\"code\":\""+code+"\"}");         // runtime options         com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();         com.aliyun.teaopenapi.models.OpenApiRequest request = new com.aliyun.teaopenapi.models.OpenApiRequest()                 .setQuery(com.aliyun.openapiutil.Client.query(queries));         // 复制代码运行请自行打印 API 的返回值         // 返回值为 Map 类型,可从 Map 中获得三类数据:响应体 body、响应头 headers、HTTP 返回的状态码 statusCode。         client.callApi(params, request, runtime);         return code;     } } 

测试

package com.zql;  import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-23 20:40  **/ @SpringBootTest  class Test01 {     @Test     public void test01()throws Exception{         System.out.println(SendMsgUtil.sendCode("*********"));     } } 

5.1 注册登录功能

6. redis的使用场景-热点数据缓存

6.1 什么是缓存?

为了把一些经常访问的数据,放入缓存中以减少对数据库的访问频率。从而减少数据库的压力,提高程序的性能。【内存中存储】

6.2 缓存的原理

6.3 什么样的数据适合放入缓存中

1. 查询频率高且修改频率低 2. 数据安全性低

6.4 哪个组件可以作为缓存

  1. redis组件

  2. memory组件

  3. ehcache组件

6.5 java使用redis如何实现缓存功能

配置文件

# 应用服务 WEB 访问端口 server.port=8080  #数据库 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/zql?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456   #映射文件路径 mybatis-plus.mapper-locations=classpath*:mapper/*.xml  #配置日志--sql日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl  #redis 配置 spring.redis.host=192.168.100.104 spring.redis.port=6379 spring.redis.database=1 spring.redis.jedis.pool.max-active=100 spring.redis.jedis.pool.max-idle=10 spring.redis.jedis.pool.max-wait=10000ms 

项目结构

config

package com.zql.config;  import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;  import java.time.Duration;  /**  * @program: spring-boot01  * @description:  * @author: 赵庆龙  * @create: 2024-07-23 19:47  **/ @Configuration public class RedisConfig {     @Bean//把该方法返回的类对象交与spring容器管理     public RedisTemplate redisTemplate(RedisConnectionFactory factory){         RedisTemplate template=new RedisTemplate<>();//创建redisTemplate对象         RedisSerializer redisSerializer=new StringRedisSerializer();//字符串序列化器         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer<>(Object.class);//json序列化器         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(redisSerializer);         //value序列化方式         template.setValueSerializer(jackson2JsonRedisSerializer);         //hash的key序列化方式         template.setHashKeySerializer(jackson2JsonRedisSerializer);         template.setHashValueSerializer(redisSerializer);         return template;     }     @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;     }  } 

controller

package com.zql.controller;  import com.zql.entity.Dept; import com.zql.service.DeptService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-25 09:16  **/ @RestController @RequestMapping("/dept") public class DeptController {     @Autowired     private DeptService deptService;     @GetMapping("/getById/{id}")     public Dept getById(@PathVariable  Integer id) {         Dept dept = deptService.getById(id);         return dept;      }     @PostMapping("/insert")     public Dept insert(@RequestBody Dept dept) {         return deptService.insert(dept);     }     @PutMapping("/update")     public Dept update(@RequestBody Dept dept) {         return deptService.update(dept);     }     @DeleteMapping("/delete/{id}")     public int delete(@PathVariable Integer id) {         return deptService.delete(id);     } } 

dao

package com.zql.dao;  import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.zql.entity.Dept;  /**  * @program: spring-boot01  * @description:  * @author:  * @create: 2024-07-24 20:00  **/ public interface DeptDao extends BaseMapper { } 

entity

package com.zql.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: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 19:58  **/ @Data @NoArgsConstructor @AllArgsConstructor @TableName("tb_dept") public class Dept {     @TableId(type = IdType.AUTO)     private Integer did;     private String dname;     private String loc;  } 

service

package com.zql.service;  import com.zql.entity.Dept;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 20:02  **/ public interface DeptService {      public Dept getById(Integer id);     public Dept insert(Dept dept);     public Dept update(Dept dept);     public int delete(Integer id);  } 
package com.zql.service.impl;  import com.zql.dao.DeptDao; import com.zql.entity.Dept; import com.zql.service.DeptService; 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;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 20:07  **/ @Service public class DeptServiceImpl01 implements DeptService {      @Autowired     private DeptDao deptDao;     @Autowired     private RedisTemplate redisTemplate;     @Override     public Dept getById(Integer id) {         //1.查询redis缓存是否命中         ValueOperations ops = redisTemplate.opsForValue();         Object o=ops.get("dept::"+id);         //表示缓存命中         if (o!=null){             return (Dept) o;         }         //查询数据库         Dept dept = deptDao.selectById(id);         if (dept!=null){             ops.set("dept::"+id,dept);         }         return dept;     }      @Override     public Dept insert(Dept dept) {         int insert = deptDao.insert(dept);         return dept;     }      @Override     public Dept update(Dept dept) {      // 更新数据库         int i = deptDao.updateById(dept);         if (i>0){             //删除缓存             redisTemplate.opsForValue().set("dept::"+dept.getDid(),dept);         }         return dept;     }      @Override     public int delete(Integer id) {         int i = deptDao.deleteById(id);         if (i>0){             //删除缓存             redisTemplate.delete("dept::"+id);         }         return i;     } } 

发现: 业务层代码除了要维护核心业务功能外,额外还要维护缓存的代码。

如何解决: 使用AOP面向切面编程。

spring框架也能想到。---aop切面来解决。

6.6 使用缓存注解完成缓存功能

package com.zql.service.impl;  import com.zql.dao.DeptDao; import com.zql.entity.Dept; import com.zql.service.DeptService; 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.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service;  /**  * @program: spring-boot01  * @description:  * @author:   * @create: 2024-07-24 20:07  **/ @Service public class DeptServiceImpl implements DeptService {      @Autowired     private DeptDao deptDao;     @Autowired     private RedisTemplate redisTemplate;     //Cacheable:表示查询时使用的注解。 cacheNames:缓存的名称  key:缓存的唯一表示值     //   1. 查询缓存中是否存在名称为cacheNames::key的值     //2.如果存在则方法不会执行     //   3. 如果不存在则执行方法体并把方法的返回结果放入缓存中cacheNames::key   @Cacheable(cacheNames = "dept",key = "#id")     @Override     public Dept getById(Integer id) {         //1.查询redis缓存是否命中         //查询数据库         Dept dept = deptDao.selectById(id);          return dept;     }       @Override     public Dept insert(Dept dept) {         int insert = deptDao.insert(dept);         return dept;     }     //CachePut:表示修改时使用的注解.     // 1. 先执行方法体     // 2. 把方法的返回结果放入缓存中 @CachePut(cacheNames = "dept",key = "#dept.did")     @Override     public Dept update(Dept dept) {      // 更新数据库          int i = deptDao.updateById(dept);          return dept;     }     //CacheEvict:表示删除时使用的注解     // 1. 先执行方法体     // 2. 把缓存中名称为cacheNames::key的值删除      @CacheEvict(cacheNames = "dept",key = "#id")     @Override     public int delete(Integer id) {         int i = deptDao.deleteById(id);         return i;     } } 

开启缓存注解

7. redis的使用场景--分布式锁.

模拟高并发:---jmeter压测工具

通过压测发现库存超卖和重卖了。---解决办法使用锁

syn和lock锁。

package com.ykq.service;  import com.ykq.dao.StockDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  /**  * @program: distinct-lock  * @description:  * @author:   * @create: 2023-08-31 16:49  **/ @Service public class StockService01 {      @Autowired     private StockDao stockDao;       //如果在单线程下---该功能没有任何问题。     //如果在多线程的情况---jmeter压测工具---发现出现线程安全问题了。==使用锁synchronized ()或lock锁。     //发现使用锁之前没有问题了。但是如果该项目是一个集群。--发现在集群的情况下本地锁【只对当前工程有效】无效了。     //解决方案就是集群工程共用一把锁就行。---可以使用redis来解决问题。     public String decrement(Integer productid) {         //根据id查询商品的库存         synchronized (this) {             int num = stockDao.findById(productid);             if (num > 0) {                 //修改库存                 stockDao.update(productid);                 System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");                 return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";             } else {                 System.out.println("商品编号为:" + productid + "的商品库存不足。");                 return "商品编号为:" + productid + "的商品库存不足。";             }         }     } }  

上面使用syn和lock虽然解决了并发问题,但是我们未来项目部署时可能要部署集群模式。

nginx代理集群

通过压测发现本地锁 无效了。使用redis解决分布式锁文件

package com.ykq.service;  import com.ykq.dao.StockDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service;  import java.util.concurrent.TimeUnit;  /**  * @program: distinct-lock  * @description:  * @author:   * @create: 2023-08-31 16:49  **/ @Service public class StockService {      @Autowired     private StockDao stockDao;     @Autowired     private StringRedisTemplate redisTemplate;     //     public String decrement(Integer productid) {         ValueOperations opsForValue = redisTemplate.opsForValue();         //1.获取共享锁资源         Boolean flag = opsForValue.setIfAbsent("product::" + productid, "1111", 30, TimeUnit.SECONDS);         //表示获取锁成功         if(flag) {             try {                 //根据id查询商品的库存                 int num = stockDao.findById(productid);                 if (num > 0) {                     //修改库存                     stockDao.update(productid);                     System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");                     return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";                 } else {                     System.out.println("商品编号为:" + productid + "的商品库存不足。");                     return "商品编号为:" + productid + "的商品库存不足。";                 }             }finally {                 //释放锁资源                 redisTemplate.delete("product::"+productid);             }         }else{             //休眠100毫秒 在继续抢锁             try {                 Thread.sleep(100);             } catch (InterruptedException e) {                 throw new RuntimeException(e);             }             return decrement(productid);         }     } }

redis超时问题[业务代码执行时间超过了上锁时间]. 第三方redisson

引入redisson依赖
     
       
            org.redisson
            redisson
            3.24.3
       

@Configuration public class RedissonConfig {      @Bean     public RedissonClient redisson(){         Config config = new Config(); //        //连接的为redis集群 //        config.useClusterServers() //                // use "rediss://" for SSL connection //                .addNodeAddress("redis://127.0.0.1:7181","","","") //        ;         //连接单机         config.useSingleServer().setAddress("redis://192.168.111.188:6379");         RedissonClient redisson = Redisson.create(config);         return redisson;     } }
package com.ykq.service;  import com.ykq.dao.StockDao; import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service;  import java.util.concurrent.TimeUnit;   @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();         }     } }

相关内容

热门资讯

8分钟了解!大赢家对冲助手(辅... 1、这是跨平台的大赢家黑科技,在线的操作超级的方便,而且功能也是很强大的。2、在线的操作方便,实用性...
必备科技!心悦麻将助赢神器购买... 1、点击下载安装,心悦麻将插件透视分类一目了然!2、免费高速下载,支持心悦麻将软件透视挂辅助器多线程...
查到实测!微友辅助器通用版(辅... 查到实测!微友辅助器通用版(辅助挂)wpk胜率跟号有关的,仔细教程(有挂指点);微友中的10万兆豆可...
攻略辅助!吉安中至麻将教你提高... 攻略辅助!吉安中至麻将教你提高胜率(辅助挂)微扑克有挂的,周全教程(有挂攻略);中至麻将是一款益智类...
新2024美金局!牌乐门app... 新2024美金局!牌乐门app教你提高胜率(辅助挂)wpk有机器人的,详实教程(有挂指示);亲,关键...
八分钟了解!浙江游戏大厅有猫腻... 1、不需要AI权限,帮助你快速的进行浙江游戏大厅计算辅助教程,沉浸在游戏的游玩之中。2、里面整个浙江...
指导大家!微乐家乡麻将教你让系... 指导大家!微乐家乡麻将教你让系统发好牌(辅助器)微扑克的辅助工具,透彻教程(有挂指示)最新版2024...
apt攻击属于什么 APT(Advanced Persistent Threat,高级持续性威胁)攻击属于网络安全领域,...
出现ie为什么 "出现ie"可能是指在使用某些软件或服务时,遇到了Internet Explorer(IE)浏览器。...
网神secfox是什么 网神secfox是一款网络安全产品,主要用于保护网络系统免受黑客攻击、病毒感染等安全威胁。它可以帮助...