使用Redis incr解决并发问题的操作

 更新时间:2020年11月24日 09:37:53   作者:chen_lay  
这篇文章主要介绍了使用Redis incr解决并发问题的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

项目背景:

1、新增问题件工单,工单中有工单编码字段,工单编码字段的规则为 “WT”+yyyyMMdd+0000001。

2、每天的工单生成量是30W,所以会存在并发问题

解决思路:

1、首先乐观的认为redis不会宕机,对应的缓存不会被清除(除非人为操作,人为操作会有独立的补救办法)

2、将工单编码存到缓存中(redis),其值只存“WT”+yyyyMMdd后面的数字部分;

对应的key为:key标识+yyyyMMdd,即每天一个key

3、每次生成工单编码时,先调用redis的incr方法,使其在原来编码的基础上加1,并返回结果

4、判断返回的结果,如果返回的是1,说明当前key之前不存在,为生成的新的一天的key,

需要设置此key的生命周期为24小时

5、每个key只会存活24小时

6、如果redis宕机,或者key被删除,调用指定的接口,接口会去数据库查询今天最大的工单编码,

解析后,将其存在redis中,后面的工单编码再在此基础上自增

7、请自行配置redisClient客户端并实例化

代码:

1、编码获取核心方法

/**
 * 通过自定义的方法查询问题件缓存
 * @param redisKey
 * @param codePre
   * @return
   */
 public static String getCodeBySelfRedis(String redisKey,String codePre){
 String nowDateStr = DateTimeUtil.getDateTimeStr(new Date(),DateTimeUtil.DATE_PATTERN_YYYYMMDD);
 Long value = 1L;
 RedisClient uceClient = null;
 Jedis jedis=null;
 try {
  uceClient = SpringContextUtil.getBean("uceClient");
  jedis = uceClient.getResource();
  value = jedis.incr(redisKey+nowDateStr);
  if(value != null){
  //如果值为1说明是第一次设置,那么设置key的存活时间为24小时
  if (value == 1){
   jedis.expire(redisKey+nowDateStr,(24*60*60+1000));
  }
  }else{
  //如指为空,重置缓存
  value = resetCodeRedis(redisKey);
  }
 }catch (Exception e){
  logger.error("获取问题件编码的自定义缓存异常:",e);
  value = resetCodeRedis(redisKey);
 }finally {
  if (uceClient != null){
  uceClient.returnResource(jedis);
  }
 }
 String problemCode = String.valueOf(value);
 for(int i = 0;i < 7;i++){
  if (problemCode.length() < 7){
  problemCode = "0"+problemCode;
  }else{
  break;
  }
 }
 return codePre + nowDateStr + problemCode;
 }

2、宕机时,调用的核心接口方法

/**
 * 重置编码缓存
 * @param redisKey
 * @return
 */
 public static Long resetCodeRedis(String redisKey){
 String oldDateStr = null;
 String nowDateStr = DateTimeUtil.getDateTimeStr(new Date(),DateTimeUtil.DATE_PATTERN_YYYYMMDD);
 //编码的最大字符串
 String databaseMaxCode = null;
 //问题件
 if(StringUtil.isNotEmpty(redisKey) && redisKey.equals(WO_PROBLEM_CODE_KEY)){
  CsWoProblemService csWoProblemService = SpringContextUtil.getBean("csWoProblemService");
  //获取数据库中的问题件的最大编码
  databaseMaxCode = csWoProblemService.getMaxCode(WO_PROBLEM_CODE_PRE);
  //获取编码的日期部分
  if(StringUtil.isNotEmpty(databaseMaxCode)){
  oldDateStr = databaseMaxCode.substring(2,10);
  databaseMaxCode = databaseMaxCode.substring(10);
  }
 }else if(StringUtil.isNotEmpty(redisKey) && redisKey.equals(WO_CUST_SER_CODE_KEY)){
  CsWoCustSerService csWoCustSerService = SpringContextUtil.getBean("csWoCustSerService");
  //获取数据库中的客户服务类工单的最大编码
  databaseMaxCode = csWoCustSerService.getMaxCode("");
  //获取编码的日期部分
  if(StringUtil.isNotEmpty(databaseMaxCode)){
  oldDateStr = databaseMaxCode.substring(0,8);
  databaseMaxCode = databaseMaxCode.substring(8);
  }
 }
 Long value = getRedisValue(oldDateStr,nowDateStr,databaseMaxCode);
 RedisClient uceClient = null;
 Jedis jedisCluster = null;
 try {
  uceClient = SpringContextUtil.getBean("uceClient");
  jedisCluster = uceClient.getResource();
  boolean keyExist = jedisCluster.exists(redisKey + nowDateStr);
  // NX是不存在时才set, XX是存在时才set, EX是秒,PX是毫秒
  if (keyExist) {
  jedisCluster.del(redisKey + nowDateStr);
  }
  //设置缓存值,并设置为24小时后自动失效
  jedisCluster.set(redisKey + nowDateStr, String.valueOf((value + 1)), "NX","EX", (24*60*60+1000));
 }catch (Exception e){
  logger.error((redisKey + "编码重置异常:"),e);
 }finally {
  if(uceClient != null){
  uceClient.returnResource(jedisCluster);
  }
 }
 return value + 1;
 }
 
 /**
 * 解析redis的值
 * @param oldDateStr
 * @param nowDateStr
 * @param databaseMaxCode
 * @return
 */
 public static Long getRedisValue(String oldDateStr,String nowDateStr,String databaseMaxCode){
 Long value = 0L;
 String firstCodeZero = "0";
 //如果日期相同,解析编码数据存到缓存中
 if(StringUtil.isNotEmpty(oldDateStr) && StringUtil.isNotEmpty(nowDateStr) && oldDateStr.equals(nowDateStr) && StringUtil.isNotEmpty(databaseMaxCode)){
  for(int i = 0;i < 7;i++){
  String firstCode = databaseMaxCode.substring(0,1);
  if (StringUtil.isNotEmpty(firstCode) && firstCodeZero.equals(firstCode)){
   databaseMaxCode = databaseMaxCode.substring(1);
  }else{
   break;
  }
  }
  if (StringUtil.isNotEmpty(databaseMaxCode)){
  value = Long.parseLong(databaseMaxCode);
  }
 }
 return value;
 }

注意:

jedis使用后一定要close,否则jedis连接被占用的会越来越多,可用的连接数会越来越少,最终会导致redis宕机,最终项目宕机。

本项目是在finally中调用的自己封装的returnResource()方法,此方法中会进行关闭操作

补充知识:redis在高并发下导致锁失效问题

解决办法:

可以给线程加唯一标识 关闭线程时判断标识是否相同

问题2:线程超时问题如何解决 同一时间会有俩个或俩个以上线程操作同一方法

使用分布式锁redisson

以上这篇使用Redis incr解决并发问题的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Netty分布式固定长度解码器实现原理剖析

    Netty分布式固定长度解码器实现原理剖析

    这篇文章主要为大家介绍了Netty分布式固定长度解码器原理剖析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • Java基础之方法重写详解

    Java基础之方法重写详解

    这篇文章主要介绍了Java基础之方法重写详解,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-05-05
  • SpringBoot获取HttpServletRequest的3种方式总结

    SpringBoot获取HttpServletRequest的3种方式总结

    这篇文章主要给大家介绍了关于SpringBoot获取HttpServletRequest的3种方式,在Spring boot项目中经常要用到Servlet的常用对象如HttpServletRequest request,HttpServletResponse response,HttpSession session,需要的朋友可以参考下
    2023-08-08
  • Java方法重写的超详细讲解

    Java方法重写的超详细讲解

    在 Java 编程的精彩世界里,方法重写是一项极为重要且实用的特性,它犹如一把神奇的钥匙,为我们开启了面向对象编程中多态性的大门,今天,就让我们一同深入探索 Java 方法重写的奥秘,感兴趣的小伙伴跟着小编一起来看看吧
    2025-03-03
  • spring boot环境抽象的实现方法

    spring boot环境抽象的实现方法

    在实际开发中,开发人员在编写springboot的时候通常要在本地环境测试然后再部署到Production环境,这两种环境一般来讲是不同的,最主要的区别就是数据源的不同。本文主要介绍了这两种,感兴趣的可以了解一下
    2019-04-04
  • Java中常用判空与判等方法详解

    Java中常用判空与判等方法详解

    java中感觉判空的一些方法太多了,感觉有点儿乱糟糟的,所以这篇文中就为大家简单总结一下在项目中常用的一些方法吧,希望对大家有所帮助
    2025-03-03
  • Java8 Optional常用方法使用场景分析

    Java8 Optional常用方法使用场景分析

    这篇文章主要介绍了Java8 Optional常用方法使用场景,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Java JSON转成List结构数据

    Java JSON转成List结构数据

    这篇文章主要介绍了Java JSON转成List结构数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • Spring定时任务注解@Scheduled详解

    Spring定时任务注解@Scheduled详解

    这篇文章主要介绍了Spring定时任务注解@Scheduled详解,@Scheduled注解是包org.springframework.scheduling.annotation中的一个注解,主要是用来开启定时任务,本文提供了部分实现代码与思路,需要的朋友可以参考下
    2023-09-09
  • Java黑科技:replace首个替换一秒搞定

    Java黑科技:replace首个替换一秒搞定

    要实现只替换第一个匹配项,可以使用Java中的String类的replaceFirst方法,该方法接受两个参数,第一个参数是要替换的字符串或正则表达式,第二个参数是替换后的字符串,需要的朋友可以参考下
    2023-10-10

最新评论