一.概述
高并发就是系统在大流量的情况下保证对客户提供持续稳定的访问。
高并发解法:水平&垂直拆分,读写分离,高性能网络IO模型,负载均衡,缓存,异步,限流&熔断&超时控制&故障转移(悲观保系统可用性),池化(连接池或内存块池化)。
高并发核心原则:优先将流量挡在前置结点。
而在设计一个高并发系统的时候,需要做以下事情:
1.定指标
需要根据业务制定相应的高并发指标,通常是:tps,qps,以及访问性能的指标如99%的访问在100ms以内。
2.根据指标进行架构选型
通常qps如果较大,则考虑做水平或垂直拆分,或加缓存等方案。
3.落地架构
4.性能压测
5.不符合做性能优化
二.高并发涉及点
1.整体全链路优化点
2.优化点细分
2.1.cdn
2.1.解决的问题
对于像图片,视频类的资源我们如果每次都从后端服务器拿,一方面会占用后端服务器网络带宽这样会减少在一定服务器的情况下整体并发吞吐量。另外一方面由于网络传输慢,所以对于用户也会有卡顿的体验。
CDN就是将这些资源能缓存在离用户近的结点。
2.1.2.如果使用
CDN核心使用关键是是2个点:
a.用户如何访问CDN
对于用户来讲我们对他提供的还是我们自已的域名,这样用户就不需要感知具体ip或cdn运营商的域名,cdn运营商的CDN可能后面我们会换。
通过CNAME进行域名解析配置相应的CDN域名。注:域名解析通常有A记录和CNAME。A记录就是解析到具体ip。
b.如何更新CDN资源
通常CDN服务商会提供相应的更新接口。
当然还可以用静态资源版本号去做管理,这样就不涉及cdn的更新了。
2.1.3.完整dns请求链路
1.整体服务器或浏览器或app先看下本地local host是否有域名映射ip,有则直接使用
服务器下就是etc host对应域名ip映射配置
2.看下服务器或浏览器对应的dns缓存,有则使用
3.由于服务器或浏览器没有请求域名的dns相关信息,所以转给路由器,路由器会看下具体是否有dns缓存,没有转交给运营商的dns
4.运营商的dns如果也没有,则转给根域名,根域名发现没有,还会转给顶级域(如com),然后再转给具体区域域名解析服务器(如某个公司二级域名at.com)
所以整体域名解析是比较耗时的,然后也看到很多结点都会有缓存,如果需要优化性能可以考虑在app启动的时候能够预先进行域名解析缓存可以采用LRU缓存。然后需要设置下过期时期以及相应的域名对应ip变更的通知机制。
2.2.缓存
2.3.负载均衡
在设计高并发服务器的时候,我们希望整体的服务器是无状态的,能够在有大流量的时候能够快速进行扩容。
2.3.1分类
DNS,CDN,IP负载均衡。
DNS负载均衡:
就是设置域名有多个ip记录。通常设置成对应的nginx集群ip。
这种负载不太灵活,且没有及时的心跳健康检测所以无法做到失效转移。。
CDN负载均衡:
内容分发到CDN,将用户请求能够路由到最近的服务器。需要运营商提供服务。需要支付一定的费用。由于多了层CDN缓存所以适合一定对时效性不敏感的场景,通常相对静态的,如商品图片这些可适用,同时费用这块也是一个考量。
IP负载:
通过tcp/ip实现负载。
分为硬件负载,软件负载。
硬件负载:
如F5.
优点是单个负载能力性能较高,包含了负载均衡,安全性功能(如sync攻击);但缺点是价格较高。
原理,客户端请求VIP地址先经过F5,F5通过修改数据包的目的ip地址,然后将请求转发到后端服务器,后端服务器处理完再返回F5,F5修改数据包的源IP地址为Vip地址,然后返回客户端。这样整体一个请求就完成了。
软件负载:
从所属网络协议层次来分,会有七层负载,四层负载。
七层负载,比如nginx,就是基于http的转发来实现。客户访问的是nginx服务器ip。然后由nginx服务器转发到后端,后端返回结果后再返回到nginx服务器。
四层负载,如LVS。实现原因就是通过修改链路层的mac地址及目标ip地址来进行转发到后端服务器。
2.3.2.负载均衡算法
常见的有:
轮循,随机,权重,就近,基于服务器处理响应时间,最小任务数(即负载均衡每交给后端服务器就对这个服务器的任务数加1,返回时再做减1)。
2.3.水平&垂直拆分
水平及垂直拆分解决大流量的请求问题。
垂直拆分:
垂直拆分主要是从业务角度去切,而切的对象可以是服务器,也可以是DB,表。
垂直拆分能同时解决读写瓶颈问题。
垂直拆分本质上底层的服务职责,DB库的职责,表的职责从业务上进行了划分。
服务的垂直拆分示例:
比如原先一个单体应用包含了交易,商品等。当流量比较大的时候单体应用无论是服务器压力,还是网络带宽都会成为瓶颈。此时垂直拆分就是按业务维度去拆,根据业务域拆分成交易,商品,库存等多个微服务。
DB的拆分同样可以以业务去拆分成交易库,商品库。而再细化一点,如果是商品表存储,此时垂直拆的话就相当于将原先商品中的核心字段保留(商品名称,所属商家),而有些低频或数据占用较大的部分单拆出一个扩展表(如商品图片,描述等)。
水平拆分:
水平拆分通常用于对于服务器,DB,表按某种非业务的维度去拆。
水平拆分库可以解决读写压力问题;水平在单库中拆表对于写的压力减少有限,通常用于缓解读的压力,mysql一般2000万单表数据就会出现读的性能下降,其底层原因还是单表2000万数据的索引会比较大,导致mysql内存放不下,从而与磁盘进行索引层面搜索的交互。
比如我们做了业务垂直拆分后,发现商品库数据量还是很大,此时我们就需要对商品库做水平维度的分库分表。
数据库水平拆分详细:
通常数据库进行水平拆分我们需要考虑3个问题:
核心的原则是主要客户使用场景要能路由到。
1.拆分维度,分库分表键
范围key:
比如采用时间进行分区。这种一般应用于并发量小的情况。因为范围本身容易产生热点。
hash key:
优:相对比较分散,无热点问题。缺:比较简单的hash后续在扩容的时候存在数据迁移量问题,可以考虑一致性hash。
通常以记录的id做分区。
通常可以以id键的后几位做分库分表,比如我们的主要客户是商家那么记录id键可以以买家后几位做为分区键。这样做的好处是买家过来查能够从买家信息中拿到分区键,并且其它场景可以直接根据id的后几位做分区键路由。
这部分可以采用2分裂变一致性hash的方法进行分区key的设计,好处是后面扩容只需要拷贝原先一半的数据。
详细可以参考:业务订单分库分表二-扩容_剑八-的博客-CSDN博客
出现热点的解决方案:
在选择分区key的时候就要尽量选好分区算法,将数据分散。如果以商户维度一般都会有热点,所以一般交易订单是按照id去做key(有些设计为了兼容买家查询会将买家id的分库分表信息冗余到id中)。
如果出现了首先考虑做数据归档。
表记录分区信息:
优:灵活(有些根据上述2种分区都不均匀的话,可以用表记录分区信息进行分区),缺:需要多查询一下分区信息。
比如用户表以表记录分区信息这种方式进行分库分表,然后我们分成5个库,这时我们在【表记录分区信息】表中记录用户id取模后0到1是在第一个物理库,2-3是第二个物理库,以此类推。
2.容量评估,需要多少库,多少表;以及后续的扩容
3.数据迁移
整体分库分表一个比较关键的点就是方案确认后如果将旧模型数据同步到新模型。
常见的2个方案:
方案 一:
停机方案,优点是比较简单,缺点是影响业务。
方案大概是:
a.建检查新旧模型数据脚本,同步历史数据,同时建好旧模型到新模型的增量同步
这个架构上可以考虑建一个备库备表用于数据同步。
b.停止对外服务
c.检查新旧数据一致性,没问题开启以新模型为准的查写,关闭旧到新的写,开启新到旧的写;对外提供服务;
有问题则同样停机检查,然后确认数据没问题后,开启旧读旧写,回切到旧模型。
方案二:
不停机双写方案。优:对业务影响小,稳点;缺:切换周期长,需要改程序;
重要业务对停机敏感的选择这个方案。
3.消息队列
3.1.消息队列作用
a.流量大时系统用消息队列可以削峰。
b.非重要业务可以异步做
c.系统间可以解耦
详见:https://blog.csdn.net/zhaozhenzuo/category_6953456.html
3.2.rocketMq主从同步
1.slave与master建立同步连接
2.slave每隔5秒,将自已的offset传给master告知要同步数据了
3.master从这个offset开始同步消息给到slave。
master与slave保持长连接
原文链接:https://blog.csdn.net/zhaozhenzuo/article/details/123938020?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165934458816782184618617%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165934458816782184618617&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~times_rank-23-123938020-null-null.nonecase&utm_term=%E8%87%AA%E5%BB%BAcdn
原创文章,作者:优速盾-小U,如若转载,请注明出处:https://www.cdnb.net/bbs/archives/6302