分别使用Redis、MySQL、ZooKeeper构建分布式锁

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

前言

本文使用Java构建三种中间件的分布式锁,下面介绍下三种分布式锁的优缺点,
使用MySQL构建分布式锁,因为数据库数据存储在磁盘中,所以IO速率相对较慢,因此构建出来的分布式锁不适合用在高并发场景,对于一些对并发要求不高的系统中可以使用,进一步提高系统的安全稳定性,提升数据库容错
使用Redis构建分布式锁,都知道redis处理数据是在内存中进行处理,执行效率很高,成为很多开发者设计分布式锁的不二之选,这里需要注意的时,redis在主从切换时,可能会出现锁丢失的情况,也是我们不可忽略的问题。
使用ZooKeeper构建分布式锁,总所周知,ZooKeeper是一个高可用的分布式协调服务,因此使用它来构建分布式锁,无疑性能是三者之中相对来说性能较好的,它可以很好的保证了数据的强一致性,但是使用ZooKeeper需要部署额外的服务,增加了系统复杂度
所以,没有最好的解决方案,只有最适合自己的解决方案。


一、分布式锁简介

分布式锁听名字就才得到,肯定时应用在分布式系统中,锁在Java技术中通常用来解决高并发访问共享资源的问题,那么分布式锁就是解决分布式系统中多个线程或者进程高并发访问共享资源的一种技术,
在分布式系统中,由于各个节点之间的网路通信延迟、故障等原因,可能会导致数据不一致的问题,分布式锁通过协调多个节点的行为,保证在任何时候只有一个节点可以访问资源,以避免数据的不一致性和冲突。


二、分布式锁要求

分布式锁通常需要满足以下几个要求:

  1. 互斥性:在任意时刻只能有一个客户端持有锁。
  2. 不会发生死锁:即使持有锁的客户端发生故障,也能保证锁最终会被释放。
  3. 具有容错性:分布式锁需要能够容忍节点故障等异常情况,保证系统的稳定性。

三、实现方案

在 Java 中,实现分布式锁的方案有多种,包括:

  1. 基于数据库实现的分布式锁:可以通过数据库的乐观锁或悲观锁实现分布式锁,但是由于数据库的 IO 操作比较慢,不适合高并发场景。
  2. 基于 ZooKeeper 实现的分布式锁:ZooKeeper 是一个高可用性的分布式协调服务,可以通过它来实现分布式锁。但是使用 ZooKeeper 需要部署额外的服务,增加了系统复杂度。
  3. 基于 Redis 实现的分布式锁:Redis 是一个高性能的内存数据库,支持分布式部署,可以通过Redis的原子操作实现分布式锁,而且具有高性能和高可用性。

四、数据库分布式锁

数据库的乐观锁或悲观锁都可以实现分布式锁,下面分别来看。

1、悲观锁

在数据库中使用 for update 关键字可以实现悲观锁,我们在 Mapper 中添加 for update 即可对数据加锁,实现代码如下:

<!-- UserMapper.xml -->
<select id="selectByIdForUpdate" resultType="User">
    SELECT * FROM user WHERE id = #{id} FOR UPDATE
</select>

在 Service 中调用 Mapper 方法,即可获取到加锁的数据:

@Transactional
public void updateWithPessimisticLock(int id, String name) {
    User user = userMapper.selectByIdForUpdate(id);
    if (user != null) {
        user.setName(name);
        userMapper.update(user);
    } else {
        throw new RuntimeException("数据不存在");
    }
}

2、乐观锁

在 MyBatis 中,可以通过给表添加一个版本号字段来实现乐观锁。在 Mapper 中,使用标签定义更新语句,同时使用 set 标签设置版本号的增量。

<!-- UserMapper.xml -->
<update id="updateWithOptimisticLock">
    UPDATE user SET
    name = #{name},
    version = version + 1
    WHERE id = #{id} AND version = #{version}
</update>

在 Service 中调用 Mapper 方法,需要传入更新数据的版本号。如果更新失败,说明数据已经被其他事务修改,具体实现代码如下:

@Transactional
public void updateWithOptimisticLock(int id, String name, int version) {
    User user = userMapper.selectById(id);
    if (user != null) {
        user.setName(name);
        user.setVersion(version);
        int rows = userMapper.updateWithOptimisticLock(user);
        if (rows == 0) {
            throw new RuntimeException("数据已被其他事务修改");
        }
    } else {
        throw new RuntimeException("数据不存在");
    }
}

五、Zookeeper分布式锁

在 Spring Boot 中,可以使用 Curator 框架来实现 ZooKeeper 分布式锁,具体实现分为以下 3 步:

  1. 引入 Curator 和 ZooKeeper 客户端依赖;
  2. 配置 ZooKeeper 连接信息;
  3. 编写分布式锁实现类。

