redis实现分布式锁

redis实现分布式锁

java 复制代码
@Component
public class RedisDistributedLock {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_EXPIRE = 30_000; // 默认30秒

    // 加锁(带UUID防误删)
    public String tryLock(String lockKey, long expire) {
        String requestId = UUID.randomUUID().toString();
        boolean result = redisTemplate.opsForValue().setIfAbsent(LOCK_PREFIX + lockKey, requestId, expire, TimeUnit.MILLISECONDS);
        return result ? requestId : null;
    }

    // 解锁(验证锁持有者身份后删除)
    public boolean unlock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        String currentRequestId = redisTemplate.opsForValue().get(key);
        if (requestId.equals(currentRequestId)) {
            return redisTemplate.delete(key);
        }
        return false;
    }
}

lua脚本实现方式

java 复制代码
public class RedisDistributedLock {
    
    private RedisTemplate<String, Object> redisTemplate;
    private String lockKey;
    private String lockValue;
    private int expireTime;
    
    // 获取锁的Lua脚本
    private static final String ACQUIRE_LOCK_SCRIPT = 
        "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
        "redis.call('expire', KEYS[1], tonumber(ARGV[2])) " +
        "return 1 " +
        "else " +
        "return 0 " +
        "end";
        
    // 释放锁的Lua脚本
    private static final String RELEASE_LOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "return redis.call('del', KEYS[1]) " +
        "else " +
        "return 0 " +
        "end";
    
    public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, 
                               String lockKey, int expireTime) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey;
        this.expireTime = expireTime;
        this.lockValue = UUID.randomUUID().toString();
    }
    
    /**
     * 尝试获取分布式锁
     * @return 是否获取成功
     */
    public boolean tryLock() {
        RedisScript<Long> script = new DefaultRedisScript<>(ACQUIRE_LOCK_SCRIPT, Long.class);
        Long result = redisTemplate.execute(script, 
                                          Collections.singletonList(lockKey), 
                                          lockValue, 
                                          String.valueOf(expireTime));
        return result != null && result == 1;
    }
    
    /**
     * 释放分布式锁
     * @return 是否释放成功
     */
    public boolean releaseLock() {
        RedisScript<Long> script = new DefaultRedisScript<>(RELEASE_LOCK_SCRIPT, Long.class);
        Long result = redisTemplate.execute(script, 
                                          Collections.singletonList(lockKey), 
                                          lockValue);
        return result != null && result == 1;
    }
    
    /**
     * 带超时的获取锁方法
     * @param timeout 超时时间(毫秒)
     * @return 是否获取成功
     */
    public boolean tryLock(long timeout) {
        long startTime = System.currentTimeMillis();
        do {
            if (tryLock()) {
                return true;
            }
            try {
                Thread.sleep(100); // 短暂休眠避免过度竞争
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        } while (System.currentTimeMillis() - startTime < timeout);
        
        return false;
    }
}

代码说明:

  1. 使用RedisTemplate的setIfAbsent方法实现原子加锁操作
  2. 通过UUID生成唯一请求ID,防止误删其他客户端的锁
  3. 解锁时通过Lua脚本验证锁持有者身份,确保原子性
  4. 支持自定义过期时间,防止死锁
  5. 依赖Spring框架,需配置RedisTemplate Bean
相关推荐
莫得感情 o6 小时前
Redis 05 · 持久化:RDB 与 AOF 怎么保证数据不丢
redis·缓存
curd_boy8 小时前
【Redis】Redis从缓存到AI向量平台
人工智能·redis·缓存
莫得感情 o8 小时前
Redis 02 · 数据结构:String 到 Stream 怎么选
redis
爱浦路 IPLOOK10 小时前
矿山无人化作业5G专网方案:企业专网核心网络选型分析
网络·分布式·科技·5g·信息与通信
devpotato10 小时前
缓存与数据库更新顺序不一致问题
java·数据库·redis
实战派K8S&DB11 小时前
如何在内网配置 TiDB 数据库 Agent
数据库·人工智能·分布式·tidb
实战派K8S&DB11 小时前
《基于 Dify + FastAPI + PyTiDB 搭建大模型驱动的 TiDB 智能运维 Agent》
运维·数据库·分布式·云原生·tidb·fastapi
小白羊丨12 小时前
Kafka任务重试、退避与失败恢复怎么做?
分布式·kafka
NeverFear79215 小时前
【黑马点评】短信登录
java·redis·tomcat·intellij-idea
攻城有术16 小时前
专项攻克——重写 Redis 依赖包方法的 6 种实现方式
java·数据库·redis·bootstrap