谈谈互联网的一些高并发设计手段
途径
通过提高吞吐量降低响应延迟这两块方向来确保高并发。
性能优化的目标
- 缩短响应时间
- 提高并发数
- 让系统处于合理状态
优化手段
- 当时间是瓶颈的时候。
当系统的运行时间是主要的瓶颈而空间绰绰有余时,可以采用缓存复用计算结果,把计算的结果存入缓存,降低系统时间上的开销,因为缓存比较容易访问。
- 当空间比较缺少时
这时候就i可以考虑时间换空间了,当系统运行时间很快,而受限于空间的影响。举两个例子:
- 网络传输是瓶颈,这时候可以使用系统时间去使用http的gzip进行压缩,消费时间换取空间的
2.App的请求分类接口,可以使用版本号来判断哪些数据需要更新,只要下载需要更新的数据就可以了
-
找到系统的瓶颈的一些思路
-
分析系统业务流程,找到关键路径并分解优化。
-
比如,一个服务集群,一般前五的几个接口占用了百分之80的流量,这几个接口就是关键路径
-
对关键路径的代码优化收益一般是最大的,其他路径也要想办法进行优化。
整个优化手段上: 调用了多少rpc接口,载入多少数据,使用什么算法,非核心流程能否异步化,没有数据依赖的逻辑能否并行执行。
优化的层次
-
架构设计层次
关注系统控制、数据流。
如何拆分系统,使各部分系统整体负载更加均衡,降低系统内部开销。
-
算法逻辑层次
关注算法的选择是否高效,算法的逻辑进行优化,空间时间优化任务并行处理,使用无锁数据结构,避免加锁(CAS)。
空间换时间
ThradLocal
时间换空间
采用压缩算法压缩数据,更复杂的逻辑减少数据传输。
- 代码优化层次
关注代码细节优化,代码实现是否合理,是否创建过多的对象,循环遍历是否高效,cache使用是否合理,是否重用结算结果。
代码优化层次如下:
循环遍历是否高效:
不要在循环里调RPC接口、查询分布式缓存、执行SQL等,应该先调批量接口组装好数据,再循环处理。
代码需要注意避免生成过多对象或无效对象,比如输出log时候的log级别需要判断,避免new无效对象。
ArrayList、HashMap初始容量设置是否合理。
对数据对象是否合理重用,比如通过RPC查到的数据能复用则必须复用。
根据数据访问特性选择合适数据结构,比如读多写少,考虑 copyOnWrite。
拼接字符串的时候是使用String相加还是StringBuilder进行append(在StringBuilder的容量预分配的情况下,StringBuilder的性能比String相加性能提高15倍左右)。
是否正确初始化数据,有些全局共享的数据,饿汉式模式,在用户访问之前初始化。
数据库建表语句能尽量小的数据结构比如表示状态的字段,如果状态值在255以内使用unisigned tinyint,IP使用int而非varchar。
使用enum的场景使用tinyint,emum扩展需要该表。
避免使用select * 查询数据,只查询需要的字段,避免浪费数据IO、内存、CPU、网络传输。
分析查询场景建立合适的索引,分析字段的可选择性,索引长度,对长的varchar使用前缀索引
字段金量为not null类型,MySql手册说明允许null字段需要额外的存储空间处理null,并且很难查询优化
总结:
以上都是为了降低服务器CPU使用率、IO流量、内存占用、网络消耗、降低响应时间等等。
CPU Cache结构
速度越来越快: 内存 -> L3 -> L2 -> L1多级缓存
哪些数据适合缓存?
很聚焦的高频访问,时效性要求不高的适合缓存,比如很聚焦很高频访问业务数据如频道,栏目,广告位。
时效性要求不是很高的,更新了不用实时体现的适合缓存提高性能。
如果对数据时效性要求很高,需要考虑更新缓存带来的一致性问题。
时效性和缓存的冲突,比如商品服务对商品进行了缓存,由于缓存和更新商品不是同一个事务,则对数据时效性要求搞得如交易,就只能从数据库查询商品信息了。
image算法逻辑优化层次如下:
image架构设计优化逻辑
- 分布式系统微服务化
- 分库分表、读写分离、数据分片
- 无状态化设计、水平扩展
- 调用链路梳理、热点数据尽量靠近用户
- 分布式cache、多级多类型缓存
- 容量规划
- 提前拒绝,保证柔韧性可用。
案例分析
秒杀系统
开发场景分析:
- 大量并发,在某一时刻99%的用户请求进入服务器
- 有效请求很低,与数据库剩余库存量一致
- 库存一致性要求严格,不能超卖
架构设计方面
数据需要进行分层
image数据分层次校验、上层尽量把无效请求过滤掉。
上层可以是不准确的过滤。
层层限流最后一层做数据一致性校验,扣减库存。
具体手段
-
Html、js、css等静态资源文件缓存到用户端(APP或浏览器)
-
非实时动态数据(秒杀期间如商品标题、商品描述、图片URL列表、店铺信息、秒杀活动信息等),这些数据缓存在用户访问链路中靠近用户的位置、粗过滤一分部流量,比如用户是否有秒杀的资格、秒杀是否已结束等、这些数据实时性要求不高,这些数据放在第一层cache上面,这些数据在数据库发生变化时,通过MQ同步到cache。
-
实时数据如用户营销数据(红包、折扣、商品库存等)再过滤掉一批用户。这些数据放到第二层缓存里面,这些数据需要通过MQ进行同步。
-
经过多层过滤最终落到数据库的流量已经很少了,最终在数据库层面使用事务保证扣库存准确性。
feed系统
示例: 微信朋友圈
image特性:
- 读多写少 100/1
- 冷热数据明显 80% 是当天数据,20%用户是活跃用户
- 热点效应明显 热点事件、重大节日、日常这些日期内,比较明显
- 高访问量
注意点
读多写少、冷热数据明显,热点数据缓存到调用链路更靠近用户的地方
L1缓存容量小,负责抗最热点的数据,L2缓存容量考虑目标是容量,缓存更大范围的数据,比如一般用户的timeLine,高热点数据单独缓存,比如设置白名单,大V的用户数据单独缓存。
数据分析各种timeline(关注的timeline、topic的timeline、一些运营的timeline)前几页的访问比例,像前三页占百分之97,把这种业务特性,把前几页的数据作为热点数据放进L1Cache。
imagepush 或 pull
当用户发布数据是,是需要考虑把数据发送push给关注者(朋友),还是用户个人从关注的人中主动pull取消息。这就是push或pull。
比如微博,一个最火的明星有几千万关注者当发布一条消息时,考虑下,如何把消息传播出去。
实现
-
基于写扩散消息同一推送频道
-
推送策略: 拆分数据并行推,活跃用户先推,非活跃用户慢慢推。
比如: 有1万个用户关注,发了一条消息,先拆分成100份,每份100个并行推
1万个用户里,活跃的假设有2000个,则活跃用户先推送,非活跃用户慢慢推每个用户会维护一份活跃用户列表,用户上线,则为活跃用户。
-
消息标准化格式
-
统一数据流,职责明确
image