Redis之Lua脚本

Redis之Lua脚本—

Redission不仅提供了一套丰富的Redis客户端功能,还增加了很多高级功能,其中就包括分布式锁、发布和订阅、支持Lua脚本。Redission在底层利用Redis的SETNX命令实现分布式锁,并且处理了锁的续期问题,使用起来非常方便。

然而,为什么在实际项目中,我们还需要使用Lua脚本呢?
这主要是因为Redisson的分布式锁机制在某些特定场景下可能无法满足需求。

1,复杂的原子操作:Redisson的分布式锁主要用于实现简单的原子操作,例如“获取锁,执行操作,释放锁”。但是,如果你需要执行更复杂的原子操作,例如“获取锁,执行操作1,执行操作2,释放锁”,这时就需要使用Lua脚本。
.
2,自定义的锁机制:在某些场景下,你可能需要自定义锁的机制,例如锁的公平性、锁的>时时间等。虽然Redisson提供了一些配置选项,但是如果你需要更细粒度的控制,那么使用Lua脚本可能是更好的选择。
.
3,跨多个Redis实例的操作:如果你需要跨多个Redis实例进行操作,例如在一个Redis实例上获取锁,在另一个Redis实例上执行操作,那么使用Lua脚本可能更方便,能够避免跨多个Redis实例操作时可能出现的竞态条件或数据不一致问题
.
4,避免网络开销:使用Lua脚本可以将多个操作合并为一个操作,从而减少网络开销。这在某些对性能要求较高的场景下非常有用。

Lua脚本介绍

Redis从2.6版本开始,通过内嵌一个Lua解释器,支持在服务器端执行Lua脚本。这个特性为Redis提供了非常大的灵活性。通过Lua脚本,我们可以实现复杂的逻辑,包括判断、循环等这在纯Redis命令中是很难实现的
.

Lua是一种轻量级的解释型脚本语言,直接在运行时执行Lua脚本,而无需预先编译。 Lua脚本的主要优点包括:
简单:Lua的语法简洁直观,易于学习。
高效:尽管Lua是解释型语言,但它的执行速度非常快,因为编译成字节码。
安全:Lua提供了沙盒环境,允许在隔离的环境中运行代码,从而提高安全性。
灵活:Lua可以很容易地与其他语言(如C、C++、Java等)集成,使得你可以在其他应用程序中嵌入Lua脚本,扩展应用程序的功能。

Lua脚本为啥是高性能的?

轻量级:Lua是一种轻量级脚本语言,解释器体积小、启动速度快,消耗的内存资源较少,使得Lua脚本在运行时具有较低的开销
.
动态类型:Lua是一种动态类型语言,它允许变量在运行时改变类型。这种灵活性避免了静态类型语言在类型转换和类型检查方面的开销,使得Lua脚本在执行时更加高效。
.
即时编译:Lua使用了一种混合的编译和解释策略,它将脚本代码编译成字节码,然后在运行时解释执行。这种即时编译的方式可以在一定程度上提高执行效率,因为编译后的字节码更加优化和紧凑。
.
垃圾回收:Lua内置了一个高效的垃圾回收机制,可以自动管理内存。这减少了开发人员手动管理内存的负担,避免了内存泄漏和内存碎片问题,使得Lua脚本在长时间运行时仍能保持稳定的性能。
.
协程支持:Lua内置了对协程(coroutine)的支持,这是一种轻量级的线程,可以在单个线程中并发执行多个任务。协程的使用可以减少线程切换和同步的开销,提高脚本的并发性能

接下来,自定义自旋锁、通过Lua脚本实现分布式锁操作


public class RedisLockHelper {

    private Logger logger = LoggerFactory.getLogger(RedisLockHelper.class);

    /**
     * 加锁超时时间:500毫秒
     */
    private static final long TIME_OUT = 500L;

    //定义获取锁的lua脚本
    private final static DefaultRedisScript<Long> LOCK_LUA_SCRIPT = new DefaultRedisScript<>(
            "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end"
            , Long.class
    );

    //定义释放锁的lua脚本
    private final static DefaultRedisScript<Long> RELEASE_LOCK_LUA_SCRIPT = new DefaultRedisScript<>(
            "if redis.call('GET',KEYS[1]) == ARGV[1] then return redis.call('DEL',KEYS[1]) else return -1 end"
            , Long.class
    );

    @Lazy
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    /**
     * 自旋锁
     * @param lockKey
     * @param lockValues
     * @param expire
     * @return
     */
    private Boolean spinLock(String lockKey, String lockValues, int expire){
        long startTime = System.currentTimeMillis();

        while (true){
            boolean lockRes = tryLock(lockKey, lockValues, expire);
            if(lockRes){
                return Boolean.TRUE;
            }
            //加锁消耗时长
            long consumeTime = System.currentTimeMillis() - startTime;
            if(consumeTime >= TIME_OUT){
                logger.warn("获取锁超时:锁key值:{}", lockKey);
                return Boolean.FALSE;
            }

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public boolean tryLock(String lockKey, String value, int expire) {
        // 参数一:redisScript,参数二:key列表
        Long result = redisTemplate.execute(LOCK_LUA_SCRIPT, Collections.singletonList(lockKey), value, String.valueOf(expire));
        if(result != null && result == 1L){
            if(logger.isDebugEnabled()){
                logger.info("[加锁成功]result:{}, key:[{}], value:[{}] ", result, lockKey, value);
            }
            return true;
        }else {
            logger.warn("[加锁失败]result:{}, key:[{}], value:[{}] ", result, lockKey, value);
            return false;
        }
    }

    public void releaseLock(String lockKey, String value) {
        Long result = redisTemplate.execute(RELEASE_LOCK_LUA_SCRIPT, Collections.singletonList(lockKey), value);
        if(result != null && result == 1L){
            if(logger.isDebugEnabled()){
                logger.info("[锁释放环节]:锁释放成功!result:{}, key:[{}], value:[{}] ", result, lockKey, value);
            }
        }else {
            logger.warn("[锁释放环节]:查询不到锁!result:{}, key:[{}], value:[{}] ", result, lockKey, value);
        }
    }

    public static String getLockValues() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}

你可能感兴趣的:(redis,lua,reddision)