前言
在高并发下,由多渠道发起的同一业务请求,如果后台保护不周,会出现同时生成多笔相同的数据,比如相同订单,相同业务请求,在生产系统中造成垃圾数据,甚至出现生产事故,在前端增加控制已不足已解决的情况下,通过redis加锁,实现同一笔请求当有效期内只有一笔数据,下面详细介绍代码实现。
一、关键方法
加锁-setnx、设置有效期-expire、获取值-get、解锁-del
二、代码实现
1.加锁
/**
* @Title: getRedisLock
* @Description: TODO 1、获取锁之前的超时时间---在尝试获取锁的时候,如果在规定的时间内还没有获取锁,直接放弃;2、获取锁之后的超时时间---当获取锁成功之后,对应key有有效期,在规定时间内进行失效
* @Description: TODO 基于redis实现分布式锁代码思路:获取锁、释放锁
* @param acquireTimeout 在获取锁之前的超时时间
* @param timeOut 在获取锁之后的超时时间
* @param ywqqid redis key对应的value值(ywqqid唯一)作用:加锁和释放锁
* @return: ywqqid
*/
public String getRedisLock(Long acquireTimeout, Long timeOut, String ywqqid) {
JedisPool jedisPool = null;
Jedis conn = null;
try {
//获取redis线程池
jedisPool = RedisConnPool.getPool();
//建立redis连接
conn = jedisPool.getResource();
//定义在获取锁之后的超时时间,以秒为单位
int expireLock = (int)(timeOut / 1000);
//定义在获取锁之前的超时时间,使用循环机制,如果没有获取到锁,要在规定acquireTimeout时间,保证重复进行尝试获取锁
Long endTime = System.currentTimeMillis() + acquireTimeout;
while (System.currentTimeMillis() < endTime) {
//插入对应的redislockKey,如果返回为1则成功获取锁,如果有效期内再进行setnx,则加锁失败,返回0
if(conn.setnx(ywqqid, ywqqid) == 1) {
//设置对应key的有效期,防止redis挂掉造成死锁
conn.expire(ywqqid, expireLock);
//返回ywqqid代表,对唯一的一笔业务加锁成功,其他相同的请求不能再去执行
return ywqqid;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally{
//释放redis资源
jedisPool.returnResource(conn);
conn = null;
}
//返回为null说明没有获取到锁,禁止重复操作
return null;
}
2.解锁
/**
* @Title: unRedisLock
* @Description: TODO 释放redis锁,唯一 ywqqid 为 redislockKey
* @param redislockKey
* @param redislockValue
* @return
* @return: boolean
*/
public boolean unRedisLock(String redislockKey, String redislockValue) {
JedisPool jedisPool = null;
Jedis conn = null;
try {
//获取redis线程池
jedisPool = RedisConnPool.getPool();
//建立redis连接
conn = jedisPool.getResource();
//判断获取锁的时候保证自己删除自己,del 返回0,1(释放成功)
if(redislockValue.equals(conn.get(redislockKey))) {
Long delKey = conn.del(redislockKey);
if(delKey > 0) {
return true;
}else {
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//释放redis资源
jedisPool.returnResource(conn);
conn = null;
}
return false;
}
注意:在高版本中 redis 关闭连接用 close() 方法。
总结
- 在服务端,将唯一业务流水号,作为 key 和 value,加锁,后面如果有相同的流水号,则直接return掉,业务执行完成后,记得释放锁,释放锁可以通过del key 或者给锁设置有效期,有效期根据实际业务执行情况定夺。
- 设置有效期expire,过期后,锁自动失效
- 释放锁,del(key),释放锁之后,立即失效,跟有效期无关了