场景:分布式锁是使用非常频繁的,批量跑逻辑比如导入、多选提交业务,也会用到,但是由于单个锁频繁网络请求非常耗时,于是就有了批量锁;
批量上锁要原子性用了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),注意这些坑即可。
另外,这里未支持重入锁的场景,批量处理一般也不会有这种场景;解锁处理要小心,没有做约束本线程可解锁,这需要改进。
批量分布式锁的实现与性能提升





