> 文档中心 > Redis实现分布式锁——通过setExNx(互斥锁)实现

Redis实现分布式锁——通过setExNx(互斥锁)实现

前置要求:
需要实现SpringBoot+redis的基础配置《SpringBoot整合Redis》

通过lua脚本+redis可以实现分布式锁;

加锁原子性:通过redis自身的setnxex命令即可,setIfAbsent(“lockKey”, value, timeOut, TimeUnit);

解锁原子性:通过redis+lua脚本实现;

定义Lua脚本:
resources目录下创建lua目录,并且创建redisLock.lua文件;
在这里插入图片描述

if redis.call('get',KEYS[1]) == ARGV[1] then     redis.call('del', KEYS[1])     return '1';else    return '0'end

Lua脚本配置:

import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.scripting.support.ResourceScriptSource;import javax.annotation.Resource;import java.util.Collections;import java.util.List;@Configurationpublic class RedisLuaConfig {    @Resource    private StringRedisTemplate stringRedisTemplate;    /**     * @return result 返回 1表示,正常,0表示限制访问     */    public boolean runLuaScript(String lockKey, String value) { List<String> keyList = Collections.singletonList(lockKey); // 执行脚本 删除锁 DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/redisLock.lua"))); redisScript.setResultType(String.class); String execute = stringRedisTemplate.execute(redisScript, keyList, value); return "1".equals(execute);    }}

Lock锁配置:

@Componentpublic class RedisLockServer {    @Resource    private StringRedisTemplate stringRedisTemplate;    @Resource    private RedisLuaConfig redisLuaConfig;    public boolean setLock(String lockKey, String value, long time, TimeUnit timeUnit) { return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value, time, timeUnit);    }    public void deleteLock(String lockKey, String value) { boolean script = redisLuaConfig.runLuaScript(lockKey, value);    }}

逻辑代码实现:
为了验收方便,在controller层进行业务的编写,测试时,通过浏览器频繁刷新进行请求,模拟锁机制的触发;

@RestController@RequestMapping("/redis")public class Test {    @Resource    private RedisLockServer redisLockServer;    @RequestMapping(value = "/lock")    public String reduceSku() throws Exception { String value = UUID.randomUUID().toString(); // value值任意即可,设置自动过期时间为10S boolean lock = redisLockServer.setLock("key", value, 10, TimeUnit.SECONDS); if (lock) {     // 当前key没有锁,加锁成功 执行数据库查询     System.out.println("===>>>加锁成功");     // 模拟耗时8S     TimeUnit.SECONDS.sleep(8);     // 所以要保证验证value值和验证成功必须满足原子性,通过redis+lua实现     redisLockServer.deleteLock("key", value); } else {     // 当前key已经存在锁,加锁失败     System.out.println("===>>>加锁失败,等待重试..."); } return "操作成功";    }}

效果:
在这里插入图片描述