【分布式】2020面试必看

秒杀系统设计

什么是秒杀

通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动

业务特点

高并发:秒杀的特点就是这样时间极短、 瞬间用户量大。

库存量少:一般秒杀活动商品量很少,这就导致了只有极少量用户能成功购买到。

业务简单:流程比较简单,一般都是下订单、扣库存、支付订单

恶意请求,数据库压力大

解决方案

前端:页面资源静态化,按钮控制,使用答题校验码可以防止秒杀器的干扰,让更多用户有机会抢到

nginx:校验恶意请求,转发请求,负载均衡;动静分离,不走tomcat获取静态资源;gzip压缩,减少静态文件传输的体积,节省带宽,提高渲染速度

业务层:集群,多台机器处理,提高并发能力

redis:集群保证高可用,持久化数据;分布式锁(悲观锁);缓存热点数据(库存)

mq:削峰限流,MQ堆积订单,保护订单处理层的负载,Consumer根据自己的消费能力来取Task,实际上下游的压力就可控了。重点做好路由层和MQ的安全

数据库:读写分离,拆分事务提高并发度

秒杀系统设计小结

秒杀系统就是一个“三高”系统,即高并发、高性能和高可用的分布式系统
秒杀设计原则:前台请求尽量少,后台数据尽量少,调用链路尽量短,尽量不要有单点
秒杀高并发方法:访问拦截、分流、动静分离
秒杀数据方法:减库存策略、热点、异步、限流降级
访问拦截主要思路:通过CDN和缓存技术,尽量把访问拦截在离用户更近的层,尽可能地过滤掉无效请求。
分流主要思路:通过分布式集群技术,多台机器处理,提高并发能力。

 

分布式

分布式(distributed)是为了解决单个物理服务器容量和性能瓶颈问题而采用的优化手段,将一个业务拆分成不同的子业务,分布在不同的机器上执行。服务之间通过远程调用协同工作,对外提供服务。

该领域需要解决的问题极多,在不同的技术层面上,又包括:分布式缓存、分布式数据库、分布式计算、分布式文件系统等,一些技术如MQ、Redis、zookeeper等都跟分布式有关。

从理念上讲,分布式的实现有两种形式:

水平扩展:当一台机器扛不住流量时,就通过添加机器的方式,将流量平分到所有服务器上,所有机器都可以提供 相同的服务;

垂直拆分:前端有多种查询需求时,一台机器扛不住,可以将不同的业务需求分发到不同的机器上,比如A机器处理余票查询的请求,B机器处理支付的请求。

集群

集群(cluster)是指在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡设备对外提供服务。

两个特点

可扩展性:集群中的服务节点,可以动态的添加机器,从而增加集群的处理能力。

高可用性:如果集群某个节点发生故障,这台节点上面运行的服务,可以被其他服务节点接管,从而增强集群的高可用性。

两大能力

负载均衡:负载均衡能把任务比较均衡地分布到集群环境下的计算和网络资源。

集群容错:当我们的系统中用到集群环境,因为各种原因在集群调用失败时,集群容错起到关键性的作用。

微服务

微服务就是很小的服务,小到一个服务只对应一个单一的功能,只做一件事。这个服务可以单独部署运行,服务之间通过远程调用协同工作,每个微服务都是由独立的小团队开发,测试,部署,上线,负责它的整个生命周期。

多线程

多线程(multi-thread):多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。多线程是为了提高CPU的利用率。

高并发

高并发(High Concurrency)是一种系统运行过程中发生了一种“短时间内遇到大量请求”的情况,高并发对应的是访问请求,多线程是解决高并发的方法之一,高并发还可以通过分布式,集群,算法优化,数据库优化等方法解决。

分布式系统设计理念

分布式系统的目标与要素

分布式系统的目标是提升系统的整体性能和吞吐量另外还要尽量保证分布式系统的容错性(假如增加10台服务器才达到单机运行效果2倍左右的性能,那么这个分布式系统就根本没有存在的意义)。

即使采用了分布式系统,我们也要尽力运用并发编程、高性能网络框架等等手段提升单机上的程序性能。

分布式系统设计两大思路:中心化和去中心化

中心化设计

