使用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解决并发问题的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • springboot配置redis过程详解

    在springboot中,默认继承好了一套完好的redis包,可以直接使用,但是如果使用中出了错不容易找到错误的原因,因此这里使用自己配置的redis: 需要使用的三个主要jar包: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>

  • 利用spring-data-redis实现incr自增的操作

    应该有不少人在使用spring-data-redis时遇到各种各样的问题.反正我是遇到了. 由于是隔了一段时间才写的本篇博客,也懒得去重现哪些错误场景了,下面凭着记忆写了几个我遇到的问题: redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range 使用的RedisTemplate,做读写操作时候,都是要经过序列化和反序列化. 这时你使用redisTemplate.o

  • SpringBoot整合Redis的步骤

    1.添加配置文件: Redis.properties # 配置单台redis服务器 redis.host=192.168.126.129 ip地址 redis.port=6379 端口号 设置配置类: RedisConfig 2.将对象转化为Json格式入门案例 API: MAPPER.writeValueAsString(itemDesc); 3.将对象转化为Json格式格式优化 4.AOP实现Redis缓存 AOP实现Redis @cacheFind()注解 实现策略: A.需要自定义注解C

  • 使用redis的increment()方法实现计数器功能案例

    一直知道redis可以用来实现计数器功能,但是之前没有实际使用过,昨天碰到一个需求:用户扫码当天达到20次即提示:当日扫码次数达到上限! 当时就想到使用redis的递增方法increment()来实现计数器功能,一定要注意redisTemplate和stringRedisTemplate的使用 首先设置key: 该key我使用了用户id和当天日期作为key的一部分,date:xxxx-xx-xx格式,这样一来该用户在第二天扫码的时候又是一个新key,因为日期不同了 设置key的过期时间: 实现计

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

    项目背景: 1.新增问题件工单,工单中有工单编码字段,工单编码字段的规则为 "WT"+yyyyMMdd+0000001. 2.每天的工单生成量是30W,所以会存在并发问题 解决思路: 1.首先乐观的认为redis不会宕机,对应的缓存不会被清除(除非人为操作,人为操作会有独立的补救办法) 2.将工单编码存到缓存中(redis),其值只存"WT"+yyyyMMdd后面的数字部分: 对应的key为:key标识+yyyyMMdd,即每天一个key 3.每次生成工单编码时,先

  • Redis原子计数器incr,防止并发请求操作

    一.前言 在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试.这些都是通过对同一时刻请求数量进行了限制,一般用作对后台系统的保护,防止系统因为过大的流量冲击而崩溃.对于系统崩溃带来的后果,显然还是拒绝一部分请求更能被维护者所接受. 而在各种限流中,除了系统自身设计的带锁机制的计数器外,利用Redis实现显然是一种既高效安全又便捷方便的方式. 二.incr命令 Redis Incr 命令将 key 中储存的数字值增一. 如果 key 不存在,那么

  • 关于SpringBoot 使用 Redis 分布式锁解决并发问题

    目录 问题背景 解决方案 主要实现原理: 可靠性: SpringBoot 集成使用 Redis 分布式锁 使用示例 参考文档 问题背景 现在的应用程序架构中,很多服务都是多副本运行,从而保证服务的稳定性.一个服务实例挂了,其他服务依旧可以接收请求.但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的:接收到请求后,先查询 DB 看是否有相关的数据,如果没有则插入数据,如果有则更新数据.在这种场景下如果相同的 N 个请求并发发到后端服务实例,就会出现重复插入数据的情况:

  • Docker安装Redis并介绍可视化客户端进行操作

    1 简介 Redis是使用ANSI C语言开发的基于Key-Value的高性能NoSQL数据库,在解决高并发.高可用等一系列问题中,它扮演着重要的角色.它的优势主要有: 速度快.持久化.原子性.数据结构丰富.多语言支持. 本文将简单介绍如何通过Docker安装Redis,并简单使用与操作. 2 Docker安装与使用 2.1 Docker安装 拉取Redis镜像,选择目前最新的稳定版本5.0.8: $ docker pull redis:5.0.8 建议配置成阿里云的镜像,速度会快很多.拉取成功

  • PHP开发中解决并发问题的几种实现方法分析

    本文实例讲述了PHP开发中解决并发问题的几种实现方法.分享给大家供大家参考,具体如下: 对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了 在PHP语言中并没有原生的提供并发的解决方案,因此就需要借助其他方式来实现并发控制. 方案一:使用文件锁排它锁 flock函数用于获取文件的锁,这个锁同时只能被一个线程获取到,其它没有获取到锁的线程要么阻塞,要么获取失败 在获取到锁的时候,先查询库存,如果库存大于0,则进行下订单操作,减库存,然后释放锁 方案二:使用Mysq

  • PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)

    上一篇文章聊了一下使用Redis事务来解决高并发商品超卖问题,今天我们来聊一下使用Redis链表来解决高并发商品超卖问题. 实现原理 使用redis链表来做,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用. 实现步骤 第一步,先将商品库存入队列 /** * 添加商品数量到商品队列 * @param int $couponId 优惠券ID */ function addCoupons($couponId) { //1.初始化Redis连接 $redis = new Redi

  • redis incr和incrBy的使用说明

    最近用incr和incrBy在接口里做了下埋点统计每天请求总数,这两个命令还是挺好用的,先说下这俩命令吧 注:redis后台服务是串行的单线程执行,不存在并发,即多线程调用Incr/incrby方法,在redis服务器上仍然是串行的单线程执行,不存在并发,所以这俩命令都是原子自增.线程安全的. 1.Incr 命令 (菜鸟教程) Redis Incr 命令将 key 中储存的数字值增一. 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作. 如果值包含错误的类

  • python3使用python-redis-lock解决并发计算问题

    目录 需求 python-redis-lock 修改业务代码,增加lock操作 需求 我在最近的一个任务中,存在一个redis高并发计算多个客户端接收预警信息的时长问题. 模型是首先模拟多个客户端连接预警服务器集群,然后向预警服务集群发送告警信息.随后预警服务集群将会向客户端推送告警信息. 此时,我记录了发送告警至预警集群的时间,并且在客户端还会记录接收到告警的时间. 我将这个时间都会记录到redis中,那么此时就会有一个问题,当多个客户端抢占式往redis 读取数据,计算,设置数据,这个过程是

  • 浅谈Redis如何应对并发访问

    目录 前言 原子性操作 单命令模式 多命令模式 lua简介 建议 事务 加锁 总结 前言 项目中经常会遇到这种场景,我们需要先将Redis数据读取到本地,然后进行修改,修改完成后在将数据写回Redis,这种读取-修改-写回操作,我们称之为RMW操作.当有多个客户端对同一份数据执行RMW操作的话,Redis如何保证RMW操作涉及的代码以原子性方式执行? 原子性操作 Redis的原子性操作是一种无锁操作,即可以保证并发控制,还能减少系统对并发性能的影响, 单命令模式 把Redis多个操作实现成一个操

  • php 使用redis锁限制并发访问类示例

    本文介绍了php 使用redis锁限制并发访问类,并详细的介绍了并发访问限制方法. 1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券. 伪代码如下: if A(可以换领)     B(执行换领)     C(更新为已换领) D(结束) 如果用户并发提交换领码,都能通过可以换领(A)的判断,因

随机推荐