基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性
1.加锁的Lua脚本: lock.lua
--- -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
if redis.call('get', KEYS[1]) == ARGV[1]
then
return redis.call('del', KEYS[1])
else
return 0
end
3.将资源文件放在资源文件夹下
4.Java中调用lua脚本
1)获取文件方式
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字符串方式
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依赖
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2)jedis调用lua
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;
}
}