两个角色: 中心化的设计思想很简单,分布式集群中的节点机器按照角色分工,大体上分为两种角色: “领导” 和 “干活的”
角色职责: “领导”通常负责分发任务并监督“干活的”,发现谁太闲了,就想发设法地给其安排新任务,确保没有一个“干活的”能够偷懒,如果“领导”发现某个“干活的”因为劳累过度而病倒了,则是不会考虑先尝试“医治”他的,而是一脚踢出去,然后把他的任务分给其他人。其中微服务架构 Kubernetes 就恰好采用了这一设计思路。
中心化设计的问题
中心化的设计存在的最大问题是“领导”的安危问题,如果“领导”出了问题,则群龙无首,整个集群就奔溃了。但我们难以同时安排两个“领导”以避免单点问题。
中心化设计还存在另外一个潜在的问题,既“领导”的能力问题:可以领导10个人高效工作并不意味着可以领导100个人高效工作,所以如果系统设计和实现得不好,问题就会卡在“领导”身上。
领导安危问题的解决办法: 大多数中心化系统都采用了主备两个“领导”的设计方案,可以是热备或者冷备,也可以是自动切换或者手动切换,而且越来越多的新系统都开始具备自动选举切换“领导”的能力,以提升系统的可用性。
去中心化设计

众生地位平等: 在去中心化的设计里,通常没有“领导”和“干活的”这两种角色的区分,大家的角色都是一样的,地位是平等的,全球互联网就是一个典型的去中心化的分布式系统,联网的任意节点设备宕机,都只会影响很小范围的功能。
“去中心化”不是不要中心,而是由节点来自由选择中心。 (集群的成员会自发的举行“会议”选举新的“领导”主持工作。最典型的案例就是ZooKeeper及Go语言实现的Etcd)
去中心化设计的问题: 去中心化设计里最难解决的一个问题是 “脑裂”问题 ,这种情况的发生概率很低,但影响很大。脑裂指一个集群由于网络的故障,被分为至少两个彼此无法通信的单独集群,此时如果两个集群都各自工作,则可能会产生严重的数据冲突和错误。一般的设计思路是,当集群判断发生了脑裂问题时,规模较小的集群就“自杀”或者拒绝服务。
分布式与集群的区别是什么?

分布式: 一个业务分拆多个子业务,部署在不同的服务器上
集群: 同一个业务,部署在多个服务器上。比如之前做电商网站搭的redis集群以及solr集群都是属于将redis服务器提供的缓存服务以及solr服务器提供的搜索服务部署在多个服务器上以提高系统性能、并发量解决海量存储问题。


CAP定理

在理论计算机科学中,CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

选项    描述
Consistency(一致性)    指数据在多个副本之间能够保持一致的特性(严格的一致性)
Availability(可用性)    指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应(不保证获取的数据为最新数据)
Partition tolerance(分区容错性)    分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障
Spring Cloud在CAP法则上主要满足的是A和P法则,Dubbo和Zookeeper在CAP法则主要满足的是C和P法则

CAP仅适用于原子读写的NOSQL场景中,并不适合数据库系统。现在的分布式系统具有更多特性比如扩展性、可用性等等,在进行系统设计和开发时,我们不应该仅仅局限在CAP问题上。

注意:不是所谓的3选2(不要被网上大多数文章误导了)

现实生活中,大部分人解释这一定律时,常常简单的表述为:“一致性、可用性、分区容忍性三者你只能同时达到其中两个,不可能同时达到”。实际上这是一个非常具有误导性质的说法,而且在CAP理论诞生12年之后,CAP之父也在2012年重写了之前的论文。

当发生网络分区的时候,如果我们要继续服务,那么强一致性和可用性只能2选1。也就是说当网络分区之后P是前提,决定了P之后才有C和A的选择。也就是说分区容错性(Partition tolerance)我们是必须要实现的。

CAP定理的证明

关于CAP这三个特性我们就介绍完了,接下来我们试着证明一下为什么CAP不能同时满足。

为了简化证明的过程,我们假设整个集群里只有两个N1和N2两个节点,如下图:

img

N1和N2当中各自有一个应用程序AB和数据库,当系统满足一致性的时候,我们认为N1和N2数据库中的数据保持一致。在满足可用性的时候,我们认为无论用户访问N1还是N2,都可以获得正确的结果,在满足分区容错性的时候,我们认为无论N1还是N2宕机或者是两者的通信中断,都不影响系统的运行。

