当前位置: 首页>后端>正文

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

本章要点

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

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 已经释放,自己将持有锁。

总结对比:

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

https://www.xamrdz.com/backend/3u41934027.html

相关文章: