基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性

1.加锁的Lua脚本: lock.lua
java 复制代码
--- -1 failed
--- 1 success

--- getLock key
local result = redis.call('setnx' , KEYS[1] , ARGV[1])
if result == 1 then
    --PEXPIRE:以毫秒的形式指定过期时间
    redis.call('pexpire' , KEYS[1] , 3600000)
else
    result = -1;
    -- 如果value相同,则认为是同一个线程的请求,则认为重入锁
    local value = redis.call('get' , KEYS[1])
    if (value == ARGV[1]) then
        result = 1;
        redis.call('pexpire' , KEYS[1] , 3600000)
    end
end
--  如果获取锁成功,则返回 1
return result
2.解锁的Lua脚本: unLock.lua
java 复制代码
if redis.call('get', KEYS[1]) == ARGV[1]
    then
        return redis.call('del', KEYS[1])
    else
        return 0
end
3.将资源文件放在资源文件夹下
4.Java中调用lua脚本

1)获取文件方式

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 14:04 2023/10/26
 * @Modified By:
 */
@Slf4j
@RestController
public class LuaLock {


    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping(value = "/getLock")
    public Long getLock() {
        DefaultRedisScript<Long> script = new DefaultRedisScript<Long>();
        script.setResultType(Long.class);
//        获取lua文件方式
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/lock.lua")));
        
        Long execute = (Long) redisTemplate.execute(script, Arrays.asList("best_liu"),"best_liu20231026150600");
        return execute;
    }
}

2)lua字符串方式

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 14:04 2023/10/26
 * @Modified By:
 */
@Slf4j
@RestController
public class LuaLock {


    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping(value = "/getLock")
    public Long getLock() {
        DefaultRedisScript<Long> script = new DefaultRedisScript<Long>();
        script.setResultType(Long.class);
//        获取lua文件方式
//        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/lock.lua")));
        String lua = "local result = redis.call('setnx' , KEYS[1] , ARGV[1])\n" +
                "if result == 1 then\n" +
                "    redis.call('pexpire' , KEYS[1] , 3600000)\n" +
                "else\n" +
                "    result = -1;\n" +
                "    local value = redis.call('get' , KEYS[1])\n" +
                "    if (value == ARGV[1]) then\n" +
                "        result = 1;\n" +
                "        redis.call('pexpire' , KEYS[1] , 3600000)\n" +
                "    end\n" +
                "end\n" +
                "return result";
        script.setScriptText(lua);
        Long execute = (Long) redisTemplate.execute(script, Arrays.asList("best_liu"),"best_liu20231026150600");
        return execute;
    }
}
5.jedis调用Lua脚本实现分布式重试锁

1)引入jedis依赖

java 复制代码
<!-- jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

2)jedis调用lua

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
@RestController
public class LuaLock {


    @GetMapping(value = "/getLock")
    public Long getLock() {
        //获取连接
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        String lua = "local result = redis.call('setnx' , KEYS[1] , ARGV[1])\n" +
                "if result == 1 then\n" +
                "    redis.call('pexpire' , KEYS[1] , ARGV[2])\n" +
                "else\n" +
                "    result = -1;\n" +
                "    local value = redis.call('get' , KEYS[1])\n" +
                "    if (value == ARGV[1]) then\n" +
                "        result = 1;\n" +
                "        redis.call('pexpire' , KEYS[1] , ARGV[2])\n" +
                "    end\n" +
                "end\n" +
                "return result";
        List<String> keys = new ArrayList<>();
        List<String> values = new ArrayList<>();
        keys.add("best_liu");
        values.add("best_liu20231026150600");
        values.add("3600000");
        Object result = jedis.eval(lua, keys, values);
        System.out.println(result);
        return 1L;
    }
}
相关推荐
菜鸡儿齐32 分钟前
kafka简介
分布式·kafka
周杰伦_Jay1 小时前
【实战|旅游知识问答RAG系统全链路解析】从配置到落地(附真实日志数据)
大数据·人工智能·分布式·机器学习·架构·旅游·1024程序员节
兜兜风d'4 小时前
RabbitMQ事务机制详解
数据库·spring boot·分布式·rabbitmq·ruby·java-rabbitmq
ifeng09184 小时前
HarmonyOS分布式任务调度——跨设备智能任务分配与迁移
分布式·华为·harmonyos
9ilk4 小时前
【仿RabbitMQ的发布订阅式消息队列】--- 模块设计与划分
c++·笔记·分布式·后端·中间件·rabbitmq
千里镜宵烛5 小时前
Lua-function的常见表现形式
开发语言·junit·lua
初见无风5 小时前
2.4 Lua代码中table常用API
开发语言·lua·lua5.4
初见无风5 小时前
2.6 Lua代码中function的常见用法
开发语言·lua·lua5.4
一周困⁸天.5 小时前
Redis Sentinel哨兵集群
redis·bootstrap·sentinel
Hello World......6 小时前
互联网大厂Java面试实战:以Spring Boot与微服务为核心的技术场景剖析
java·spring boot·redis·微服务·junit·kafka·spring security