场景:分布式锁是使用非常频繁的,批量跑逻辑比如导入、多选提交业务,也会用到,但是由于单个锁频繁网络请求非常耗时,于是就有了批量锁;
批量上锁要原子性用了lua脚本实现,批量解锁用的pipeline,废话不说上代码
public class RedisBatchLock {
static final Integer MAX_SIZE = 100;
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();
}
public static Boolean batchLock(List keys, Long timeOut) {
if (CollectionUtil.isEmpty(keys) ||
timeOut < 1000L ||
keys.size() > MAX_SIZE) {
return false;
}
List values = new ArrayList();
List 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 redisMasterTemplate = RedisHelper.getRedisTemplate();
RedisScript redisScript = new DefaultRedisScript(getScriptLua(), Boolean.class);
Boolean flag = redisMasterTemplate.execute(redisScript, finalKeys, values.toArray(new String[0]));
return flag;
}
public static boolean batchUnLock(List keys) {
if (CollectionUtil.isEmpty(keys)) {
return false;
}
RedisTemplate redisTemplate = RedisHelper.getRedisTemplate();
List
Redis与spring集成就不多说了,主要逻辑在此,个别封装代码可以忽略,稍微动一下就可以运行啦;
经测试,批量处理10把锁性能提示4倍。
lua脚本和java调用注意返回值类型要一致(这里是Boolean),参数集合类型要一致(这里是String),注意这些坑即可。
另外,这里未支持重入锁的场景,批量处理一般也不会有这种场景;解锁处理要小心,没有做约束本线程可解锁,这需要改进。



