场景:分布式锁是使用非常频繁的,批量跑逻辑比如导入、多选提交业务,也会用到,但是由于单个锁频繁网络请求非常耗时,于是就有了批量锁;
批量上锁要原子性用了lua脚本实现,批量解锁用的pipeline,废话不说上代码
public class RedisBatchLock { static final Integer MAX_SIZE = 100; /** * 获取lua脚本 * * @return */ static final String getScriptLua() { StringBuffer str = new StringBuffer(); str.append(" local seccess = {}; "); str.append(" for k, v in pairs(KEYS) do "); str.append(" local value = ARGV[k] ; "); str.append(" local ok = redis.call('setnx', v, value); "); str.append(" if ok == 1 then "); str.append(" redis.call('expire', v, ARGV[#ARGV]); "); str.append(" seccess[k]=v; "); str.append(" else "); str.append(" if #seccess>0 then "); str.append(" for x, y in pairs(seccess) do "); str.append(" redis.call('del', y); "); str.append(" end "); str.append(" end "); str.append(" return false; "); str.append(" end "); str.append(" end "); str.append(" return true; "); return str.toString(); } /** * 批量上锁 * * @param keys * @param timeOut 单位毫秒 * @return */ public static Boolean batchLock(List<String> keys, Long timeOut) { if (CollectionUtil.isEmpty(keys) || timeOut < 1000L || keys.size() > MAX_SIZE) { return false; } List values = new ArrayList(); List<String> finalKeys = new ArrayList(); for (String key : keys) { LockInfo lockInfo = new LockInfo(key, Thread.currentThread().getName(), Thread.currentThread().getId()); lockInfo.setStatus(1); finalKeys.add(RedisLockKeyGenerator.generateRedisLockKey(key)); values.add(JSON.toJSONString(lockInfo)); } values.add(timeOut / 1000 + ""); RedisTemplate<String, String> redisMasterTemplate = RedisHelper.getRedisTemplate(); RedisScript<Boolean> redisScript = new DefaultRedisScript(getScriptLua(), Boolean.class); Boolean flag = redisMasterTemplate.execute(redisScript, finalKeys, values.toArray(new String[0])); return flag; } /** * 批量解锁 * * @param keys * @return */ public static boolean batchUnLock(List<String> keys) { if (CollectionUtil.isEmpty(keys)) { return false; } RedisTemplate<String, String> redisTemplate = RedisHelper.getRedisTemplate(); List<Object> list = redisTemplate.executePipelined(new SessionCallback<Boolean>() { @Override public Boolean execute(RedisOperations operations) throws DataAccessException { for (String key : keys) { key = RedisLockKeyGenerator.generateRedisLockKey(key); operations.delete(key); } return null; } }); return true; } }
Redis与spring集成就不多说了,主要逻辑在此,个别封装代码可以忽略,稍微动一下就可以运行啦;
经测试,批量处理10把锁性能提示4倍。
lua脚本和java调用注意返回值类型要一致(这里是Boolean),参数集合类型要一致(这里是String),注意这些坑即可。
另外,这里未支持重入锁的场景,批量处理一般也不会有这种场景;解锁处理要小心,没有做约束本线程可解锁,这需要改进。