Redis系列之使用Lua脚本

2024-01-14 10:12:39 浏览数 (2)

什么是lua脚本?

lua语言是一个轻量级的脚本语言,可以嵌入其他语言中使用,调用宿主语言的功能。lua语法简单,小巧,源码一共才200多K,本身不会有太强的功能,很多的语言也支持lua语言,比如redis、Nginx

redis语言中完美嵌入了lua脚本功能,redis可以调用lua脚本中的api,lua脚本也可以调用redis中的命令

redis调用lua脚本

在redis中调用lua脚本,需要使用eval指令

代码语言:javascript复制
127.0.0.1:6379>eval "return 'hello'" 0
"hello"

调用lua脚本,动态传入参数,其中表达式script后面第一个参数nkey表示key的对应位置,后面的表示key和对应的参数argv

代码语言:javascript复制
# script脚本后面,第一个参数1表示key为其后的第一个参数,也就是1,如何key后面的参数都是ARGV
127.0.0.1:6379>eval "if KEYS[1]=='1' then return ARGV[1] end return ARGV[2]" 1 1 'hello' 'hi'
"hello"
127.0.0.1:6379>eval "if KEYS[1]=='1' then return ARGV[1] end return ARGV[2]" 1 1 'hello' 'hi'
"hi"

lua脚本调用redis命令

使用lua调用redis的命令,需要使用redis.call调用

代码语言:javascript复制
# key为0表示能获取到锁
127.0.0.1:6379>eval "local key = redis.call('exists',KEYS[1]) if key==0 then return redis.call('set',KEYS[1],ARGV[1]) end return 1" 1 orderId01 1

写个lua脚本,来实现一个简单的分布锁锁

代码语言:javascript复制
private static final String LOCK_LUA_SCRIPT = "local lockParam = redis.call('exists', KEYS[1])n"  
            "if lockParam == 0 thenn"  
            "redis.call('set', KEYS[1], ARGV[1])n"  
            "redis.call('expire', KEYS[1], ARGV[2])n"  
            "endn"  
            "return lockParamn";

简单实现抢单的业务

代码语言:javascript复制
  @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testLua() {
        Long orderId = IdUtil.getSnowflake().nextId();
        String lockKey = "order:" orderId;
        String requestId = IdUtil.randomUUID();
        try {
            Long lock = (Long) redisTemplate.execute(RedisScript.of(LOCK_LUA_SCRIPT, Long.class), Arrays.asList(lockKey), requestId, 30);
            // 抢得到锁
            if (lock == 0) {
                // 模拟业务执行10s
                TimeUnit.MILLISECONDS.sleep(10*1000);
            }
            log.info("lock:[{}]", lock);
        } catch (Exception e) {
            testRelease(lockKey, requestId);
        } finally {
            testRelease(lockKey, requestId);
        }
    }

锁释放的,也通过lua脚本实现,主要是保证原子性

代码语言:javascript复制
 private String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

@Test
public void testRelease(String lockKey, String lockValue) {
    redisTemplate.execute(RedisScript.of(UNLOCK_LUA_SCRIPT, Long.class), Arrays.asList(lockKey), lockValue);
}

Lua脚本使用场景

  • 保证原子性地执行多个命令
  • 需要返回中间值组合编排后面的命令

0 人点赞