首页 > 数据库 >Redis分布式锁三种实现方式详解

Redis分布式锁三种实现方式详解

来源:互联网 2026-07-24 09:07:57

当多个节点同时操作共享资源时,如何保证数据一致性和业务正确性?这几乎是每个分布式系统都会遇到的经典难题。而分布式锁,正是解决这一难题的常见手段。今天,我们就来深入聊聊基于Redis实现分布式锁的三种主流方案,并对比它们的优劣。 什么是分布式锁? 简单来说,分布式锁是一种机制,确保在分布式系统中,同一

当多个节点同时操作共享资源时,如何保证数据一致性和业务正确性?这几乎是每个分布式系统都会遇到的经典难题。而分布式锁,正是解决这一难题的常见手段。今天,我们就来深入聊聊基于Redis实现分布式锁的三种主流方案,并对比它们的优劣。

Redis分布式锁三种实现方式详解

长期稳定更新的攒劲资源: >>>点此立即查看<<<

什么是分布式锁?

简单来说,分布式锁是一种机制,确保在分布式系统中,同一时间只有一台机器、一个节点能访问某个共享资源。这和我们日常用的单机锁(比如Java里的synchronized)不一样,它跨越了进程边界,需要借助一个外部协调者(比如Redis、ZooKeeper或Consul)来管理。

分布式锁的应用场景很广泛,主要包括:

  • 限流控制:防止多个请求同时修改同一个资源,造成混乱。
  • 全局任务调度:在多个节点中,确保只有一台机器执行某个特定任务。
  • 防止重复处理:避免同一笔订单被两个服务同时处理两次。
  • 分布式唯一性保证:比如生成全局唯一ID。

1. 基于 Redis SETNX 实现分布式锁

Redis 是分布式锁实现中最常用的工具之一。利用其SETNX(SET if Not eXists)命令,我们能快速实现一个基本的分布式锁。如果你对这个名字不熟悉,可以理解为:它只会在键不存在的时候才去设置值。这样一来,就能保证同一时间只有一个节点能“抢”到锁。

实现步骤:

  1. 通过SETNX命令尝试写入一个特定的键值对(比如lock:resource)。
  2. 如果写入成功(返回1),说明当前节点获得了锁,可以执行后续业务。
  3. 如果写入失败(返回0),说明锁已被其他节点持有,当前节点需要等待或重试。
  4. 获取锁后,需要立即设置一个过期时间,防止因节点宕机导致的死锁。

代码示例(Java)

import redis.clients.jedis.Jedis;
import java.util.UUID;

public class RedisDistributedLock {
    private static final String LOCK_KEY = "lock:resource";
    private static final int EXPIRE_TIME = 10;
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;

    private Jedis jedis;

    public RedisDistributedLock() {
        jedis = new Jedis(REDIS_HOST, REDIS_PORT);
    }

    // 获取锁
    public boolean acquireLock() {
        String lockValue = UUID.randomUUID().toString();
        Long result = jedis.setnx(LOCK_KEY, lockValue);
        if (result == 1) {
            jedis.expire(LOCK_KEY, EXPIRE_TIME);
            return true;
        }
        return false;
    }

    // 释放锁
    public boolean releaseLock() {
        String lockValue = jedis.get(LOCK_KEY);
        if (lockValue != null && lockValue.equals(jedis.get(LOCK_KEY))) {
            jedis.del(LOCK_KEY);
            return true;
        }
        return false;
    }

    public void close() {
        jedis.close();
    }

    public static void main(String[] args) {
        RedisDistributedLock lock = new RedisDistributedLock();
        if (lock.acquireLock()) {
            try {
                System.out.println("Lock acquired, performing task.");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.releaseLock();
                System.out.println("Lock released.");
            }
        } else {
            System.out.println("Failed to acquire lock, try again later.");
        }
        lock.close();
    }
}

关键点:

  • SETNX命令保证了“只有一个节点能成功设置锁”这一核心逻辑。
  • 为锁设置过期时间,可以防止因节点崩溃导致的死锁。
  • 释放锁前,通过检查锁对应的值(UUID),确保只有锁的持有者才能删除锁,这是防止误释放的关键一步。

2. 使用 Redisson 实现分布式锁

Redisson 是一个基于 Redis 的高层次 Java 客户端,它把分布式锁的实现封装得相当优雅。通过其提供的RLock接口,开发者几乎可以像使用本地锁一样使用分布式锁,而不用操心底层那些烦人的原子性、重试、续期等问题。

简单来说,它的核心优势在于:开箱即用,省心省力。

Redisson 分布式锁代码示例

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.api.Redisson;
import org.redisson.config.Config;

public class RedissonLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);

        RLock lock = redisson.getLock("lock:resource");

        try {
            if (lock.tryLock()) {
                System.out.println("Lock acquired, performing task.");
                Thread.sleep(5000);
            } else {
                System.out.println("Failed to acquire lock, try again later.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            redisson.shutdown();
            System.out.println("Lock released.");
        }
    }
}

