推广网站可以做跳转吗,wordpress 后台管理插件,保险网站程序源码,网站怎样做收录会更好文章目录 引言正文分布式锁的定义分布式锁的具体应用场景如何实现分布式锁主动轮询型分布式锁实现思路一、MySQL分布式锁二、Redis分布式锁 监听回调型分布式锁Etcd分布式锁Zookeeper分布式锁 锁的对比 总结 引言
最近面试#xff0c;一直被问到分布式锁#xff0c;然后仅仅… 文章目录 引言正文分布式锁的定义分布式锁的具体应用场景如何实现分布式锁主动轮询型分布式锁实现思路一、MySQL分布式锁二、Redis分布式锁 监听回调型分布式锁Etcd分布式锁Zookeeper分布式锁 锁的对比 总结 引言
最近面试一直被问到分布式锁然后仅仅只知道redis了解的不够深刻这里做一个深入的总结
正文
分布式锁的定义 在Java多线程编程中使用Synchronized来实现多个线程之间有序地访问共享资源。相似地定义分布式锁针对多个机器而言的控制他们有序访问共享的资源节点。 一般是通过第三方组件控制多个机器有序访问常见的有 MySQLRedisEtcdZookeeper 基于redis中的分布式锁的命令“setnx key value px expire_time”分布式锁具有如下性质 独占性同一把锁某一个时刻只能被一个机器占用。健壮性不会轻易死锁如果持有锁的机器偶然死机了也能够通过过期时间进行兜底顺利解锁其他机器持有锁对称性谁加锁谁解锁就像redis中的value是每一个线程的uuid一样。
分布式锁的具体应用场景
多任务调度场景
多个任务调度器在不同节点上运行从任务队列中取出任务执行 分布式锁能够保证每一个任务都只会被调用一次
电子商务场景
电子商务系统中创建订单的操作会涉及检查库存、支付服务以及物流服务等多个阶段通过分布式锁保证这些服务按照指定的顺序执行保证数据的一致性。
如何实现分布式锁
分布式锁的实现模型主要分为两种 主动轮询型 取锁方会持续地向分布式锁服务发出获取锁的动作如果锁已经被占用了会不断发起轮询请求直到取到锁为主常见类型 MySQL、Redis 监听回调型 在取锁方发现锁已经被其他对象占用时会创建watcher监听器来订阅锁的释放事件随后不主动获取锁当锁被释放的时候取锁方能通过之前创建的watcher监听到这一变化发起竞争锁的尝试动作。常见类型 Etcd、Zookeeper
主动轮询型分布式锁
实现思路
使用一个数据变量来表示锁redis中类似一个String对象具体流程如下 创建一个变量lock表示一把锁lock是锁的名称lock的内容是持有锁的具体对象 加锁创建lock对象并将自身的标识写入到lock中解锁删除lock对象轮询锁如果锁已经被其他人获取就持续性轮询判定这个变量还存不存在直到自己持有锁 常见的有两种方式 redisMySQL
一、MySQL分布式锁 MySQL加入数据时需要满足唯一性约束如果满足插入成功不满足插入不成功。基于此可以尝试将限定访问的方法设置为具有唯一性约束的数据 加锁插入名称为方法名的记录解锁删除名称为方法名的记录轮询锁 检查对应字段是否存在 具体实现方法如下
CREATE TABLE LOCK_INFO(ID INT(11) UNSINGNED NOT NULL AUTO_INCREMENT,METHOD_NAME VARCHAR(64) NOT NULL ,PRIMARY KEY(ID),UNIQUE KEY METHOD_NAME // 这个最关键指明访问的互斥资源对应字段是独一无二的
)
竞争锁的方式如下
INSERT INTO LOCK_INFO (NAME) VALUES(LOCKMETHODS);
缺点 运行缓慢 数据库操作的是磁盘是通过磁盘IO来实现数据的读写在高并发的情况下运行缓慢 容易死锁 并不能像redis一样有px可以给每一个锁设置一个过期时间进行兜底。如果持有锁的线程挂掉了没有删除数据库中的锁记录就会导致死锁。 单点依赖性 数据库是单点非常依赖数据库的可用性
基本上不用运行慢而且死锁难以解决还是会用基于内存存储的组件来实现分布式锁
二、Redis分布式锁
不同于MySQLredi是基于内存的存储系统轻便高效并且redis是使用单线程模型来完成主要工作的。redis的setnx命令 ex可以设置过期时间以及自身也有过期键删除策略所以能够有效避免死锁问题nxnx仅仅是目标不存在时才会设置对象所以轻松实现分布式锁
具体实现流程
每一把锁使用同一数据来标识key就是锁的名字加锁向redis中插入一条key为锁的名字的数据表示加锁成功解锁删除名称为key的记录表示解锁成功轮询在加锁的时候发现数据已经存在就轮询等待直到持有锁的一方释放锁 正常情况下会间隔一段时间在尝试获取锁
健壮性的实现——锁能够顺利传下去 工作站崩溃无限期持有锁 使用ex指令超过了过期时间会自动释放锁防止线程因为持有锁而陷入死锁状态 工作没干完就过期了 看门狗机制 单独创建一个线程定时向redis进行续期操作防止业务完成之前锁就过期了某一个线程长期持有锁会让其他工作站拿不到锁
对称性的实现——谁加锁谁释放
Lua脚本 使用Lua脚本保证判断锁的归属权 删除锁 操作的原子性
高可用保障——RedLock redis 本身单点执行的为了应对单点故障redis支持主从复制但是redis的主从复制是异步的这就会导致锁的独占性问题。 多个对象同时获得一把锁情况 对象A在Master中获得了lock锁但在之后Master立刻崩溃但是Slave还没来的及将锁同步过去slave提升为Master但是其中并没有对象A关于lock锁的获取情况所以对象B在申请lock也会成功使得对象A和对象B同时获得了lock RedLock多个节点的锁 客户端会和多个独立的Redis实例依次请求加锁只要获得半数一样的锁就算是加锁成功这样不用担心崩溃问题了。只要多数redis节点正常工作分布式锁就能正常工作提升分布式锁的可靠性。 锁获取失败 锁获取失败之后会释放已经获得的部分锁并等待一段随机时间后在重试获得锁
监听回调型分布式锁
不用轮询的相当于给管理员一个通知方式有锁可以竞争就直接通知需要竞争锁的对象来竞争锁。加锁解锁和之前轮询的差不多唯一的区别就是不用一直轮询具体流程如下 定义锁一把锁用一个数据来标识加锁在存储组件里面插入一条数据这条数据之前不存在插入成功加锁成功解锁将该条数据从存储组件中删除的行为是解锁操作
最大的区别
加锁失败监听 锁的竞争者回去监听锁的删除事件当发生锁的删除事件之后锁的竞争者会继续尝试获取锁。
常见应用
Etcd分布式锁ZooKeeper
Etcd分布式锁
Etcd是一种key-value的基于内存的分布式存储仓库其实分布式锁的方式如下。
健壮性——锁能够顺利流转 租约机制 可以为保存锁的key-value设置租约即使持有锁的对象出现故障也能够到期自动释放锁删除键值对支持续约相当于redis中专门实现的一个看门狗续期线程 watch机制 支持watch某个固定的key或者watch一个范围的key一旦某一个key或者范围发生变化客户端会收到通知
总结
Etcd是基于Raft协议实现的高可靠、强一致性的存储组件正常情况下不会像Redis主从异步复制一样导致出现所数据丢失的情况Etcd是监听回调型分布式锁基于Watch机制能够感知锁的释放再去拿锁不会像redis一样轮询
Zookeeper分布式锁
用于提供分布式引用程序协调服务的组件Zookeeper是一种树型结构具有固定的根节点/只能在根节点下创建子节点并在子节点下继续创建节点只能用绝对路径查询Zookeeper节点。总共有4种类型的节点 持久节点 一直存在于Zookeeper中的节点默认的接待你 持久顺序节点 创建节点时按照创建节点的顺序对节点进行编号 临时节点分布式锁实现 客户端和ZooKeeper连接时临时创建的节点客户端断开俩节后进程创建的临时节点就会被删除依赖定期的心跳检测来实现 临时顺序节点 按时间顺序编号创建的临时节点 具体实现流程 加锁 创建临时顺序节点不用担心续期的问题会有心跳检测直到客户端完成任务才会断开当客户机宕机后临时节点就会随之消亡 解锁 客户机完成任务后主动断开链接临时节点会消亡。 监听 监听上一个节点的删除事件
锁的对比 总结
这里再说分布式锁的选型的时候就可以大概从性能和可靠性的角度介绍一下然后在选择redis因为我的项目确实对于性能的要求更高对于可靠性的要求并不高而且占据任务这个唯一需要分布式锁控制访问的接口访问时间也很快不会出现执行时间过长分布式锁过期的情况。两个角度 业务本身执行时间很短、对于安全性和可靠性的要求并不高任务本身是幂等的对于性能要求的高分布式锁的特性redis是上述四种锁中性能最高的而且比较熟悉