一些分布式相关问题

(分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储MongoDB、高并发系统架构的组成)

分布式缓存

项目中使用缓存可以做到:高性能(把复杂耗时操作结果缓存起来),高并发(高额的请求,在进入数据库前缓冲下)
常见缓存问题:双写不一致、缓存雪崩、缓存穿透、缓存并发竞争

分布式锁

  • redis和zookeeper的分布式锁的使用,及优缺点?

redis分布式锁
有一个官方RedLock算法,是redis官方支持的分布式锁算法。重要的是三点:互斥(只有一个客户端获取锁),不死锁,容错

zk分布式锁
就是某节点尝试创建znode,此时创建成功就获取锁;别的客户端创建锁会失败,只能注册个监听器监听这个锁。释放锁就删除这个znode,通知客户端,别的等待中的客户端就可以重新加锁。

分布式session

tomcat + redis
类似以前的session代码一样,基于tomcat原生session支持即可。利用Tomcat RedisSessionManager部署session存到redis中

springsession + redis
上方策略严重依赖web容器且代码移植性弱。
基于java一站式解决方案,spring包掉大部分使用的框架,springcloud做微服务mspringboot做脚手架,所以使用spring session
给spring session配置基于redis来存储session数据,然后配置了一个spring session的过滤器;这样的话,session相关操作都交由spring session来管理。接着在代码中,操作原生session操作即可,基于springsession从redis取数据。

分布式事务

准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

  • 思路一:两段式提交
    准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

所以这个就是所谓的XA事务,两阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复ok,那么就正式提交事务,在各个数据库上执行操作;如果任何一个数据库回答不ok,那么就回滚事务。
这种分布式事务方案,比较适合单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。如果要玩儿,那么基于spring + JTA就可以搞定,自己随便搜个demo看看就知道了。

这个方案不现实,一般来说某个系统内部如果出现跨多个库的这么一个操作,是不合规的。我可以给大家介绍一下, 现在微服务,一个大的系统分成几百个服务,几十个服务。一般来说,我们的规定和规范,是要求说每个服务只能操作自己对应的一个数据库。

如果你要操作别的服务对应的库,不允许直连别的服务的库,违反微服务架构的规范,你随便交叉访问,导致服务乱套,这样是没法管理与治理的,经常数据被别人改错,自己的库被别人写挂。如果你要操作别人的服务的库,你必须是通过调用别的服务的接口来实现,绝对不允许你交叉访问别人的数据库!

  • 思路二:TCC方案
    准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

这个其实是用到了补偿的概念,分为了三个阶段:
1)Try阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留
2)Confirm阶段:这个阶段说的是在各个服务中执行实际的操作
3)Cancel阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作

比较适合的场景:这个就是除非你是真的一致性要求太高,是你系统中核心之核心的场景,比如常见的就是资金类的场景,那你可以用TCC方案了,自己编写大量的业务逻辑,自己判断一个事务中的各个环节是否ok,不ok就执行补偿/回滚代码。

简而言之,就是需要自己手写回滚代码经行补偿,代码量不仅增加,还难以维护。

  • 思路三:可靠消息最终一致性方案

这个的意思,就是干脆不要用本地的消息表了,直接基于MQ来实现事务。比如阿里的RocketMQ就支持消息事务。
准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...
大概的意思就是:
1)A系统先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作别执行了
2)如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉mq发送确认消息,如果失败就告诉mq回滚消息
3)如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务
4)mq会自动定时轮询所有prepared消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认消息?那是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,别确认消息发送失败了。
5)这个方案里,要是系统B的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿

这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用RocketMQ支持的,要不你就自己基于类似ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的。

  • 思路四:最大努力通知方案
    准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...
    这个方案的大致意思就是:

1)系统A本地事务执行完之后,发送个消息到MQ
2)这里会有个专门消费MQ的最大努力通知服务,这个服务会消费MQ然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统B的接口
3)要是系统B执行成功就ok了;要是系统B执行失败了,那么最大努力通知服务就定时尝试重新调用系统B,反复N次,最后还是不行就放弃

假如,我们现在想保证我们的某个系统非常的可靠,任何一个数据都不能错,我们用的是微服务架构,几十个服务。结果我们一盘点,发现,如果到处都要搞的话,一个系统要做几十个分布式事务出来。
我们的经验,我带几十人的team,最大的一个项目,起码几百个服务,复杂的分布式大型系统,里面其实也没几个分布式事务。
你其实用任何一个分布式事务的这么一个方案,都会导致你那块儿代码会复杂10倍。很多情况下,系统A调用系统B、系统C、系统D,我们可能根本就不做分布式事务。如果调用报错会打印异常日志。
每个月也就那么几个bug,很多bug是功能性的,体验性的,真的是涉及到数据层面的一些bug,一个月就几个,两三个?如果你为了确保系统自动保证数据100%不能错,上了几十个分布式事务,代码太复杂;性能太差,系统吞吐量、性能大幅度下跌。
99%的分布式接口调用,不要做分布式事务,直接就是监控(发邮件、发短信)、记录日志(一旦出错,完整的日志)、事后快速的定位、排查和出解决方案、修复数据。
每个月,每隔几个月,都会对少量的因为代码bug,导致出错的数据,进行人工的修复数据,自己临时动手写个程序,可能要补一些数据,可能要删除一些数据,可能要修改一些字段的值。
比你做50个分布式事务,成本要来的低上百倍,低几十倍
trade off,权衡,要用分布式事务的时候,一定是有成本,代码会很复杂,开发很长时间,性能和吞吐量下跌,系统更加复杂更加脆弱反而更加容易出bug;好处,如果做好了,TCC、可靠消息最终一致性方案,一定可以100%保证你那快数据不会出错。
1%,0.1%,0.01%的业务,资金、交易、订单,我们会用分布式事务方案来保证,会员积分、优惠券、商品信息,其实不要这么搞了

分布式搜索ES——ElasticSearch

底层还是lucene。核心思想:在多台机器上启动多个es进程实例,组成es集群,es在存储数据的基本单位是索引
index—>type—>mapping—>document—>field
(库)       (表)       (表的定义)       (行)

  • ES写数据
    1)客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)
    2)coordinating node,对document进行路由,将请求转发给对应的node(有primary shard)
    3)实际的node上的primary shard处理请求,然后将数据同步到replica node
    4)coordinating node,如果发现primary node和所有replica node都搞定之后,就返回响应结果给客户端

  • ES读数据
    查询,GET某一条数据,写入了某个document,这个document会自动给你分配一个全局唯一的id,doc id,同时也是根据doc id进行hash路由到对应的primary shard上面去。也可以手动指定doc id,比如用订单id,用户id。
    你可以通过doc id来查询,会根据doc id进行hash,判断出来当时把doc id分配到了哪个shard上面去,从那个shard去查询
    1)客户端发送请求到任意一个node,成为coordinate node
    2)coordinate node对document进行路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica中随机选择一个,让读请求负载均衡
    3)接收请求的node返回document给coordinate node
    4)coordinate node返回document给客户端

  • ES搜索数据
    1)客户端发送请求到一个coordinate node
    2)协调节点将搜索请求转发到所有的shard对应的primary shard或replica shard也可以
    3)query phase:每个shard将自己的搜索结果(其实就是一些doc id),返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果
    4)fetch phase:接着由协调节点,根据doc id去各个节点上拉取实际的document数据,最终返回给客户端

  • 搜索的底层原理,倒排索引,画图说明传统数据库和倒排索引的区别

  • 写数据底层原理

1)先写入buffer,在buffer里的时候数据是搜索不到的;同时将数据写入translog日志文件

2)如果buffer快满了,或者到一定时间,就会将buffer数据refresh到一个新的segment file中,但是此时数据不是直接进入segment file的磁盘文件的,而是先进入os cache的。这个过程就是refresh。

每隔1秒钟,es将buffer中的数据写入一个新的segment file,每秒钟会产生一个新的磁盘文件,segment file,这个segment file中就存储最近1秒内buffer中写入的数据

但是如果buffer里面此时没有数据,那当然不会执行refresh操作咯,每秒创建换一个空的segment file,如果buffer里面有数据,默认1秒钟执行一次refresh操作,刷入一个新的segment file中

操作系统里面,磁盘文件其实都有一个东西,叫做os cache,操作系统缓存,就是说数据写入磁盘文件之前,会先进入os cache,先进入操作系统级别的一个内存缓存中去

只要buffer中的数据被refresh操作,刷入os cache中,就代表这个数据就可以被搜索到了

为什么叫es是准实时的?NRT,near real-time,准实时。默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。

可以通过es的restful api或者java api,手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。

只要数据被输入os cache中,buffer就会被清空了,因为不需要保留buffer了,数据在translog里面已经持久化到磁盘去一份了

3)只要数据进入os cache,此时就可以让这个segment file的数据对外提供搜索了

4)重复1~3步骤,新的数据不断进入buffer和translog,不断将buffer数据写入一个又一个新的segment file中去,每次refresh完buffer清空,translog保留。随着这个过程推进,translog会变得越来越大。当translog达到一定长度的时候,就会触发commit操作。

buffer中的数据,倒是好,每隔1秒就被刷到os cache中去,然后这个buffer就被清空了。所以说这个buffer的数据始终是可以保持住不会填满es进程的内存的。

每次一条数据写入buffer,同时会写入一条日志到translog日志文件中去,所以这个translog日志文件是不断变大的,当translog日志文件大到一定程度的时候,就会执行commit操作。

5)commit操作发生第一步,就是将buffer中现有数据refresh到os cache中去,清空buffer

6)将一个commit point写入磁盘文件,里面标识着这个commit point对应的所有segment file

7)强行将os cache中目前所有的数据都fsync到磁盘文件中去

translog日志文件的作用是什么?就是在你执行commit操作之前,数据要么是停留在buffer中,要么是停留在os cache中,无论是buffer还是os cache都是内存,一旦这台机器死了,内存中的数据就全丢了。

所以需要将数据对应的操作写入一个专门的日志文件,translog日志文件中,一旦此时机器宕机,再次重启的时候,es会自动读取translog日志文件中的数据,恢复到内存buffer和os cache中去。

commit操作:1、写commit point;2、将os cache数据fsync强刷到磁盘上去;3、清空translog日志文件

8)将现有的translog清空,然后再次重启启用一个translog,此时commit操作完成。默认每隔30分钟会自动执行一次commit,但是如果translog过大,也会触发commit。整个commit的过程,叫做flush操作。我们可以手动执行flush操作,就是将所有os cache数据刷到磁盘文件中去。

不叫做commit操作,flush操作。es中的flush操作,就对应着commit的全过程。我们也可以通过es api,手动执行flush操作,手动将os cache中的数据fsync强刷到磁盘上去,记录一个commit point,清空translog日志文件。

9)translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下,可能有5秒的数据会仅仅停留在buffer或者translog文件的os cache中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。

实际上你在这里,如果面试官没有问你es丢数据的问题,你可以在这里给面试官炫一把,你说,其实es第一是准实时的,数据写入1秒后可以搜索到;可能会丢失数据的,你的数据有5秒的数据,停留在buffer、translog os cache、segment file os cache中,有5秒的数据不在磁盘上,此时如果宕机,会导致5秒的数据丢失。

如果你希望一定不能丢失数据的话,你可以设置个参数,官方文档,百度一下。每次写入一条数据,都是写入buffer,同时写入磁盘上的translog,但是这会导致写性能、写入吞吐量会下降一个数量级。本来一秒钟可以写2000条,现在你一秒钟只能写200条,都有可能。

10)如果是删除操作,commit的时候会生成一个.del文件,里面将某个doc标识为deleted状态,那么搜索的时候根据.del文件就知道这个doc被删除了

11)如果是更新操作,就是将原来的doc标识为deleted状态,然后新写入一条数据

12)buffer每次refresh一次,就会产生一个segment file,所以默认情况下是1秒钟一个segment file,segment file会越来越多,此时会定期执行merge

13)每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segment file。

es里的写流程,有4个底层的核心概念,refresh、flush、translog、merge

当segment file多到一定程度的时候,es就会自动触发merge操作,将多个segment file给merge成一个segment file。

分布式解决方案 Dubbo

  • Dubbo的缺点,过分依赖zookeeper,就是过分依赖注册中心。在微服务中,应该做到各司其职,就是注册中心服务网关,配置中心,三者不应该耦合度那么高。【与springcloud的对比:注册中心——Eureka、服务网关——Zuul、配置中心——SpringCloud Config】
    微服务理应各个服务解耦,独立不相关的。

【面试点】dubbo是如何实现负载均衡的?dubbo需要动态获取服务列表,才能调用服务 。

  • Dubbo的容错策略,没有细粒度到方法级别上;负载均衡则可以确定到方法上,导致使用上有一定的别扭。

  • 引申出的问题:一个服务,如果都是查询,配置成failover;若有添加或修改,一定要配置成failfast
    目前Dubbo的集群容错策略,还没有能够细粒度到方法上

  • Dubbo在微服务的地位
    Dubbo更擅长Tcp/Ip协议,虽然支持http协议,但是那样就是相当于一个单机的传统业务,类似部署在tomcat中了。

dubbo所暴露的服务,一般情况下是不支持浏览器的。服务面向tcp/ip的客户端

准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

Dubbo多用于内网集群间通讯,解决服务集群之间的调用的,然后Application/Controller控制层想要调用服务,连接到的是注册中心

用户访问的是Nginx,然后访问应用,再访问注册中心…这个过程之中,用户是感觉不出来使用了dubbo。dubbo擅长构建内网的,基于tcp/ip通讯的集群

Dubbo是将原来应用中的service剥离出来,然后暴露出来,而controller没变。MVC三层中的Model层进行了剥离拆分。

而SpringCloud才是擅长分离应用层/控制层。综上dubbo和springcloud各有擅长的领域。dubbo擅长内网基于模型,业务层的拆分、springcloud则是擅长对控制层拆分。dubbo主内,springcloud主外。

又因为业务层主要是内部通讯,毋须面向用户,且使用tcp/ip层协议能更好的提升性能,选择Dubbo;对外使用springcloud,对controller进行拆分,再用上分布式的Nosql,就是全栈分布式

分布式解决方案 SpringCloud

  • 什么是微服务?
    基于SOA架构的一种架构,系统中各个微服务可被独立部署,微服务间解耦合,各服务单一职责。
    特点是:分布式业务拆分
    微服务的设计原则:AFK拆分原则(x轴水平扩展,y轴垂直拆分)、前后端分离原则无状态服务(把有状态业务变成无状态计算类服务,将状态数据放到分布式缓存中存储,这样微服务的动态增删不需在考虑数据同步)、RestFul通讯风格

微服务的4个设计原则和19个解决方案http://p.primeton.com/articles/59b0f9244be8e61fea00be67

思路:基于服务名,可以查询服务列表,再根据负载均衡策略,选择一个服务,这样实现对controller的负载均衡调用;那如果想要实现自动的服务发现与注册,引入注册中心的概念。去把服务和服务列表注册到某个路径下,日后可以查询。

  • Ribbon的作用?
    其实就是操纵了spring中的工具类RestTemplate,去修改RestTemplate中的调用服务列表的地址,做到来回的切换地址,这样就实现了简单的负载均衡。

只有Eureka是AP,其他学到的Nosql和ZK都是CP,留了可用性,就无法保证一致性。

  • Eureka与Ribbon,实现负载均衡的思路:
    (SpringCloud负载中心的本质),本质是修改RestTemplate的拦截器,在拦截器中集成了Eureka,Eureka去查询服务列表,拿到服务列表之后,实现了一套负载均衡策略,默认轮询。

  • 熔断与容错
    熔断:当调用超时时,进行熔断处理
    容错:当调用失败时,进行容错处理

  • SpringCloud是利用NetFilx的Hystrix实现类来实现熔断,对加注解的controller方法做一个环绕通知,切到HystrixCommand方法实现中,容错/熔断就走getFallback方法

  • 熔断器的使用简述:
    引入依赖、入口类上加注解(@EnableCircuitBreaker等价于@EnableHystrix)、要切的方法上加上注解(@HystrixCommand)

  • SpringCloudConfig
    未来开发项目,想要动态变更参数配置, 将这个类写成组件bean,在bean类上加@RefreshScope,这样Spring在生产这个bean的时候,会基于这个bean生成一个代理对象,对类中的所有方法进行代理,当执行刷新方法的时候;它会重新对bean的代理对象bean参数赋值。

  • Bus 消息总线

  • Zuul 服务网关
    网关就相当于代理,可以把服务映射为对应url,因为网络服务太多,那需要统一一个入口,让用户借由网关访问后台服务,这样性能会有损耗。好处是可以规划服务,所有请求访问网关入口下的url,可以将后台集群规模隐藏。另一个好处是,提供Filter功能,边缘化服务。

疑问:为什么不使用Nginx来做网关呢? Nginx无法搭建集群(只有概念,没有实操)

  • 根据点击做推荐+flume采集示例:
    搭建完网关后,请求走的是zuul网关,这样可以利用filter可以拿到request请求,可以作统计处理。就是发的请求,会有固定的一串东西,最后拿到东西的id,可以将点击传给flume,后台就可以统计并记录。 打分同理:拿到xxxScore的url,将打分存储起来,可以对登录用户基于打分进行推荐了。

  • 权限管理示例:
    网关filter可以拿到request,那当然也可以拿到对应的session,也就能拿到对应用户。
    服务边缘化:springcloud在开发微服务的过程中,可以对服务进行拆分,但是服务对外界的形式需要是网关形式,任何请求不可以对内直接调用,内网架构不能向外暴露,对用户暴露的只可以是网关。
    创建网关,在网关加上权限控制,每个业务互相访问,先走的一定是网关。这样可以防止非法的服务访问。


  • 总结:
    Ribbon:负载均衡,原理是利用给restTemplate增加拦截器,拦截器修改访问的ip和port;
    Eureka:Ribbon一般需要和Eureka集成,拿到服务所对应的服务列表,随机的去调,修改方式是在bean工厂中注入一个xxx实现类,有不同的策略 轮询或随机,两者结合就可以实现功能了;
    Hystrix:断路器,原理是借助Netfilx的Hystrix工具包,通过异步的形式启动,主线程负责计时,计算子线程请求处理时间,超时熔断,子线程失败容错。Hystrix可以用在服务端,也可以用在调用端,实现原理就是aop,对加注解的方法做环绕通知,起到熔断容错策略。
    Config:配置服务器,可以集中应用中的配置。目前可以将配置放到git上。配置服务器实现动态刷新的原理是:在服务启动时,配置的bean会通过代理,刷新赋值。
    Bus:消息总线,让配置服务器的提供方和引用方,都注册到消息队列当中,这样执行任意一台,busrefrush可以更新所有节点
    Zuul:服务网关,作用是屏蔽后台服务,对外提供一个统一的入口,类似于Nginx代理服务器

分布式存储MongoDB

  • 一些小tips
    结构大致是:库 —— 集合 —— 记录
    集合大小不限,单条记录16MB限制;大于16MB用GridFS存储大文件。

MongoDB适合存储大尺寸,低价值数据,比如归档数据,指的是旧数据

  • 一些小tips2
    Mongodb中,固定集合capped的使用,例如轮播图
    Mongodb DML中一些注意点:

save和insert区别:
在没有_id的情况下,两者一样都是自动产生_id的插入;
当给定_id时,insert是插入,save则可能是更新

find和findone区别:
findone返回的是json文档,就是一个变量;
find返回游标,效果是这能遍历一次。

空值查询:利用type类型null,也可以利用exist存在false

分页中size和count的区别:
size:返回游标大小,受分页参数影响
count:返回的是查询匹配的记录总条数

  • 一些小tips3

MongoDB 的 MapReduce:底层也是拥有shuffle的,区别别于MapReduce,mongo的只能统计存储在它自己上的数据,无法统计第三方数据。mongo的统计可以处理业务中数据统计情况。
同时MongoDB支持数据的导出,它的导出格式为json,之后可以对接Hive做数据的清洗。直接交给MapReduce进行计算。

索引:
什么是索引?它是独立在数据之外的一种对数据的排列方式,可以优化搜索的一种机制。

GridFS 网格式存储
由一个个255KB的chunks构成,明显要小于HDFS的128MB。存储在磁盘上,不太占用内存。Hadoop存储海量数据,是用来做MapReduce分析,而MongoDB的海量存储,多数是用来存储流媒体,流媒体的好处是以细小粒度切割文件,所以那些在线缓冲的视频,的数据来源于此

  • 一些小tips4
    准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...
传统数据可以架构 MySQL
  • MS架构
    好处:读写分离,实现数据冗余备份,可以按照操作类型提升读写能力
    实现思路:就是配置多数据源,进行切换
    • 方案一:数据库驱动层,改变驱动类com.mysql.jdbc.ReplicationDriver,new一下就可以了,设置下参数即可。
    • 方案二:切换数据源方式
      继承AbstractRountingDataSource,去覆盖LookupKey()方法,通过ThreadLocal那当前操作的读写方式,然后在aop当中,切面方法,读取方法上的注解,判断是读是写。

本质是路由,根据配置的方法,选择是主库还是从库的数据源,起到代理作用。

缺点:单机写故障,单机存储瓶颈

  • MM架构

双主架构,每台机器即是Master,又是Slave,彼此拷贝彼此,可以解决单机写故障问题。

  • 分片/分库
    海量数据情况,提升写能力 —— MyCat真正使用场景
    强调分区存储,一级路由找到对应shard片区,二级路由选择读/写。
    一级路由继续实现切换数据源方法,三个DataSource相当于主从集群,一级路由对二级路由封装,就是请求进来先选择datasource,基于主键key选择,到达二级路由,选择读或写。
    mycat可以很好的实现一级二级路由。分片,缺点是无法做出需要全部整合的操作,如分页,如排序

高并发系统架构的组成

  1. 系统拆分:将一个系统拆分成若干子系统,例如dubbo来搞定;然后每个系统连接一个数据库,做到多数据库抗高并发。
  2. 缓存:高并发场景,一般读多写少,就是让读请求走缓存。redis可以做到单机几万并发
  3. MQ:高并发场景,业务操作频繁操作数据库,redis无法承载,且数据格式简单无事务;所以使用MQ,大量请求堆积在MQ中,排好队后,慢慢写调,控制在mysql承载范围内。
    所以考虑在项目里,那些承载复杂写业务逻辑的场景里,如何用MQ来异步写,提高并发性。MQ单机抗几万并发也是ok的。
  4. 分库分表:数据库也要高并发,对数据库进行拆分,拆库抗高并发,拆表提升sql性能。
  5. 读写分离:数据库多是读多写少,每必要所有请求集中在一个库上。
    使用主从架构,主写从读,读大流量时动态添加更多从库。
  6. ES:分布式搜索,天然分布式支持高并发。可以一些比较简单的查询、统计类的操作,可以考虑用es来承载,还有一些全文搜索类的操作,也可以考虑用es来承载。

其实实际上在真正的复杂的业务系统里,做高并发要远远比我这个图复杂几十倍到上百倍。你需要考虑,哪些需要分库分表,哪些不需要分库分表,单库单表跟分库分表如何join,哪些数据要放到缓存里去啊,放哪些数据再可以抗掉高并发的请求,你需要完成对一个复杂业务系统的分析之后,然后逐步逐步的加入高并发的系统架构的改造,这个过程是务必复杂的,一旦做过一次,一旦做好了,你在这个市场上就会非常的吃香。
其实大部分公司,真正看重的,不是说你掌握高并发相关的一些基本的架构知识,架构中的一些技术,RocketMQ、Kafka、Redis、Elasticsearch,高并发这一块,单单有技术的人才。
真正想要的是,对一个有几十万行代码的复杂的分布式系统,一步一步架构、设计以及实践过高并发架构的人,这个经验是难能可贵的。

准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

  • 技术对应:
    高并发分流 —— Nginx —— Tengine
    高并发缓存 —— Redis、Memcached —— Tair
    分布式消息队列 —— RabbitMQ、Kafka —— Notify、MetaQ
    分布式存储 —— ShardingJDBC、MongoDB —— 分布式数据库
    分布式协调工具 —— Zookeeper、Dubbo —— 分布式服务HSF(最新一代RPC框架HSF,全称High Speed Framework)
    分布式通信框架
    Docker容器技术

准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

  • 互联网技术架构图
    准备的一些 "分布式" 相关问题 (分布式缓存、分布式锁、分布式session、分布式事务、分布式搜索、Dubbo与SpringCloud、分布式存储)...

请求先经过LVS或F5硬件,进行负载均衡,然后交给Nginx集群【作用seesion分布式共享,Nginx本地缓存,动静分离】,然后真正的请求去到API网关,根据验证和参数情况调控,这时会用到中间件技术,如缓存服务,搜索服务,MQ发送到微服务集群中等,如果可以从缓数据,就直接走缓存;搜索引擎同理,之后未被处理的请求会来到微服务集群。这一切一切是为了减轻后端存储服务的压力【传统DB、文件存储、NoSql】。其他还有些组件如配置中心、服务注册发现、服务编排,为一系列业务服务。

相关文章: