批量分布式锁 Redis 批量命令执行 lua&pipeline

批量分布式锁的实现与性能提升

场景:分布式锁是使用非常频繁的,批量跑逻辑比如导入、多选提交业务,也会用到,但是由于单个锁频繁网络请求非常耗时,于是就有了批量锁;

批量上锁要原子性用了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),注意这些坑即可。

另外,这里未支持重入锁的场景,批量处理一般也不会有这种场景;解锁处理要小心,没有做约束本线程可解锁,这需要改进。

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值