• 分布式锁


    为什么要使用分布式锁

    使用分布式锁的目的,无外乎是为了保证共享资源在同一时间只被一个线程操作。

    单机时代,通常我们会使用java并发相关API(Lock或synchronized)进行互斥实现。分布式情况下较为复杂,线程分配在不同的进程或机器中,这时候我们需要一种跨JVM的互斥机制来控制共享资源的访问,分布式锁应运而生。

    需要具备哪些条件

    1. 在分布式系统环境下,一个方法在同一时间只能被一个线程执行;

    2. 高可用、高性能的获取锁和释放锁;

    3. 具备可重入性;

    4. 失效机制,防止死锁;

    5. 具备阻塞锁和非阻塞锁特性(根据业务需要考虑需要);

    常见的分布式锁实现方案

    1. 基于数据库实现方案;

    2. 基于分布式缓存实现的锁服务:典型代表是使用Redis实现锁服务和基于Redis实现的RedLock方案;

    3. 基于分布式一致性算法实现锁服务:典型代表zookeeper和google的Chubby; 

    具体实现

    基于数据库

    先来了解核心实现:

    1. 创建一张锁表,以其中一个字段为唯一索引

    2. 使用方法名向表中插入数据,插入成功则获取锁成功

    3. 方法结束后删除数据,释放锁

    以上三步完成锁的基本要求,实现简单,但工作中为什么我们不使用这种方式呢?

    有以下几个问题:

    • 数据库的可用性和性能影响分布式锁的可用性及性能

    • 锁没有失效时间,可能会导致死锁

    • 只能非阻塞,成功失败立刻返回

    • 非重入,同一个线程在没有释放锁之前无法再次获得该锁

    当然我们也可以在程序上处理这几个问题,但考虑到性能与复杂度又会得不偿失。

    基于Redis

    先来了解锁核心实现:

    1. 加锁setnx(key,value)

    2. 锁超时expire(key,time)

    3. 解锁del(key)

    以上三步满足锁的基本要求,细思量某些场景又会出问题:

    场景1:A想获取锁,于是A操作setnx成功,这时A突然挂了,还未来得及expire; 此时锁就无超时时间。

    解决:使用SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX] 替代setnx与expire,变成原子操作;

    场景2:A获取锁成功,这时B来了,B获取锁失败,走之前顺便del一下,把锁释放了,这时C来了就和A撞一起了。 

    解决:使用线程ID存储value,释放锁之前判断是否是自己的锁(不限于此)

    场景3:A获取锁成功,但是A干活太慢所已经超时了,这时B来了

    解决:启动守护线程重设A的超时时间(考虑程序优化,为什么执行这么慢呢)

     

    基于zookeeper

    了解Zookeeper的节点分为4种类型,分别为持久节点、持久顺序节点、时节点、临时顺序节点;这里我们重点关注 【临时顺序节点】,这是实现分布式锁的重要依据。这种节点有如下几个特性:

    1. 会话结束后节点自动删除,也可手动删除;

    2. 不能拥有子节点;

    3. 节点以10位数字序列号为后缀形式命名;(zk_lock_0000000001)

    来看一张图:

    先来分析排它锁(Exclusive_lock),核心过程如下:

    1. A、B、C竞争锁资源,创建临时节点lock,A创建成功;

    2. B、C设置watch lock节点;

    3. A执行操作完成,删除lock节点释放锁,或A挂了,lock节点自动删除;

    4. B、C收到zk的通知;

    5. B和C竞争创建临时节点lock,同上面4步,直至获取锁成功

    排它锁实现简单易于理解,不再多说;下面来分析共享锁(Share_lock);

    1. A、B、C分别创建自己的顺序临时节点分别为R1、W2、W3

    2. 获取zk的 /Share_lock下的所有节点;

    3. 判断自己的节点是否是Share_lock下的最小节点,A最小获取锁成功,B、C置自身上一个节点的watcher;即B watch R1 , C watch W2;

    4. A操作完成,删除R1节点;

    5. zk通知B获取锁;

    简述优缺点

    从三个方面考虑:实现复杂度、性能、可靠性由高到低对三种实现方式比较

    实现复杂度:zookeeper > 缓存 > 数据库

    可靠性:zookeeper > 缓存 > 数据库

    性能:缓存 > zookeeper > 数据库

  • 相关阅读:
    聪明人 & 普通人
    13种模型及方法论
    面向大规模商业系统的数据库设计和实践
    分治算法
    软件架构
    隐含前提思维模型
    Git回滚代码到某个commit
    使用arthas排查 test 问题
    Arthas
    docker 操作入门
  • 原文地址:https://www.cnblogs.com/sjp007/p/10238718.html
Copyright © 2020-2023  润新知