Spring Boot 中的 Redis 分布式锁

这篇具有很好参考价值的文章主要介绍了Spring Boot 中的 Redis 分布式锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Spring Boot 中的 Redis 分布式锁

在分布式系统中,多个进程同时访问共享资源时,很容易出现并发问题。为了避免这些问题,我们可以使用分布式锁来保证共享资源的独占性。Redis 是一款非常流行的分布式缓存,它也提供了分布式锁的功能。在 Spring Boot 中,我们可以很容易地使用 Redis 分布式锁来管理并发访问。

本文将介绍 Redis 分布式锁的概念和原理,并说明如何在 Spring Boot 中使用它们。

springboot redis分布式锁,Java 教程,spring boot,redis,分布式

Redis 分布式锁的概念和原理

Redis 分布式锁是一种基于 Redis 的分布式锁解决方案。它的原理是利用 Redis 的原子性操作实现锁的获取和释放,从而保证共享资源的独占性。

在 Redis 中,我们可以使用 setnx 命令来实现分布式锁。setnx 命令可以将一个键值对设置到 Redis 中,但只有在该键不存在的情况下才会设置成功。因此,我们可以将锁的获取和释放分别实现为 setnx 和 del 命令。

以下是一个基本的 Redis 分布式锁示例:

public class RedisDistributedLock {

    private static final String LOCK_KEY_PREFIX = "lock:";
    private static final long LOCK_EXPIRE_TIME = 30000L;

    private RedisTemplate<String, Object> redisTemplate;
    private String lockKey;
    private String lockValue;
    private boolean locked = false;

    public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, String lockKey, String lockValue) {
        this.redisTemplate = redisTemplate;
        this.lockKey = LOCK_KEY_PREFIX + lockKey;
        this.lockValue = lockValue;
    }

    public boolean lock() {
        if (redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue)) {
            redisTemplate.expire(lockKey, LOCK_EXPIRE_TIME, TimeUnit.MILLISECONDS);
            locked = true;
            return true;
        } else {
            return false;
        }
    }

    public void unlock() {
        if (locked) {
            redisTemplate.delete(lockKey);
        }
    }
}

在这个示例中,我们定义了一个 RedisDistributedLock 类,它包含了 lock 和 unlock 两个方法。lock 方法用于获取锁,unlock 方法用于释放锁。

lock 方法的实现逻辑如下:

  1. 使用 setIfAbsent 方法尝试将锁的键值对设置到 Redis 中。
  2. 如果设置成功,则调用 expire 方法设置锁的过期时间,并标记 locked 为 true。
  3. 如果设置失败,则说明锁已经被其他进程占用,返回 false。

unlock 方法的实现逻辑如下:

  1. 如果 locked 为 true,则使用 delete 方法删除锁的键值对。

Spring Boot 中的 Redis 分布式锁实现

在 Spring Boot 中,我们可以使用 RedisTemplate 来访问 Redis,并利用其提供的 setIfAbsent 和 delete 方法实现 Redis 分布式锁。

以下是一个基本的 Spring Boot + Redis 分布式锁示例:

@RestController
public class UserController {

    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public UserController(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable("id") String id) throws InterruptedException {
        RedisDistributedLock lock = new RedisDistributedLock(redisTemplate, "user:" + id, UUID.randomUUID().toString());
        try {
            while (!lock.lock()) {
                Thread.sleep(100);
            }
            // 处理业务逻辑
            return new User();
        } finally {
            lock.unlock();
        }
    }
}

在这个示例中,我们定义了一个 UserController 类,其中包含了一个 getUser 方法,用于获取用户信息。在方法中,我们首先创建了一个 RedisDistributedLock 对象,然后在 while 循环中调用 lock 方法获取锁,直到获取成功为止。在获取锁后,我们可以执行相应的业务逻辑。最后,在 finally 块中调用 unlock 方法释放锁。

需要注意的是,由于 Redis 分布式锁是基于时间的,因此必须设置合适的过期时间。在示例中,我们将锁的过期时间设置为 30 秒。

Redis 分布式锁的优化

在实际应用中,Redis 分布式锁的性能和可靠性都非常重要。以下是几个优化 Redis 分布式锁的方法:

1. 使用 Lua 脚本

在上面的示例中,我们使用了两个 Redis 命令(setIfAbsent 和 expire)来实现分布式锁,这将导致两次网络通信。在高并发情况下,这会增加 Redis 的负载,影响性能。为了避免这个问题,我们可以使用 Lua 脚本来将这两个命令合并为一个原子操作。

以下是一个使用 Lua 脚本实现 Redis 分布式锁的示例:

public class RedisDistributedLock {

    private static final String LOCK_SCRIPT = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('pexpire', KEYS[1], ARGV[2]) return true else return false end";
    private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private static final String LOCK_KEY_PREFIX = "lock:";
    private static final long LOCK_EXPIRE_TIME = 30000L;

    private RedisTemplate<String, Object> redisTemplate;
    private String lockKey;
    private String lockValue;
    private boolean locked = false;
    private String lockScript;
    private String unlockScript;

