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

以上是 使用Redis incr解决并发问题的操作 的全部内容, 来源链接: utcz.com/z/343978.html

回到顶部