在高并发下,由多渠道发起的同一业务请求,如果后台保护不周,会出现同时生成多笔相同的数据,比如相同订单,相同业务请求,在生产系统中造成垃圾数据,甚至出现生产事故,在前端增加控制已不足已解决的情况下,通过redis加锁,实现同一笔请求当有效期内只有一笔数据,下面详细介绍代码实现。
加锁-setnx、设置有效期-expire、获取值-get、解锁-del
/** * @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; }
/** * @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() 方法。