1、引入Curator和ZooKeeper

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>latest</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>latest</version>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>latest</version>
</dependency>

2、配置ZooKeeper连接

在 application.yml 中添加 ZooKeeper 连接配置:

spring:
  zookeeper:
    connect-string: localhost:2181
    namespace: demo

3、编写分布式实现类

@Component
public class DistributedLock {

    @Autowired
    private CuratorFramework curatorFramework;

    /**
     * 获取分布式锁
     *
     * @param lockPath   锁路径
     * @param waitTime   等待时间
     * @param leaseTime  锁持有时间
     * @param timeUnit   时间单位
     * @return 锁对象
     * @throws Exception 获取锁异常
     */
    public InterProcessMutex acquire(String lockPath, long waitTime, long leaseTime, TimeUnit timeUnit) throws Exception {
        InterProcessMutex lock = new InterProcessMutex(curatorFramework, lockPath);
        if (!lock.acquire(waitTime, timeUnit)) {
            throw new RuntimeException("获取分布式锁失败");
        }
        if (leaseTime > 0) {
            lock.acquire(leaseTime, timeUnit);
        }
        return lock;
    }

    /**
     * 释放分布式锁
     *
     * @param lock 锁对象
     * @throws Exception 释放锁异常
     */
    public void release(InterProcessMutex lock) throws Exception {
        if (lock != null) {
            lock.release();
        }
    }
}

六、Redis分布式锁

我们可以使用 Redis 客户端 Redisson 实现分布式锁,它的实现步骤如下:

  1. 添加 Redisson 依赖
  2. 配置 Redisson 连接信息
  3. 编写分布式锁代码类

1、添加Redisson依赖

在pom.xml中添加如下配置

<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.20.0</version>
</dependency>

2、配置Redission连接

在 Spring Boot 项目的配置文件 application.yml 中添加 Redisson 配置:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
redisson:
  codec: org.redisson.codec.JsonJacksonCodec
  single-server-config:
    address: "redis://${spring.data.redis.host}:${spring.redis.port}"
    database: "${spring.data.redis.database}"
    password: "${spring.data.redis.password}"

3、编写分布式锁代码类

