基于CAP模型设计企业级真正高可用的分布式锁
来自:架构之美
1.CAP定律剖析
2000年Eric Brewer教授提出CAP猜想,2年后CAP猜想被Seth Gilbert和Nancy Lynch从理论上证明。CAP是Consitency(强一致性)、Availability(可用性)、Partition tolerance(网络分区容忍性)三个不同维度的组合体,如图1所示。
图1 CAP定律
在分布式系统中,CAP定律中的三者只能同时满足二者(如图1所示):CP、AP、AC模型。进一步分析,AC模型并不真正的存在,脱离P(分布式环境)谈AC都是耍流氓。我们以多机房数据库同步更新的场景来分析下为什么CAP定律中三者不能同时满足,如图2所示,用户通过机房一的数据访问层写入数据到MySQL主库,并通过网络把此数据同步到机房二的MySQL从库。在此场景下,CAP对应的含义为:C为机房一的MySQL数据库主节点更新,那么机房二的MySQL数据从节点也要更新;A为必须保证MySQL主从两个数据节点都是可用的;P为当机房一和机房二主从节点出现网络分区,必须保证系统对外可用。对于机房一的写请求,一旦机房一和机房二出现网络分区(即网络断开),此时写请求无法成功写入到机房二的MySQL从库,就会导致写请求无法返回,即A(可用性)无法满足。大家可以思考一个问题,假设机房一和机房二的网络终究会恢复,用户侧也能够容忍机房网络恢复的时间一直等待,那么CAP定律是否同时满足?
图2 CAP定律多机房数据同步场景验证
2.业务场景驱动
我们来看三个典型的业务场景:业务场景一:在秒杀的场景下,只允许用户购买一件商品;业务场景二:用户下单成功后会产生下单消息,在订单消息响应应答模式下会发送多条消息到MQ中,下游在对MQ中订单消息进行消费时,需要对此订单消息进行去重;业务场景三:在用户对商品下单后,订单状态变为待支付,在某一时刻用户正在对该订单做支付操作,商家对该订单进行改价操作,如何保证操作的数据一致性。
通过业务需求剖析业务背后的本质,是架构师需要具备的核心能力之一。这三个业务场景背后共性的本质是什么?业务场景一需要对用户进行并发控制,也就是需要对用户ID进行串行化操作处理,防止用户重复下单;业务场景二需要对订单消息中的订单ID进行串行化操作处理,防止下游对订单消息重复消费;业务场景三需要对订单ID进行串行化操作处理,防止出现数据的不一致性。既然三个场景都需要对共享资源进行串行化处理,问题转化为锁处理的问题。如果是单机环境通过本地锁的方式可以优雅解决(如图3),在分布式的环境下,服务冗余部署多份,不同的请求由不用的冗余服务来处理,本地锁将不能很好的工作,需要分布式锁进行处理(如图4)。
图3 本地锁
图4 分布式锁
3.分布式锁本质
提到分布式锁,大家能够想到基于Redis来实现,锁的本质是对共享资源串行化处理。Redis内部采用唯一线程的串行化处理请求恰好满足锁的使用场景。那么基于Redis如何具体实现分布式锁?我们从具体命令和架构两个维度进行分析。
在具体命令实现侧,有两种方式,第一采用Redis SetNX(Set if Not eXsits)命令,此命令在指定的Key不存在时,为Key设置指定的值。SETNX Key Value命令设置成功返回1,设置失败,返回0。比如在业务场景一,用户ID为1009,此时Key为1009,Value 可以随意填写,例如100。当两个服务同时调用调用SETNT 1009 100命令申请锁时,Redis保证只有一个服务能够成功申请到锁。对于锁我们需要加上锁使用的时间,确保锁的公平性,最坏情况下,其他服务能够有机会申请到这把锁。为了达到这个目的,需要对锁进一步添加过期时间,使用EXPIRE Key seconds命令,设置生存时间,比如为用户ID1009设置10S的生存时间:EXPIRE 1009 10。由于SETNX命令和EXPIRE命令是两条命令,需要保证他们同时执行成功。Redis提供了事务的处理方式:采用【MULTI;多个执行命令;EXEC;】LUA脚本执行语句组。业务场景一可以如下进行事务处理:
MULTI;
SETNX 1009 100;
EXPIRE 1009 10;
EXEC;
上述具体命令实现较为复杂,在Redis 2.6.12及以上的版本,可以采用Set Key Value NX PX milliseconds命令方式,此命令在指定的Key不存在时,为Key设置指定的值,并设置生存时间。其中NX参数表示指定的Key不存在时,再设置指定的值;PX参数表示生存时间,单位为毫秒。业务场景一采用Set命令,具体调用命令如下:Set 1009 100 NX PX 10000。
在架构设计侧,第一种方式采用Redis单机方式(如图5),当服务S1和服务S2同时申请锁(Set 1009 100 NX PX 10000)简称为L1时,Redis单机会按照接受到请求先后顺序的处理方式,保证S1申请到锁L1,S2申请锁L1失败。(假设S1先申请锁L1,S2后申请锁L1)。Redis单机模式存在单点隐患,一旦Redis宕机,内存中的锁全部丢失,Redis再次启动,假如此时服务S1还在使用锁L1,服务S2又再次申请锁L1,就会申请成功,此时就会出现同一时刻2个服务同时都拿到同一把锁的尴尬局面。
图 5 分布式锁Redis单机模式
第一种方式问题在于Redis的单机模式,通过使用Redis集群的主从模式来解决Redis单机模式的数据丢失问题。第二种方式如同6所示,在业务场景一,当服务S1和服务S2同时在Redis主节点申请锁L1时,服务S1申请到锁L1。通过Redis主从集群的数据同步机制会异步同步给Redis从节点,Redis从节点也拥有了锁L1。假设Redis主节点挂掉,由于Redis集群的Sentinal的哨兵监控和主从切换机制,此时Redis集群的从节点会提升为新Redis集群的主节点,继续对外提供锁申请服务,使得锁申请服务继续正常进行。大家思考一个极端的场景如图7所示,服务S1刚在Redis集群主节点申请到锁L1,锁L1还未同步到Redis集群从节点,此时Redis集群主节点挂掉,根据Redis集群的Sentinal哨兵机制,会把从节点提升为新Redis集群的主节点,而服务S2继续在新Redis集群的主节点申请锁L1,那么服务S2就会成功申请到锁L1。那么再次出现服务S1和服务S2在此时同时申请到锁L1的情况。那么为何会造成这样的情况,让我们从架构的本质(CAP模型)来深入分析下原因。
图6 分布式锁Redis集群主从模式
图7 分布式锁Redis集群主从模式主节点故障
我们要保证同一把分布式锁的申请在同一时刻只能有一个服务拿到此锁,因此从CAP模型底层分析,分布式锁是CP模型。而Redis集群的主从模式是AP模型。也就是说从架构设计哲学层面来看,分布式锁选用Redis集群的主从模式就是不优雅的,从而导致了上述一系列问题的出现。
但是,当在百度里搜索分布式锁,有很多的实现方案是基于Redis集群。为什么会是这样?我们继续深入分析。一切脱离场景谈架构都是耍流氓,特别是脱离业务场景。业务场景分为2类:追求数据强一致性场景、追求数据最终一致性场景。数据强一致性场景比如金融、电商交易等,使用分布式锁时需要使用CP模型,不然就会出现支付去重失败等重大问题,此时公司离破产只差用户一个大请求;数据最终一致性场景比如微信发消息等,在使用分布式锁时使用AP模型较优雅,比如对用户发送消息(今晚有空吗?约个饭)的去重,极端情况下使用分布式锁去重失败,也就是消息发送到对方2次,反而会增加彼此之间的感情,本来要拒绝邀请的,由于收到2次邀请消息,结果就不好意思拒绝了。
4.分布式锁设计与实践
分布式锁存储选型至关重要,以下对比了Redis、ZooKeeper、etcd等存储模型,如图8所示。通过以上的分析,对于数据一致性要求高的业务场景需使用CP型的存储模型。ZooKeeper多锁实现使用创建临时节点和Watch机制,在执行效率、扩展能力以及社区活跃度等方面低于etcd,因为选用基于etcd作为分布式锁的存储模型。
图8 分布式锁存储模型选型对比
分布式锁的架构设计如图8所示,由etcd存储集群、分布式锁客户端、监控平台等三部分构成。其中etcd集群负责锁的申请、续租、释放等操作处理,分布式锁客户端通过HTTP API对etcd集群进行操作,从而使得微服务调用方能够申请锁、对锁进行续租、对锁探活、锁操作的监控等。在部署层面,etcd集群至少需要部署3台,分布式锁客户端以SDK的方式嵌入到微服务中。
5.总结
从架构设计哲学层面分析,分布式锁本质上是CP模型。一切脱离场景谈架构设计都是耍流氓,因此我们需要针对业务场景的不同,选用优雅的分布式锁实现,在追求数据强一致性的业务场景中,选用CP存储模型,在追求数据最终一致性的业务场景中,选用AP存储模型。
特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:
长按订阅更多精彩▼如有收获,点个在看,诚挚感谢
基于CAP模型设计企业级真正高可用的分布式锁相关推荐
- 分布式为什么一定要有高可用的分布式锁?一线大厂必看!
" 现在面试都会聊到分布式系统,其中不免谈到分布式锁这块的知识,今天就来聊聊如何设计高可用的分布式锁. 作者:陈于喆,来自:51CTO技术栈 分布式锁定义 分布式锁在分布式环境下,锁定全局唯 ...
- 为什么多个线程不可能同时抢到一把锁_分布式为什么一定要有高可用的分布式锁?看完就知道了...
分布式锁定义 分布式锁在分布式环境下,锁定全局唯一公共资源,表现为: 请求串行化 互斥性 第一步是上锁的资源目标,是锁定全局唯一公共资源,只有是全局唯一的资源才存在多个线程或服务竞争的情况. 互斥性表 ...
- 视频教程- 19年录制Redis实战教程 高可用秒杀分布式锁布隆过滤器实战 SpringBoot教程整合-Java
19年录制Redis实战教程 高可用秒杀分布式锁布隆过滤器实战 SpringBoot教程整合 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有nod ...
- 【FastDFS】如何打造一款高可用的分布式文件系统?这次我明白了!!
写在前面 前面我们学习了如何基于两台服务器搭建FastDFS环境,而往往在生产环境中,需要FastDFS做到高可用,那如何基于FastDFS打造一款高可用的分布式文件系统呢?别急,今天,我们就一起来基 ...
- 亚马逊AWS在线系列讲座——基于AWS云平台的高可用应用设计
设计高可用的应用是架构师的一个重要目标,可是基于云计算平台设计高可用应用与基于传统平台的设计有很多不同.云计算在给架构师带来了很多新的设计挑战的时候,也给带来了很多新的设计理念和可用的服务.怎样在设计 ...
- 基于rhcs套件实现的高可用集群
1.基于rhcs套件实现nginx平台的高可用集群 实验环境: 1> server1 server5 集群节点为了节省节点我们还用了server1作为管理节点安装了luci图形管理: 2> ...
- 基于故障转移群集的高可用虚拟机解决方案
基于故障转移群集的高可用虚拟机解决方案 推荐 某医药公司现有1台服务器,安装Windows Server 2008 R2,运行专用管理软件,计算机上有USB加密狗.网络中有100个左右的工作站,安装专 ...
- 分布式架构中常见理论以及如何才能设计出高可用的分布式架构?
分布式架构中常见理论以及如何才能设计出高可用的分布式架构? 一.前言 我们就来聊一聊目前主流的分布式架构以及分布式架构中常见理论以及如何才能设计出高可用的分布式架构好了.分布式架构中,SOA和微服务架 ...
- Spring Cloud(十一)高可用的分布式配置中心 Spring Cloud Bus 消息总线集成(RabbitMQ)
上一篇文章,留了一个悬念,Config Client 实现配置的实时更新,我们可以使用 /refresh 接口触发,如果所有客户端的配置的更改,都需要手动触发客户端 /refresh ,当服务越来越多 ...
最新文章
- ArchSummit2018深圳站筹备中,18大专题征集演讲嘉宾
- 基于.NET2.0的System.Net.Mail发送邮件Demo
- php期末作业经验,期末作业.php
- 数据库管家----ADODB类库.
- 关于sizeof的一些东西
- 山东大学2016-2017校历
- Teamcenter 2007 之 Part 物件关联
- post接口请求测试,通俗易懂
- 向日葵远程控制第一人称3D鼠标无法操作旋转问题
- cx_oracle 字符编码,cx_oracle访问处理oracle中文乱码问题
- scratch迷宫小游戏
- 大神论坛 利用活跃变量分析来去掉vmp的大部分垃圾指令
- MacOS - 快捷键以及各种操作汇总
- crawler:AJAX动态网页数据抓取、Selenium使用
- win7 64位的 svchost.exe 占用内存过大的问题
- POJ 1265 Area(Pick定理)
- NYOJ273 字母小游戏
- 证件类型、证件号码、性别、出生日期校验(身份证、户口簿、港澳居民居住证、台湾居民居住证、港澳居民来往内地通行证、台湾居民来往大陆通行证、境外永久居住证、外国人永久居留身份证、护照、其他)
- 设计模式之备忘录模式(Memento Pattern)
- LDO(线性稳压器)设计检查
热门文章
- nginx alias php,Nginx Alias 无法解析PHP的解决办法
- Codeforces数学1600day3[数学CodeForces - 1213D2, CodeForces - 1165E 数论,CodeForces - 1165D 因子分解]
- 《计算机网络》常考概念、英文缩写、公式大全
- 【算法笔记】竞赛图(有向完全图)(相关题型总结)
- 模板 - 输入输出优化
- springmvc + springboot + mybatis java b2b2c电子商城系统源码...
- EntLib 3.1学习笔记(6) : Security Application Block
- 独立云计算服务商的多维实践之道:用户需求驱动变革
- GIT 换行符相关操作
- 花卉世界大观园和杂技之游