暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

整理常见的几种分布式锁实现方案

龙虾编程 2025-02-10
251

    分布式锁是一种用于保证分布式系统中多个进程或线程同步访问共享资源的技术。分布式锁通过协调多个节点的行为,保证在任何时刻只有一个节点可以访问共享资源,以避免数据的不一致性和冲突。实现的分布式锁的常见的方案有借助数据库、Redis、Zookeeper等方案,下面我们来聊聊这几种实现方案。

1、数据库实现分布式锁的方案

    使用数据库实现分布式锁的方案比较简单,此方案的核心是创建一张锁表,然后对数据表中的字段作唯一性约,数据表的设计如下所示:

在分布式环境下,借助数据库实现分布式锁的流程如下所示:

    线程A尝试获取锁时,由于此时没有其他线程持有锁,所以线程A可以成功获取到了锁,并向MySQL表中插入一行数据。

    insert into lock_table(`lock_key`,`lock_time`,`lock_duration`,`lock_owner`) values('9875613',10's','theard_001');

        假如线程B现在想要获取相同的锁,线程B先查询数据表,此时检测到线程A插入的数据行已经存在(代表已有线程加锁成功了)。在这种情况下线程B无法获取到锁。

        此时线程B会在指定的TTL窗口内不断重试几次,如果线程A释放锁后线程B可以成功获取锁,如果在TTL窗口时间中线程A没有释放锁,那么线程B获取锁失败。

        线程A完成业务逻辑成功后,线程A将删除数据表中的行数据,这样就实现了释放锁的功能。

      delete from lock_table where id = #{id};

      一旦线程A释放了锁,其他线程就可以竞争锁。

      2、Redis实现分布式锁

          Redis实现分布式锁使用的命令是setnx,这个命令执行过程中当且仅当key不存在时,set一个key的值value返回1;若key存在,则什么都不做并且返回0,使用Redis的setnx加锁的执行流程如下所示:

          获取锁的时候使用setnx加锁,锁的value值可以为一个随机生成的UUID,通过value在释放锁的时候进行判断,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁。如果这个时候其他的线程加同样的锁就返回失败。

          持有锁的线程在释放锁的时候,首先通过value中的UUID值判断是不是当前的锁,若是当前锁,则执行delete进行锁释放。

          使用Redis实现分布式锁中一些细节需要自己控制(如锁的超时时间设置问题、Redis加锁后主节点突然宕机问题),所以推荐使用市面上成熟的框架Redisson实现分布式锁,Redisson的执行流程流程如下所示:

          Redisson实现的分布式锁可以很好解决锁应该设置多长时间的超时时间的问题,因为它有看门狗帮助我们做延时操作,如果发现业务没有完成,那么看门狗可以自动延时。

          Redis的主从架构是AP模式,存在重复加锁的问题。假设主节点(master)加锁成功但是没有及时的同步到从节点(slave),此时主节点突然宕机了,那么此时从节点就升级成主节点,线程B来到刚升级为主节点的从节点上加锁,此时加锁是成功的,这样就会出现重复加锁的问题。如下图所示:

          Redisson使用了红锁来解决这个问题,通过红锁对每一个节点进行数据的存储,只有每个节点都存储成功了后,才返回上锁成功,这样就可以能够保证把每个节点都可以当成主节的去使用,也就保证这个数据一致性。

      3、Zookeeper实现分布式锁

          ZooKeeper是一个高可用性的分布式协调服务,可以通过它的顺序节点方式来实现分布式锁功能,Zookeeper的分布式锁的流程如下图所示:

          当Zookeeper接收到请求后,在lock节点下创建临时顺序节点,然后根据是不是当前节点下最小的节点(如/sqe_00000000是最小的节点),如果是最小节点就表示线程获取锁成功,如果不是最小节点,那么对前一个节点进行监听。

          获取到锁的线程处理完业务后,执行delele节点释放锁,然后后面的节点将收到监听通知,可以获取到锁执行业务。

          Zookeeper是cp模式,具有天然的一致性,所以不需要考虑节点之间的数据同步问题。

      总结:

      (1)基于数据库的分布式锁存在并发性能较差,公平锁与阻塞式锁的实现比较复杂,数据库IO操作比较慢,不适合高并发场景。

      (2)Redis和ZooKeeper都可以用来实现分布式锁,在实现分布式锁的机制和原理上存在一定的差异,具体区别如下:

          (a)Redis将锁信息存储在内存中,而ZooKeeper将锁信息存储在 ZooKeeper的节点上,因此ZooKeeper需要更多的磁盘空间。

          (b)Redis 的主从同步可能会存在数据不一致的问题;而ZooKeeper是强一致性的分布式系统,保证了数据的一致性。

          (c)Redis的性能比ZooKeeper更高,因为Redis 将锁信息存储在内存中,而ZooKeeper需要进行磁盘读写操作。

          (d)Redis使用的是单机锁,容易发生单点故障;而ZooKeeper使用的是分布式锁,具有较好的可用性和可扩展性。

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

      文章转载自龙虾编程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论