import jakarta.annotation.Resource;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RedissonLockService {
    @Resource
    private Redisson redisson;

    /**
     * 加锁
     *
     * @param key     分布式锁的 key
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return
     */
    public boolean tryLock(String key, long timeout, TimeUnit unit) {
        RLock lock = redisson.getLock(key);
        try {
            return lock.tryLock(timeout, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    /**
     * 释放分布式锁
     *
     * @param key 分布式锁的 key
     */
    public void unlock(String key) {
        RLock lock = redisson.getLock(key);
        lock.unlock();
    }
}

七、Redis锁与ZooKeeper分布式锁的区别

Redis 和 ZooKeeper 都可以用来实现分布式锁,它们在实现分布式锁的机制和原理上有所不同,具体区别如下:

  1. 数据存储方式:Redis 将锁信息存储在内存中,而 ZooKeeper 将锁信息存储在 ZooKeeper 的节点上,因此 ZooKeeper 需要更多的磁盘空间。
  2. 锁的释放:Redis 的锁是通过设置锁的过期时间来自动释放的,而 ZooKeeper 的锁需要手动释放,如果锁的持有者出现宕机或网络中断等情况,需要等待锁的超时时间才能自动释放。
  3. 锁的竞争机制:Redis 使用的是单机锁,即所有请求都直接连接到同一台 Redis 服务器,容易发生单点故障;而 ZooKeeper 使用的是分布式锁,即所有请求都连接到 ZooKeeper 集群,具有较好的可用性和可扩展性。
  4. 一致性:Redis 的锁是非严格意义下的分布式锁,因为在多台机器上运行多个进程时,由于 Redis 的主从同步可能会存在数据不一致的问题;而 ZooKeeper 是强一致性的分布式系统,保证了数据的一致性。
  5. 性能:Redis 的性能比 ZooKeeper 更高,因为 Redis 将锁信息存储在内存中,而 ZooKeeper 需要进行磁盘读写操作。

总结,Redis 适合实现简单的分布式锁场景,而 ZooKeeper 适合实现复杂的分布式协调场景,也就是 ZooKeeper 适合强一致性的分布式系统。

知识点:强一致性是指系统中的所有节点在任何时刻看到的数据都是一致的。ZooKeeper
中的数据是有序的树形结构,每个节点都有唯一的路径标识符,所有节点都共享同一份数据,当任何一个节点对数据进行修改时,所有节点都会收到通知,更新数据,并确保数据的一致性。在
ZooKeeper 中,强一致性体现在数据的读写操作上。ZooKeeper 使用 ZAB(ZooKeeper Atomic
Broadcast)协议来保证数据的一致性,该协议确保了数据更新的顺序,所有的数据更新都需要经过集群中的大多数节点确认,保证了数据的一致性和可靠性。


备注

本文根据51CTO博主磊哥文章一步步操作实现之后,做的笔记
原文连接:https://www.51cto.com/article/766555.html文章来源地址https://www.toymoban.com/news/detail-735796.html

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

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

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

相关文章

  • 图解Redis和Zookeeper分布式锁

    Redis分布式锁原理如上图所示,当有多个Set命令发送到Redis时,Redis会串行处理,最终只有一个Set命令执行成功,从而只有一个线程加锁成功 利用_ Redis的setNx 命令在Redis数据库中创建一个Key,Value记录, 这条命令只有当Redis中没有这个Key的时候才执行成功,当已经有这个Key的时

    2024年02月07日
    浏览(27)
  • 实现分布式锁:Zookeeper vs Redis

    目录 引言 1. Zookeeper分布式锁 1.1特点和优势: 强一致性 顺序节点 Watch机制 1.2 Zookeeper分布式锁代码示例 2. Redis分布式锁 2.1特点和优势: 简单高效 可续租性 灵活性 2.2Redis分布式锁代码示例 3.对比和选择 3.1  一致性要求 3.2  适用场景 3.3 性能和复杂度 结论 在分布式系统中,

    2024年01月22日
    浏览(35)
  • 探索分布式锁:Redis与Zookeeper实现解析

    分布式锁是分布式系统中的一个常见问题,它可以帮助我们在多台机器上同步对共享资源的访问。在本文中,我们将介绍两种流行的分布式锁实现:基于Redis的分布式锁和基于Zookeeper的分布式锁。我们将讨论它们的原理、应用场景以及一些实用的技巧和案例。 在传统的单体应

    2024年04月23日
    浏览(35)
  • 分布式锁实现(mysql,以及redis)以及分布式的概念(续)redsync包使用

    这张尽量结合上一章进行使用:上一章 这章主要是讲如何通过 redis 实现分布式锁的 这里我用 redis 去实现: 技术: golang , redis , 数据结构 这里是有一个大体的实现思路:主要是使用 redis 中这些语法 redis 命令说明: setnx 命令: set if not exists ,当且仅当 key 不存在时,将 ke

    2024年01月22日
    浏览(52)
  • 分布式系统面试全集通第一篇(dubbo+redis+zookeeper----分布式+CAP+BASE+分布式事务+分布式锁)

    什么是分布式 一个系统各组件分别部署在不同服务器。彼此通过网络通信和协调的系统。 也可以指多个不同组件分布在网络上互相协作,比如说电商网站 也可以一个组件的多个副本组成集群,互相协作如同一个组件,比如数据存储服务中为了数据不丢失而采取的多个服务备

    2024年04月11日
    浏览(38)
  • Java中利用Redis,ZooKeeper,数据库等实现分布式锁(遥遥领先)

    1.1 什么是分布式锁 在我们进行单机应用开发涉及并发同步的时候,我们往往采用synchronized或者ReentrantLock的方式来解决多线程间的代码同步问题。但是当我们的应用是在分布式集群工作的情况下,那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题

    2024年02月03日
    浏览(38)
  • 使用ZooKeeper实现分布式锁

    目录 引言 1. ZooKeeper简介 2. 分布式锁实现原理 3. 分布式锁实现步骤 步骤一:创建ZooKeeper客户端 步骤二:创建分布式锁类 步骤三:使用分布式锁 4. 总结 在分布式系统中,实现分布式锁是一项常见的任务,可以用于保证同一时间只有一个客户端可以访问共享资源,从而避免竞

    2024年02月21日
    浏览(36)
  • 分布式锁实现(mysql,以及redis)以及分布式的概念

    我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如何,我却道不出个1,2,3。现在就将这些做一个详细的总结。至少以后碰到面试官可以说上个123。 那么就正式进入

    2024年01月21日
    浏览(46)
  • springboot 使用zookeeper实现分布式队列

    一.添加ZooKeeper依赖:在pom.xml文件中添加ZooKeeper客户端的依赖项。例如,可以使用Apache Curator作为ZooKeeper客户端库: 二.创建ZooKeeper连接:在应用程序的配置文件中,配置ZooKeeper服务器的连接信息。例如,在application.properties文件中添加以下配置:  三.创建分布式队列:使用Z

    2024年02月13日
    浏览(28)
  • springboot 使用zookeeper实现分布式锁

    一.添加ZooKeeper依赖:在pom.xml文件中添加ZooKeeper客户端的依赖项。例如,可以使用Apache Curator作为ZooKeeper客户端库: 二.创建ZooKeeper连接:在应用程序的配置文件中,配置ZooKeeper服务器的连接信息。例如,在application.properties文件中添加以下配置: 三.创建分布式锁:使用ZooKee

    2024年02月13日
    浏览(24)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包