    public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, String lockKey, String lockValue) {
        this.redisTemplate = redisTemplate;
        this.lockKey = LOCK_KEY_PREFIX + lockKey;
        this.lockValue = lockValue;
        this.lockScript = new DefaultRedisScript<>(LOCK_SCRIPT, Boolean.class).getScriptAsString();
        this.unlockScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class).getScriptAsString();
    }

    public boolean lock() {
        Object result = redisTemplate.execute(new DefaultRedisScript<>(lockScript, Boolean.class), Collections.singletonList(lockKey), lockValue, LOCK_EXPIRE_TIME);
        locked = (Boolean) result;
        return locked;
    }

    public void unlock() {
        if (locked) {
            redisTemplate.execute(new DefaultRedisScript<>(unlockScript, Long.class), Collections.singletonList(lockKey), lockValue);
        }
    }
}

在这个示例中,我们将 setIfAbsent 和 expire 命令合并为一个 Lua 脚本,并使用 RedisTemplate 的 execute 方法来执行脚本。在 lock 方法中,我们执行 LOCK_SCRIPT 脚本,如果返回 true,则表示获取锁成功。在 unlock 方法中,我们执行 UNLOCK_SCRIPT 脚本来释放锁。

使用 Lua 脚本可以减少 Redis 的网络通信次数,从而提高性能和可靠性。

2. 重试机制

由于分布式系统中存在网络抖动等问题,Redis 分布式锁的获取和释放可能会失败。为了提高可靠性,我们可以使用重试机制来重复执行获取锁和释放锁的操作。

以下是一个基本的重试机制实现示例:

public boolean lockWithRetry(int retryCount, long retryInterval) throws InterruptedException {
    int count = 0;
    while (count < retryCount) {
        if (lock()) {
            return true;
        }
        count++;
        Thread.sleep(retryInterval);
    }
    return false;
}

public void unlockWithRetry(int retryCount, long retryInterval) throws InterruptedException {
    int count = 0;
    while (count < retryCount) {
        try {
            unlock();
            break;
        } catch (Exception e) {
            count++;
            Thread.sleep(retryInterval);
        }
    }
}

在这个示例中,我们定义了 lockWithRetry 和 unlockWithRetry 两个方法。lockWithRetry 方法会重复执行 lock 方法,如果获取锁成功,则返回 true;否则,等待一段时间后重试。unlockWithRetry 方法会重复执行 unlock 方法,如果释放锁成功,则结束重试;否则,等待一段时间后重试。

使用重试机制可以提高 Redis 分布式锁的可靠性,保证在网络抖动等情况下仍能正常工作。

3. 采用 Redlock 算法

Redis 分布式锁的另一个问题是单点故障。由于 Redis 是单点的,如果 Redis 实例宕机,那么所有的分布式锁都会失效。为了解决这个问题,我们可以采用 Redlock 算法。

Redlock 算法是 Redis 官方提出的一种分布式锁算法。它通过使用多个 Redis 实例来避免单点故障的问题。具体来说,Redlock 算法首先获取多个 Redis 实例上的锁,然后比较这些锁的时间戳,选择时间戳最小的锁为有效锁。

以下是一个使用 Redlock 算法实现 Redis 分布式锁的示例:

public class RedisDistributedLock {

    private static final String LOCK_KEY_PREFIX = "lock:";
    private static final long LOCK_EXPIRE_TIME = 30000L;
    private static final int RETRY_COUNT = 3;
    private static final long RETRY_INTERVAL = 100L;

    private RedisTemplate<String, Object> redisTemplate;
    private String lockKey;
    private String lockValue;
    private boolean locked = false;
    private List<RedisConnection> connections = new ArrayList<>();

    public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, String lockKey, String lockValue) {
        this.redisTemplate = redisTemplate;
        this.lockKey = LOCK_KEY_PREFIX + lockKey;
        this.lockValue = lockValue;
    }

    public boolean lock() {
        for (int i = 0; i < RETRY_COUNT; i++) {
            RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
            connections.add(connection);
            try {
                byte[] keyBytes = redisTemplate.getKeySerializer().serialize(lockKey);
                byte[] valueBytes = redisTemplate.getValueSerializer().serialize(lockValue);
                long expireTime = System.currentTimeMillis() + LOCK_EXPIRE_TIME + 1;
                for (int j = 0; j < connections.size(); j++) {
                    RedisConnection conn = connections.get(j);
                    if (j == connections.size() - 1) {
                        conn.set(keyBytes, valueBytes, Expiration.milliseconds(LOCK_EXPIRE_TIME), RedisStringCommands.SetOption.SET_IF_ABSENT);
                    } else {
                        conn.set(keyBytes, valueBytes, Expiration.milliseconds(LOCK_EXPIRE_TIME), RedisStringCommands.SetOption.SET_IF_ABSENT);
                        conn.pExpire(keyBytes, expireTime);
                    }
                }
                locked = true;
                return true;
            } catch (Exception e) {
                // ignore
            }
            try {
                Thread.sleep(RETRY_INTERVAL);
            } catch (InterruptedException e) {
                // ignore
            }
        }
        return false;
    }

    public void unlock() {
        for (RedisConnection connection : connections) {
            try {
                connection.del(redisTemplate.getKeySerializer().serialize(lockKey));
            } catch (Exception e) {
                // ignore
            } finally {
                connection.close();
            }
        }
        connections.clear();
        locked = false;
    }
}

