基于 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;
    }
}
相关推荐
小马爱打代码1 小时前
分布式锁:原理算法和使用建议
分布式·算法
野犬寒鸦1 小时前
从零起步学习Redis || 第十一章:主从切换时的哨兵机制如何实现及项目实战
java·服务器·数据库·redis·后端·缓存
problc2 小时前
PostgreSQL + Redis + Elasticsearch 实时同步方案实践:从触发器到高性能搜索
redis·elasticsearch·postgresql
一叶飘零_sweeeet3 小时前
从 “黑盒“ 到 “透明“:SkyWalking 实战指南 —— 让微服务问题无所遁形
分布式·微服务·skywalking·分布式链路追踪
yinke小琪3 小时前
从秒杀系统崩溃到支撑千万流量:我的Redis分布式锁踩坑实录
java·redis·后端
会跑的葫芦怪4 小时前
Go语言操作Redis
开发语言·redis·golang
ArabySide4 小时前
【ASP.NET Core】分布式场景下ASP.NET Core中JWT应用教程
分布式·后端·asp.net core
阿湯哥6 小时前
Redis数据库隔离业务缓存对查询性能的影响分析
数据库·redis·缓存
麦兜*6 小时前
Redis 7.2 新特性实战:Client-Side Caching(客户端缓存)如何大幅降低延迟?
数据库·spring boot·redis·spring·spring cloud·缓存·tomcat
小马爱打代码7 小时前
zookeeper:一致性原理和算法
分布式·zookeeper·云原生