# 高并发出现的原因
- 刚开始系统都是连接数据库的,数据库支撑到每秒并发两三千的时候,基本就快扛不住了,系统扛不住压力就会宕机;
- 应用系统集群部署的方式,已经比较成熟,且成本低,易操作,因此此处不讨论;
# 提高系统并发的手段
# 拆分系统
- 将单个系统拆分成多个系统,每一个系统连接一个数据库,由原来的单库变为了多库;
# 使用缓存
- 大部分高并发场景是读多写少,因此考虑在数据库和缓存都写数据,读的时候大量走缓存;
- 缓存要考虑到可用性和稳定性;
# 使用消息队列
- 系统可能存在高并发写的场景,单纯用缓存不能满足要求,因为缓存数据随时会被LRU,且数据格式简单,没有事务支撑,该用数据库还得数据库,可以通过消息队列将数据控制在数据库可以承受的范围,系统慢慢消费
- 消息队列单机可以抗几万并发
- 消息队列,又可以分为高并发型消息队列,高可用性消息队列,主流产品为
rabbitMq,rocketMq,kafka
;
# 分库分表
- 将一个数据库拆分成多个数据库,由多个库去抗并发
- 将一个表拆分成多个表,每个表的数据量保持少一点,提高sql性能;
# 读写分离
- 大部分时候,数据库是读多写少
- 可以弄一个主从架构,主库写入,从库读取,读流量太多时,还可以添加更多的从库;
- 需要考虑从库时延问题;
# 使用ES
ElasticSearch
是分布式的,可随便扩容,天然支撑高并发;- 一些比较简单的查询,统计或者全文搜索的操作,可以考虑用
es
实现;
# 常见的高并发场景
淘宝的双11
春运时抢票
微博大V的热点新闻
每秒几十万请求的秒杀系统
每天千万级的订单系统
每天亿级日活的信息流系统
...
# 高并发的最佳实践
- 使用高并发的处理方法去演进系统,包括架构设计,编码实现,甚至产品方案角度,而不是一味升级硬件,加机器做水平扩展;
- 可以从读写关系进行大致的分类,包括读多写少的信息流场景,读多写多的交易场景
- 高并发并不只是为了追求高性能,从宏观角度看,高并发的目标有三个:高性能、高可用、高可扩展
# 性能指标
平均响应时间(AVG),最常用,但是缺陷最明显,对于满请求不敏感;
分位值(TP),将响应时间从小到大排序,TP80表示排在第80分位的响应时间;分位值越大,对满请求越敏感;
吞吐量,和响应时间成反比,比如响应时间为1ms,则吞吐量为每秒1000次;
从用户角度看,200毫秒被认为是第一个分界点,用户感受不到延迟;1秒是第二个分界点,用户能感受到延迟,但可以接受;
# 可用性指标
可用性= 平均故障时间/系统总运行时间,一般用几个9来描述
对于高并发系统来说,最基本的要求是:保证3个9或者4个9
例如对于2个9的系统,意味着有**1%**的故障时间,某些大公司动辄千亿级别的收入,1%就是10亿级别的业务影响,这是不能接受的;
实际上,一个健康的项目,这个可用性指标,并不难;
# 可扩展性指标
- 面对突发流量,不可能临时改造架构,最快的方式就是增加机器来线性提高系统的处理能力;
- 理想的扩展能力是:资源增加几倍,性能就提升几倍,通常来说,扩展能力要维持在**70%**以上;
- 当服务集群,数据库,带宽,缓存和消息队列等中间件,负载均衡等达到一定量级后,每一个因素都可能成为扩展的瓶颈;
# 方法论
# 纵向扩展
- 提升单机的硬件性能,增加内存,CPU核数,存储容量,磁盘升级为SSD等;
- 提升单机的软件性能,如减少IO次数,使用并发或者异步的方式增加吞吐量;
# 横向扩展
- 单机性能总会存在极限,最终不得不引入横向扩展,通过集群部署,进一步提高并发处理能力;
- 做好分层架构,这是横向扩展的提前,通过分层处理,可以简化复杂问题,更容易做到横向扩展;
- 各层进行水平扩展:无状态组件做水平扩容,有状态组件做分片路由,业务集群通常能设计成无状态的,而数据库和缓存通常是有状态的;
- 针对有状态组件,也可以通过主从同步、读写分离的方案提升读性能;
# 秒杀场景泛化
- 12306火车票抢票
- 抢红包场景
- 推广至所有需要高并发的场景
# 秒杀场景实操关键
# 限流
- 实际上,高并发的大部分压力,都由网关承受;
- 对于用于而言的并发,与实际应用服务器承受的并发,并不是一个共同概念;
- 本质上,是对秒杀问题的一个降维处理;
# 分布式锁
- 锁是并发的正确性、安全性保障,是高并发场景的前提;
- 锁本质上,是将一系列问题,串行化;
- 串行化必然会带来性能的急剧下降;
# 排队
- 排队的并发数,才是应用程序实际抗住的并发;
- 排队用于缓解串行化导致的性能急剧下降;
- 排队成功,一般而言就代表秒杀成功;
- 排队的性能,实际上就代表了实际的应用服务器的处理性能,从数据库连接数,应用程序能够承受的最高并发考虑;
# 秒杀场景口诀
- 某种程度上,高并发场景,可以理解为限流+分布式锁+排队;
- 其它的措施,都是增加秒杀可用性的:比如消息队列,缓存,分布式事务等;
- 秒杀有一个不可忽视的特性,那就是重试,秒杀失败可以快速重试
- 秒杀并不存在所谓的十万级,百万级,亿级,只是所谓的架构师玩的障眼法?如果从并发可用性的角度来看,尚有讨论的意义:
- 1%的成功率,十万,百万,千万,亿级分别对应着 1千,1万,10万,100万并发;
- 0.1%的成功率,十万,百万,千万,亿级分别对应着100,1千,1万,10万并发;
- 0.01%的成功率,十万,百万,千万,亿级分别对应着10,100,1千,1万并发;
- 不过尽管降低了两个以上数据量级的维度来讨论并发,常规应用依然支撑不了这些降低了维度的并发,需要借助缓存+消息队列辅助处理
# 秒杀与高并发的区别
- 秒杀所承受的并发冲击,并不是实际的流量冲击
- 高并发场景,代表的是需要实际承受的并发吞吐量,从理论角度预估:
- 应用程序以
fatjar
单机400并发为例,支持横向扩展,一万的并发,就要25台; - 以
4核8g
的机器部署的mysql
而言,理论上应该可以支持5千
的并发连接;8核16g
部署的mysql
而言,理论上能够支持1万
的并发连接;但是支持连接,并不能保证服务速度 - 借助,消息队列,缓存,可以有效解决数据库的并发瓶颈;
- 应用程序以