在这个示例中,我们定义了一个 RedisDistributedLock 类,它使用多个 RedisConnection 来实现 Redlock 算法。在 lock 方法中,我们首先获取多个 RedisConnection,然后在这些连接上分别执行 set 和 pExpire 命令。在执行完这些命令后,我们比较这些锁的时间戳,选择时间戳最小的锁为有效锁。在 unlock 方法中,我们分别释放每个 RedisConnection 上的锁。

使用 Redlock 算法可以提高 Redis 分布式锁的可靠性,避免单点故障的问题。

总结

在分布式系统中,使用分布式锁是保证共享资源独占性的重要方式。Redis 分布式锁是一种基于 Redis 的分布式锁解决方案,它通过利用 Redis 的原子性操作实现锁的获取和释放,从而保证共享资源的独占性。在 Spring Boot 中,我们可以很容易地使用 Redis 分布式锁,通过 RedisTemplate 来操作 Redis 实例。在使用 Redis 分布式锁时,需要注意锁的粒度、锁的超时时间、重试机制和 Redlock 算法,以保证分布式锁的可靠性和高可用性。文章来源地址https://www.toymoban.com/news/detail-671564.html

到了这里,关于Spring Boot 中的 Redis 分布式锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot如何实现分布式系统中的服务发现和注册?

    随着互联网的快速发展,越来越多的企业开始将自己的业务迁移到分布式系统中。在这种情况下,服务发现和注册变得尤为重要。对于分布式系统中的每个服务来说,它需要知道其他服务的位置和状态,这样才能进行通信和协作。Spring Boot提供了一些工具和框架,可以帮助我

    2024年02月07日
    浏览(30)
  • spring boot + minio 分布式文件上传

    1、分布式文件系统 简单理解为:一个计算机无法存储海量的文件,通过网络将若干计算机组织起来共同去存储海量的文件,去接收海量用户的请求,这些组织起来的计算机通过网络进行通信。 好处: 一台计算机的文件系统处理能力扩充到多台计算机同时处理。 一台计算机

    2024年02月08日
    浏览(45)
  • Spring Boot如何实现分布式消息队列

    在分布式系统中,消息队列是非常重要的一部分,可以帮助开发人员实现异步处理、解耦系统、提高系统可靠性等。本文将介绍如何使用 Spring Boot 实现分布式消息队列。 消息队列是一种存储消息的容器,可以缓存消息并在需要的时候按照一定的规则将消息发送给消费者。常

    2024年02月14日
    浏览(31)
  • Spring Boot 集成 Redisson分布式锁

            Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库,可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面,例如分布式锁、分布式集合、分布式事件发布和订阅等。本篇

    2024年02月10日
    浏览(38)
  • Spring Boot 集成 Redisson 实现分布式锁

            Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库,可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面,例如分布式锁、分布式集合、分布式事件发布和订阅等。本篇

    2024年02月08日
    浏览(32)
  • Spring Boot与Redisson的整合。分布式锁

    Spring Boot与Redisson的整合可以帮助您在Spring Boot应用程序中使用分布式锁、缓存等功能。下面是一些基本步骤来整合Spring Boot与Redisson: 添加Maven/Gradle依赖: 在您的Spring Boot项目的 pom.xml (Maven)或 build.gradle (Gradle)文件中添加Redisson的依赖。 Maven依赖示例: Gradle依赖示例:

    2024年02月12日
    浏览(27)
  • Spring Boot 集成 Redisson分布式锁(注解版)

            Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库,可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面,例如分布式锁、分布式集合、分布式事件发布和订阅等。本篇

    2024年02月09日
    浏览(33)
  • 基于 SpringBoot + Redis 实现分布式锁

    大家好,我是余数,这两天温习了下分布式锁,然后就顺便整理了这篇文章出来。 文末附有源码链接,需要的朋友可以自取。 至于什么是分布式锁,这里不做赘述,不了解的可以自行去查阅资料。 1. 使用 Redis 的 Setnx(SET if Not Exists) 命令加锁。 即锁不存在的时候才能加锁成功

    2024年02月05日
    浏览(45)
  • SpringBoot使用Redis实现分布式缓存

    ✅作者简介:2022年 博客新星 第八 。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏:SpringBoot 框架从入门到精通 ✨特色专栏:国学周更-心性养成之路 🥭本文内容:SpringBoot使用

    2023年04月09日
    浏览(29)
  • spring boot 实现Redisson分布式锁及其读写锁

    分布式锁,就是控制分布式系统中不同进程共同访问同一共享资源的一种锁的实现。 1、引入依赖 2、配置文件 3、配置类 4、测试代码 5、理解 一、时间设置 默认 lock() 小结 lock.lock (); (1)默认指定锁时间为30s(看门狗时间) (2)锁的自动续期:若是业务超长,运行期间自

    2024年02月12日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包