基于 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;
    }
}
相关推荐
啦啦啦_99997 分钟前
Redis-5-doFormatAsync()方法
数据库·redis·c#
生产队队长16 分钟前
Redis:Windows环境安装Redis,并将 Redis 进程注册为服务
数据库·redis·缓存
袁煦丞 cpolar内网穿透实验室1 小时前
远程调试内网 Kafka 不再求运维!cpolar 内网穿透实验室第 791 个成功挑战
运维·分布式·kafka·远程工作·内网穿透·cpolar
人间打气筒(Ada)1 小时前
GlusterFS实现KVM高可用及热迁移
分布式·虚拟化·kvm·高可用·glusterfs·热迁移
xu_yule1 小时前
Redis存储(15)Redis的应用_分布式锁_Lua脚本/Redlock算法
数据库·redis·分布式
清风拂山岗 明月照大江4 小时前
Redis笔记汇总
java·redis·缓存
消失的旧时光-19435 小时前
第十四课:Redis 在后端到底扮演什么角色?——缓存模型全景图
java·redis·缓存
難釋懷6 小时前
分布式锁的原子性问题
分布式
消失的旧时光-19436 小时前
第十四课 · 实战篇:Redis 缓存系统落地指南(Spring Boot 从 0 到可用)
spring boot·redis·缓存