关键点:

  • RLock 是 Redisson 提供的分布式锁对象,用法和 Java 里的 ReentrantLock 很相似。
  • tryLock() 方法会尝试获取锁,获取成功返回 true,否则返回 false
  • Redisson 内部自动处理了锁的超时、重试、以及自动续期等逻辑,大大降低了开发的复杂度。

3. 使用 Lua 脚本实现分布式锁

在 Redis 中,Lua 脚本可以保证多个命令的原子性执行。通过它,我们可以将获取锁、设置过期时间和释放锁等多个操作合并成一步,从根本上避免了竞态条件。

import redis.clients.jedis.Jedis;

public class RedisDistributedLockWithLua {
    private static final String LOCK_KEY = "lock:resource";
    private static final int EXPIRE_TIME = 10;
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;

    private Jedis jedis;

    public RedisDistributedLockWithLua() {
        jedis = new Jedis(REDIS_HOST, REDIS_PORT);
    }

    // 获取锁
    public boolean acquireLock(String lockValue) {
        String script =
            "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then " +
            "   redis.call('EXPIRE', KEYS[1], ARGV[2]) " +
            "   return 1 " +
            "else " +
            "   return 0 " +
            "end";
        Object result = jedis.eval(script, 1, LOCK_KEY, lockValue, String.valueOf(EXPIRE_TIME));
        return "1".equals(result.toString());
    }

    // 释放锁
    public boolean releaseLock(String lockValue) {
        String script =
            "if redis.call('GET', KEYS[1]) == ARGV[1] then " +
            "   return redis.call('DEL', KEYS[1]) " +
            "else " +
            "   return 0 " +
            "end";
        Object result = jedis.eval(script, 1, LOCK_KEY, lockValue);
        return "1".equals(result.toString());
    }

    public void close() {
        jedis.close();
    }

    public static void main(String[] args) {
        RedisDistributedLockWithLua lock = new RedisDistributedLockWithLua();
        String lockValue = "unique-lock-value";

        if (lock.acquireLock(lockValue)) {
            try {
                System.out.println("Lock acquired, performing task.");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.releaseLock(lockValue);
                System.out.println("Lock released.");
            }
        } else {
            System.out.println("Failed to acquire lock, try again later.");
        }
        lock.close();
    }
}

解释 Lua 脚本:

  1. 获取锁:脚本里先执行SETNX尝试设置锁,如果成功(返回1),紧接着调用EXPIRE设置过期时间。整个过程在 Redis 服务端是原子执行的,避免了“获取锁后,设置过期时间前”这个脆弱窗口期。
  2. 释放锁:脚本先通过GET命令检查当前锁的值是否等于客户端的lockValue,只有完全匹配,才执行DEL删除操作。这防止了其他客户端误删锁。

对比三种 Redis 分布式锁实现方式

到这里,三种方案都展示完了。在实际选型时,并没有银弹,每种方案都有其最适合的场景。下面这张表格可以帮你快速决策。

特性SETNX 锁实现Redisson 锁实现Lua 脚本锁实现
实现复杂度简单简单,依赖 Redisson稍复杂,需编写 Lua 脚本
原子性低(需要分两步操作)高(自动处理锁的生命周期)高(所有操作在 Redis 上原子执行)
支持锁续期否(需手动在脚本中实现续期)
锁释放保障需要手动确认锁值自动释放,且可靠性高需要手动确认锁值
性能较高较高,但有额外封装性能开销非常高(减少了网络延迟)
依赖仅需 Redis 客户端需要引入 Redisson 库仅需 Redis 客户端,使用 Lua 脚本
适用场景简单场景,低并发需求高并发,自动续期,可靠性需求高性能、低延迟需求,且能接受脚本复杂性
错误处理容易处理易于使用且错误处理简洁错误处理较为复杂

总结

总结一下,选择哪种方案,核心取决于你的业务场景和对“确定性”的要求:

  • 基于 SETNX 的实现:适合对性能要求较高、场景简单、能容忍锁的可能误操作(比如短时死锁)的场景。优点是实现快,缺点是原子性差、没有续期。
  • Redisson 实现:可以说是生产环境中最推荐的选择。它解决了SETNX方案的核心痛点,提供自动续期、重试等高可靠性能力,并且API设计得非常易用。如果你在用Java,它绝对是首选。
  • Lua 脚本实现:如果你对性能有极致要求,或者不能引入任何外部库,同时团队里有能力编写和调试 Lua 脚本的成员,那么这是一个非常强大的选择。它把原子性和性能都做到了极致,但开发成本也相应更高。

选对了锁,就能在分布式系统的复杂性中,为你的关键业务路径夯实一道可靠的防线。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。