我们假设一种极端情况,假设某个时刻N1和N2之间的网络通信突然中断了。如果系统满足分区容错性,那么显然可以支持这种异常。问题是在此前提下,一致性和可用性是否可以做到不受影响呢?

我们做个假象实验,如下图,突然某一时刻N1和N2之间的关联断开:

img

有用户向N1发送了请求更改了数据,将数据库从V0更新成了V1。由于网络断开,所以N2数据库依然是V0,如果这个时候有一个请求发给了N2,但是N2并没有办法可以直接给出最新的结果V1,这个时候该怎么办呢?

这个时候无法两种方法,一种是将错就错,将错误的V0数据返回给用户。第二种是阻塞等待,等待网络通信恢复,N2中的数据更新之后再返回给用户。显然前者牺牲了一致性,后者牺牲了可用性。

这个例子虽然简单,但是说明的内容却很重要。在分布式系统当中,CAP三个特性我们是无法同时满足的,必然要舍弃一个。三者舍弃一个,显然排列组合一共有三种可能。

BASE理论

BASE理论由eBay架构师Dan Pritchett提出,在2008年上被分表为论文,并且eBay给出了他们在实践中总结的基于BASE理论的一套新的分布式事务解决方案。

BASE 是 Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性) 三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的,它大大降低了我们对系统的要求。

BASE理论的核心思想

即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。也就是牺牲数据的一致性来满足系统的高可用性,系统中一部分数据不可用或者不一致时,仍需要保持系统整体“主要可用”。

针对数据库领域,BASE思想的主要实现是对业务数据进行拆分,让不同的数据分布在不同的机器上,以提升系统的可用性,当前主要有以下两种做法:

按功能划分数据库
分片(如开源的Mycat、Amoeba等)。
由于拆分后会涉及分布式事务问题,所以eBay在该BASE论文中提到了如何用最终一致性的思路来实现高性能的分布式事务。

BASE理论三要素

1. 基本可用

基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。但是,这绝不等价于系统不可用。

比如:

响应时间上的损失:正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
2. 软状态

软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时

3. 最终一致性

最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

分布式锁的三种实现方式

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。

基于数据库实现分布式锁;  基于缓存(Redis等)实现分布式锁;  基于Zookeeper实现分布式锁;

尽管有这三种方案,但是不同的业务也要根据自己的情况进行选型,他们之间没有最好只有更适合!

基于数据库的实现方式

基于数据库的实现方式的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

(1)创建一个表:

(2)想要执行某个方法,就使用这个方法名向表中插入数据:

(3)成功插入则获取锁,执行完成后删除对应的行数据释放锁:

注意:这只是使用基于数据库的一种方法,使用数据库实现分布式锁还有很多其他的玩法!

使用基于数据库的这种实现方式很简单,但是对于分布式锁应该具备的条件来说,它有一些问题需要解决及优化:

1、因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能,所以,数据库需要双机部署、数据同步、主备切换;

2、不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;

3、没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;

4、不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。

5、在实施的过程中会遇到各种不同的问题,为了解决这些问题,实现方式将会越来越复杂;依赖数据库需要一定的资源开销,性能问题需要考虑。

基于Redis的实现方式

1、选用Redis实现分布式锁原因:

(1)Redis有很高的性能; (2)Redis命令对此支持较好,实现起来比较方便

2、使用命令介绍:

(1)SETNX

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

 

(2)expire

expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

 

(3)delete

delete key:删除key

 

在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

3、实现思想:

(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

 

基于ZooKeeper的实现方式

ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个目录mylock; (2)线程A想获取锁就在mylock目录下创建临时顺序节点; (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,

redis和zk实现分布式锁的区别

1.redis分布式锁,需要自己不断去尝试获取锁,比较消耗性能

2.zk分布式锁,注册个监听器即可,不需要不断去尝试获取锁,性能开销小

3.redis获取锁的那个客户端挂了,就只能等超时时间之后才能释放锁,而zk的话,因为是创建的临时节点,只要客户端挂了,znode就没了,此时就自动释放锁

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值