美文网首页
第二章架构设计之技术实践篇(中)

第二章架构设计之技术实践篇(中)

作者: 农民工进城 | 来源:发表于2022-01-06 00:36 被阅读0次

    本章要点

    • 分布式锁特点
    • 分布式锁实现

    1. 分布式锁特点

    分布式特点:

    • 强一致性
    • 服务高可用、系统稳健
    • 分布式续约及其自动释放
    • 代码高度抽象,业务接入简单
    • 可视化管理后台
    • 业务可重入

    2.分布式锁实现

    2.1 Redis 分布式锁
    2.1.1 加锁:
    SET key value [EX seconds] [PX milliseconds] [NX|XX]
    
    • value 是客户端生成的唯一的字符串。
    • PX 5000 设置键的过期时间为5000毫秒
    • EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
    • PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
    • NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
    • XX :只在键已经存在时,才对键进行设置操作。
    2.1.2 解锁:(判断value是否相等,相等才能删除)
    if redis.call('get',KEYS[1]) == ARGV[1] then 
       return redis.call('del',KEYS[1])  //先比较一下值,相等才删除。防止其他线程把锁给解了
    else
       return 0 
    end
    

    redis分布式锁存在的问题:

    • 业务时间,无法估计,时间到了,无法续期
    • redis单点,如果宕机,整个系统就会崩溃;redis主从模式,那么master宕机了,存储的key还没同步到slave,存在锁失效问题

    redisson主要解决的就是redis这两个问题。

    • Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

    • 至少部署3个主从集群(至少6个节点),大多数获取成功,才是成功(过半原则)

      分布式锁:分布式锁是CP模型,但是Redis是AP模型所以不适合做分布式锁

    2.2 基于Mysql 分布式锁
    2.2.1 基于表记录
    • 创建表
    CREATE TABLE `database_lock` (
        `id` BIGINT NOT NULL AUTO_INCREMENT OMMENT '主键id',
        `resource` varchar(32) NOT NULL COMMENT '锁定的资源',
        `description` varchar(1024) NOT NULL DEFAULT "" COMMENT '描述',
        PRIMARY KEY (`id`),
        UNIQUE KEY `uiq_idx_resource` (`resource`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据库分布式锁表';
    
    

    获取锁

    INSERT INTO database_lock(resource, description) VALUES ('mylock', 'lock');
    
    

    释放锁

    DELETE FROM database_lock WHERE resource='mylock';
    
    2.2.2 乐观锁(版本号或时间戳)
    CREATE TABLE `optimistic_lock` (
        `id` BIGINT NOT NULL AUTO_INCREMENT,
        `resource` int NOT NULL COMMENT '锁定的资源',
        `version` int NOT NULL COMMENT '版本信息',
        `created_at` datetime COMMENT '创建时间',
        `updated_at` datetime COMMENT '更新时间',
        `deleted_at` datetime COMMENT '删除时间', 
        PRIMARY KEY (`id`),
        UNIQUE KEY `uiq_idx_resource` (`resource`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据库分布式锁表';
    
    
    • STEP1 获取资源: SELECT resource, version FROM optimistic_lock WHERE id = 1
    • STEP2 执行业务逻辑
    • STEP3 更新资源:UPDATE optimistic_lock SET resource = resource -1, version = version + 1 WHERE id = 1 AND version = oldVersion
    2.2.3悲观锁
    • STEP1 获取锁:SELECT * FROM database_lock WHERE id = 1 FOR UPDATE;。
    • STEP2 执行业务逻辑。
    • STEP3 - 释放锁:COMMIT。
    2.3 基于ZK分布式锁

    ZK有四种节点类型:持久节点、持久节点顺序节点、临时节点和临时顺序节点;ZK分布式锁主要是用临时顺序节点。

    • 1)创建的临时顺序节点是文件夹下的第一个节点,则认为是获取分布式锁成功。
    • 2)创建的临时顺序节点不是文件夹下的第一个节点,则认为当前锁已经被另一个客户端线程获取,此时需要进入阻塞状态,等待节点顺序中的前一个节点释放锁的时候唤醒当前线程。

    curator是Netflix公司开源的一个ZooKeeper客户端封装。curator 对于锁这块做了一些封装,curator 提供了InterProcessMutex 这样一个 api

    Curator的几种锁方案:
    • InterProcessMutex:分布式可重入排它锁
    • InterProcessSemaphoreMutex:分布式排它锁
    • InterProcessReadWriteLock:分布式读写锁
    • InterProcessMultiLock:将多个锁作为单个实体管理的容器

    InterProcessMutex通过在zookeeper的某路径节点下创建临时序列节点来实现分布式锁,即每个线程(跨进程的线程)获取同一把锁前,都需要在同样的路径下创建一个节点,节点名字由uuid + 递增序列组成。而通过对比自身的序列数是否在所有子节点的第一位,来判断是否成功获取到了锁。当获取锁失败时,它会添加watcher来监听前一个节点的变动情况,然后进行等待状态。直到watcher的事件生效将自己唤醒,或者超时时间异常返回

    2.4 基于 etcd分布式锁

    etcd分布式锁实现的基础机制:

    • Lease机制:etcd具有TTL机制,所以etcd 可以为存储的 key-value对设置过期时间,当key-value 将失效时,同时也可以,通过客户端进行续约, 以避免 key-value对过期失效。

    • Revision机制:每个 key 带有一个 Revision 号,每进行一次事务便+1,它是全局唯一的, 通过 Revision 的大小就可以知道进行写操作的顺序。在实现分布式锁时,多个客户端同时抢锁, 根据 Revision 号大小依次获得锁,可以避免 “羊群效应” ,实现 公平锁

    • Prefix机制:即前缀机制。例如,一个名为 /etcd/lock 的锁,两个争抢它的客户端进行写操作, 实际写入的 key 分别为:key1="/etcd/lock/UUID1",key2="/etcd/lock/UUID2"。其中,UUID 表示全局唯一的 ID,确保两个 key 的唯一性。写操作都会成功,但返回的 Revision 不一样,通过前缀 /etcd/lock 查询,返回包含两个 key-value 对的的 KeyValue 列表, 同时也包含它们的 Revision,通过 Revision 大小,客户端可以判断自己是否获得锁。

    • Watch机制:即监听机制,Watch 机制支持 Watch 某个固定的 key,也支持 Watch 一个范围(前缀机制),当被 Watch 的 key 或范围发生变化,客户端将收到通知;在实现分布式锁时,如果抢锁失败, 可通过 Prefix 机制返回的 Key-Value 列表获得 Revision 比自己小且相差最小的 key(称为 pre-key), 对 pre-key 进行监听,因为只有它释放锁,自己才能获得锁,如果 Watch 到 pre-key 的 DELETE 事件, 则说明 pre-key 已经释放,自己将持有锁。

    总结对比:

    image.png

    相关文章

      网友评论

          本文标题:第二章架构设计之技术实践篇(中)

          本文链接:https://www.haomeiwen.com/subject/zruztltx.html