分布式锁介绍和一些例子

背景

多进程和多线程中有时候需要加锁,分布式锁是不同主机的不同线程之间需要加锁,加锁的目的就是保证数据一致性,防止数据竞争,单机中有很多保证数据一致性的手段,比如互斥锁、信号量、条件变量等等,那么多主机多线程就需要分布式锁

分布式锁是个啥

分布式锁其实算一种资源,存储在网络主机上,根据不同的手段来实现互斥加锁、解锁。有几个基础的操作,加锁、解锁、网络的交互、而且要求加锁和解锁是一个对象

特点

特点主要有四个

  1. 互斥性,因为要保证数据一致性,防止数据竞争,因此需要互斥性,仅有一个节点持锁
  2. 锁超时,不能集群中一个节点加上锁后,就一直上锁,这样的话如果加锁节点崩溃了,那么这个锁就一直加锁了,其他节点就不能访问资源了,因此要给加锁设置一个时间,超过时间自动释放,如果还想继续用,自己续锁(添加加锁时间)
  3. 可用性,可用性就是在合理的时间内得到正确的回复,如果不保证可用性,存储锁的节点如果崩溃了,那么锁就没有了,其他节点再加锁就会出现不对的情况,可用性根本法则就是 copy,也就是有多个备份点
  4. 容错性,在可用性的基础上,存储锁节点崩溃后,保证锁是由正常行为的(加锁或解锁状态,谁给其加锁的等等),也就是节点崩溃后替换上来的节点和不崩溃的状态要一模一样,主要是使用 raft 一致性算法(半数),redis 的 redlock

类型

主要有两个类型

  1. 可重入锁/非可重入锁,也就是是否是递归锁
  2. 公平锁/非公平锁,也就是互斥锁和自旋锁的区别,公平体现在互斥锁加锁动作获取不到锁,就在阻塞队列排队了,而自旋锁一直在轮询尝试加锁

实现的重点

  1. 锁本质是一个节点上的资源,需要一个节点存储;要保证可用性,避免锁失效
  2. 加锁和解锁需要是同一个节点
  3. 互斥语义
  4. 加锁解锁行为是网络通信,需要锁超时
  5. 获取已持有锁方法
    1. 主动轮询持锁 非公平锁
    2. 被动通知
      1. 广播 非公平锁(不是按照排队的顺序获得的)
      2. 排队单独通知 公平锁
  6. 是否允许同一对象多次加锁
    1. 重入锁
    2. 非重入锁

分布式样例

mysql

mysql 实现分布式主要是利用数据库唯一键的约束来实现互斥性,构建一个表来实现分布式锁。这个表来存储所有的分布式锁。

DROP TABLE IF EXISTS `dislock`;
CREATE TABLE `dislock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `lock_type` varchar(64) NOT NULL COMMENT '锁类型',
  `owner_id` varchar(255) NOT NULL COMMENT '持锁对象',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_lock_type` (`lock_type`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT
CHARSET=utf8 COMMENT='分布式锁表';

加锁操作和解锁操作

INSERT INTO dislock (`lock_type`, `owner_id`)
VALUES ('act_lock', 'ad2daf3');

DELETE FROM dislock WHERE `lock_type` = 'act_lock'
AND `owner_id` = 'ad2daf3';

看看是否可以满足分布式锁的特性

  1. 可用性取决于使用的 mysql 的可用性,如果不是 mysql 集群,仅有一个 mysql 单点,那单点崩溃了,就不可用了
  2. 加锁和解锁需要是同一个节点,owner_id 字段来实现加锁和解锁是一个节点
  3. 互斥性,唯一键值保证了互斥语义的实现
  4. 锁超时,mysql 本身没有实现定时机制,因此需要自己单独开一个线程不断轮询的判断锁是否超时
  5. 获取已持有锁方法,依然需要自己有一个线程,不断地轮询判断能否可以加锁,因此是非公平锁
  6. 加一个 count 字段来记录加锁次数,只有当次数为 0 时,才释放锁。因此可以根据实现,来实现重入锁和非重入锁,配合 owner_id 字段来实现

mysql 实现分布式锁的场景(效率最低,最不完备)

  1. 当主机中仅有 mysql 时,主要业务用不到其他的组件
  2. 仅有少量的业务使用分布式锁

redis

redis 是一种内存数据库,一半是用来做缓存的,官方有分布式锁的样例

看看是否可以满足分布式锁的特性

  1. 可用性,redis 是可以实现可用性的,redis 本事有哨兵模式和 cluster 模式,可以保证数据的可用性,但是容错是不能保证的,因为主从复制是采用异步复制的方式的,因此容错性是不行的。
    1. 同步复制,集群所有节点全部写入了,再返回成功
    2. 异步复制,raft 一致性协议,半数写入就可以返回成功
  2. 加锁和解锁需要是同一个节点,本身有实现此类机制的命令实现,lua 脚本可以实现原子操作,并且可以实现锁超时和互斥性
  3. redlock 解决容错性,redlock 就是有多个 redis 进程,加锁的时候对每个进程进行加锁,半数以上的进程加锁成功,加锁才成功,解锁的时候对每个进程都解锁,半数解锁成功才算成功

redis 实现分布式锁的场景(效率最高)

  1. 我们业务用到了 redis,比如做缓存什么的,有 redis 可以使用
  2. 我们有 redis 集群,那么可以使用其中一个或多个节点实现分布式锁

etdc

完备性最高的分布式锁

  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    140 引用 • 441 回帖
  • 分布式
    78 引用 • 149 回帖 • 4 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...