学习https://www.w3cschool.cn/architectroad/architectroad-optimization-of-seckilling-system.html 笔记
秒杀系统优化
列出集中场景:(im)多读多,(微博关注)1读多,(秒杀)多读1
应对:
设计上: 将请求拦截在上游、尽量利用好缓存
具体:
客户端(点过置灰,防止无限制重点、js层面,限制x秒只能提交一次请求)
站点层(用uid统计和去重、x秒内同一uid的请求均返回同一页面、另一种思路是丢弃部分请求)
服务层(对于写请求,请求队列,超过的直接返回已售完、对于读请求,cache)
业务规则优化(分时段卖,流量均摊、数据量粗粒度化有票和无票、异步下单与支付、未支付超过一定时长再回仓)
=======================================================================================
分布式ID生成
全局唯一ID,支持分页排序,时间维度一致性等
数据库auto_increment生成:问题是单库压力大,扩展性差。如果水平扩展多个库(0-0,3,6;1-1,4,7...)无法保证绝对递增,并且业务库压力依然很大
单点批量生成:用本地时钟保证绝对时序,使用批量使用DB的方式降低数据库写压力,数据库只存储当前的最大值,ID生成器可以一次派发多个ID。服务挂了再重启部分ID丢失,服务仍然是单点,海量ID时仍然无法水平扩展
vip+keepalived提供对等ID生成器影子服务,保证高可用
UUID,无法保证递增趋势,并且过长性能较低
当前毫秒数,本地生成,不需要远程调用,但并发高会重复
根据snowflake:大体就是用毫秒数来描述趋势递增,再配合其他标志位表明唯一
定义一个结构,根据不同的位来填充不同的信息,最高位包含毫秒数保证趋势递增,每毫秒内生成序号,机房标志,服务器标志,业务线标志
时钟无法统一
=======================================================================================
容量设计
访问量评估:咨询业务、产品对上线后的预期
平均访问量:一天按照4W秒来算,就是按照基本请求落在白天来评估的
高峰访问量:根据业务特性,通过业务访问曲线评估
系统单机极限承载量评估:通过压力测试(tomcat压测单机只能抗住1200QPS,数据库轻松抗500QPS,cache要看网络带宽等)一般线上系统留个余量,打八折
结果:
如果峰值QPS5000,单机QPS1000,线上目前有2台,则扛不住,最少需要额外的3台,预留出1台,4台更安全
=======================================================================================
线程数设置
cpu核数有限,所以同时并发的线程数是有限的,太多了会增加切换的开销
sleep会让出CPU给其他需要CPU的线程使用。网络编程中的accept()/recv()也不占用CPU
单核时设置多线程的意义,让代码更清晰,各司其职,如果某个任务一直占用CPU那么多线程就没用了,如果不是则可以在某些任务休息的是时候其他线程继续工作
常见的服务线程模型:
IO线程与工作线程通过队列解耦 (几个专门处理IO的线程,接收后加入任务队列,多个工作线程处理队列里的任务)
纯异步线程,单线程,callback方式,框架更复杂
工作者流程(从队列中获取任务->进行本地初始化计算->访问cache->本地计算->通过rpc调用service->本地计算->访问DB->收尾计算)
分析整个工作者流程,其中一些是消耗本地CPU的,一些不占用,进行量化分析,可以根据日志看出worker线程占用和不占用CPU时间的比例,N核服务器,通过执行业务单线程分析出本地计算时间为X,等待时间为Y,则工作线程数设计为N * (x + y)/ x
加密解密,搜索排序,压缩解压缩,搜索排序等计算型的都是CPU密集型的。
=======================================================================================
单点系统的架构和可用性优化
为什么要单点系统?因为单点master设计会大大简化系统设计
一般互联网架构(浏览器->dns服务器->nginx反向代理服务器->tomcat等冗余站点层->rpc后端冗余服务层->数据库一主多从) - 这里看出可能出现单点的就是nginx反向代理服务层和数据库写库。
GFS,client(客户端)->mster(元数据)->n个chunk-server(实际存储文件的服务器) - mster可能单点, 非高可用,性能瓶颈
解决单点高可用问题
影子mster,keepalived + vip 提供高可用,相同的虚拟IP,探活,平时潜伏,mster出问题时启动
如何提升单点性能?减少单点的交互 - 批量写,比如ID生成器一次要N个,然后再派发;客户端缓存,比如gfs客户端请求后缓存metadata
水平扩展,比如dns轮询解析出多个后端nginx,拆分流量到多个nginx
=======================================================================================
负载均衡
将请求、数据均匀的分摊到多个操作单元上执行
客户端->反向代理->站点层->服务层->数据层 每个下游都有多个上游调用,每个上游都均匀访问每个下游
DNS轮询、nginx反向代理配置、连接池/rpc服务治理等进行负载均衡和故障转移,数据库分库分表后的负载均衡(range/idhash)
=======================================================================================
DNS轮询
负载均衡方法(nginx配置反向代理、lvs、keepalived、f5,dns轮询)
dns负载均衡非高可用,只是部署了多台服务,然后dns返回不同的ip,如果某个服务挂了还是不行,而且dns解析有生效期,扩容不能立即生效,并且会对外暴露过多IP。
nginx反向代理,中间多了一层反向代理层,延迟增加,架构更复杂,并且反向代理节点本身成了单点
keepalived解决高可用,相同的虚拟IP,两个服务互为备份,mster挂了,备份节点会发现并切换为主节点提供服务,但明显的是资源利用率只有50%,实现后nginx还是单点
lvs/f5从lvs从操作系统层面,f5从硬件层面实施,性能更好。通过Keepalived和vip方案解决高可用,扩展性和负载均衡的问题
结论:通过DNS轮询来线性扩展入口Lvs层的性能,通过Keepalived来保证高可用,通过Lvs来扩展多个Nginx,通过nginx来做负载均衡
=======================================================================================
异构服务器
是否根据异构服务器的处理能力来动态、自适应进行负载均衡及过载保护
服务层负载均衡,一般是通过service连接池来实现的,调用方连接池会简历与下游服务多个链接,每次按照约定策略获取对应服务
随机,权重(静态、动态-初始能力相同,没成功处理一个请求,动态权重加1 - 考虑设置限制值),过载保护(当系统负载超过处理能力,超过的请求丢弃或放入队列等),动态过载保护 - 比如连续3个请求都超时,则某一小段时间内不再分配这个service,如果重复几次还不行,就不再分配。
=======================================================================================
高并发
同时并行处理很多请求,指标,响应时间-处理单个请求的时间,吞吐量-单位时间内处理的请求量,每秒查询率-每秒响应的用户请求数,并发用户等
提高系统并发:水平扩展和垂直扩展(单机性能提升,提升单机架构性能-异步,缓存,无锁结构)
水平扩展:dns轮询,域名解析多个Ip,反向代理,多个后端服务,微服务,服务治理,rpc根据约定选择不同的服务,数据库分库分表,主从分离,读写分离
=======================================================================================
高可用 - 冗余 - 自动故障转移
keepalived探活,两台nginx,一个坏了,keepalived发现后自动转移到另一台,因为是相同的虚拟ip,所以对于用户不感知
nginx反向代理也能探测后面服务的可用性,如果挂了会进行故障转移,将流量迁移到其他后端服务
缓存的高可用,一种是利用客户端封装,服务对缓存进行双读或双写;另一种是缓存本身的主从同步,redis sentinel用来探活和自动切换
=======================================================================================
数据架构设计
version + ext方案,ext用来承载不同业务需求的个性化属性,使用version来标志ext里各个字段的含义
好处是动态扩展,兼容、不好是ext字段无法建索引,key大量冗余
文中举例说友商是按照业务进行垂直拆分,新的业务会创建一个新的表,但是出现的问题是服务维护在不同的部门,存储在不同的db里,一些规范性问题无法统一,如果要跨品类或按时间查询不好满足等
58的例子,基础数据基础服务统一,将不同品类、异构的数据统一存储,一些通用的字段抽取出来单独存储,通过ext来存储不同业务线的个性化需求
元数据服务 属性服务 搜索服务
这个分类还需要仔细阅读****
=======================================================================================
反向依赖与解耦
为什么会有这个问题?举例一个数据库换了IP,那么其上层调用方需要改配置重启,反向依赖,说明架构有提升空间
怎么优化上条:通过内网域名而不是ip来进行下游连接,当ip切换只需要运维层将内网域名指向新的ip,然后统一切断原有旧连接即可
公共库耦合:解决方法 1.垂直拆分,拆分Jar对应每个服务有各自的jar;2.服务化,如果业务共性能强,则可以升级为公共服务
服务化不彻底:共性服务中,包含大量根据不同业务,执行不同分支的代码;解决方法 业务特性代码上浮,业务共性代码下沉,彻底解耦
notify不合理实现导致的耦合:直接调用会导致发送方和接收方耦合,通过mq解耦即可
=======================================================================================
典型数据库架构设计
单库->一主多从,主从同步,读写分离(写master,读slave,master/slave通过binlog数据同步,多个实例数据库完全相同) 提升读性能,清楚读写冲突,高可用
->分片(建议分库不分表,因为分表依然有磁盘IO竞争,分库很容易将数据迁移到不同数据库实例上扩展性好)范围、哈希切分;多个实例之间结构相同数据不同,容量提升,性能提升->通过分片来降低单库的数据量,通过分组来提升读性能保证高可用->垂直切分,根据长度和访问频度,因为数据库以行为单位将数load到buffer中,长度短频度高能Load更多数据,命中率高,磁盘io减少提升性能,多个实例之间数据不同结构不同,但有关联,降低单库数据量,降低磁盘io从而提升吞吐