基于 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;
    }
}
相关推荐
佛祖让我来巡山36 分钟前
线上 Redis 突然“爆”了,怎么办?
redis·redis宕机·redis崩了·redis线上事故
z落落1 小时前
C# 事件(Event)+自定义带参数事件例子
开发语言·分布式·c#
三十..2 小时前
Redis 核心原理与高可用架构实践
运维·数据库·redis
我是一颗柠檬3 小时前
【Java项目技术亮点】分库分表+数据路由策略:单表5000万后的架构升级方案
java·开发语言·分布式·架构
叶小鸡3 小时前
Java 篇-项目实战-AI 天机学堂(从 0 到 1)-day5
数据库·redis·缓存
IT策士3 小时前
Redis 从入门到精通:Python 操作 Redis
redis·python·bootstrap
半夜修仙4 小时前
RabbitMQ中如何保证消息的可靠性传输
java·分布式·中间件·rabbitmq·github·java-rabbitmq
周杰伦的稻香4 小时前
Go + Redis:本地部署高性能图片主色调提取服务
开发语言·redis·golang
小二·6 小时前
Redis 7 分布式缓存架构实战
redis·分布式·缓存
zhuhai_xigedian6 小时前
源网荷储一体化 vs 传统供用电模式:差异、优势与转型路径
大数据·人工智能·分布式·系统架构·能源