阿里P8面试官:如何设计一个扛住千万级并发的架构(超级详细)

您所在的位置:网站首页 如何设计一个高并发接口 阿里P8面试官:如何设计一个扛住千万级并发的架构(超级详细)

阿里P8面试官:如何设计一个扛住千万级并发的架构(超级详细)

2023-03-19 15:39| 来源: 网络整理| 查看: 265

在上一篇文章中,详细分析了设计一个千万级并发架构所需要思考的问题,以及解决方案。 在这一片文章中,我们主要分析如何在职场足够用户数量的情况下,同步提升架构的性能降低平均响应时间。

如何降低RT的值

继续看上面这个图,一个请求只有等到tomcat容器中的应用执行完成才能返回,而请求在执行过程中会做什么事情呢?

查询数据库 访问磁盘数据 进行内存运算 调用远程服务

这些操作每一个步骤都会消耗时间,当前客户端的请求只有等到这些操作都完成之后才能返回,所以降低RT的方法,就是优化业务逻辑的处理。

数据库瓶颈的优化

当18000个请求进入到服务端并且被接收后,开始执行业务逻辑处理,那么必然会查询数据库。

每个请求至少都有一次查询数据库的操作,多的需要查询3~5次以上,我们假设按照3次来计算,那么每秒会对数据库形成54000个请求,假设一台数据库服务器每秒支撑10000个请求(影响数据库的请求数量有很多因素,比如数据库表的数据量、数据库服务器本身的系统性能、查询语句的复杂度),那么需要6台数据库服务器才能支撑每秒10000个请求。

除此之外,数据库层面还有涉及到其他的优化方案。

首先是Mysql的最大连接数设置,大家可能遇到过MySQL: ERROR 1040: Too many connections这样的问题,原因就是访问量过高,连接数耗尽了。

show variables like '%max_connections%';

如果服务器的并发连接请求量比较大,建议调高此值,以增加并行连接数量,当然这建立在机器能支撑的情况下,因为如果连接数越多,介于MySQL会为每个连接提供连接缓冲区,就会开销越多的内存,所以要适当调整该值,不能盲目提高设值。

数据表数据量过大,比如达到几千万甚至上亿,这种情况下sql的优化已经毫无意义了,因为这么大的数据量查询必然会涉及到运算。

可以缓存来解决读请求并发过高的问题,一般来说对于数据库的读写请求也都遵循2/8法则,在每秒54000个请求中,大概有43200左右是读请求,这些读请求中基本上90%都是可以通过缓存来解决。

分库分表,减少单表数据量,单表数据量少了,那么查询性能就自然得到了有效的提升

读写分离,避免事务操作对查询操作带来的性能影响

写操作本身耗费资源

数据库写操作为IO写入,写入过程中通常会涉及唯一性校验、建索引、索引排序等操作,对资源消耗比较大。一次写操作的响应时间往往是读操作的几倍甚至几十倍。

锁争用

写操作很多时候需要加锁,包括表级锁、行级锁等,这类锁都是排他锁,一个会话占据排它锁之后,其他会话是不能读取数据的,这会会极大影响数据读取性能。

所以MYSQL部署往往会采用读写分离方式,主库用来写入数据及部分时效性要求很高的读操作,从库用来承接大部分读操作,这样数据库整体性能能够得到大幅提升。

不同类型的数据采用不同的存储库,

MongoDB nosql 文档化存储 Redis nosql key-value存储 HBase nosql, 列式存储,其实本质上有点类似于key-value数据库。 cassandra,Cassandra 是一个来自 Apache 的分布式数据库,具有高度可扩展性,可用于管理大量的结构化数据 TIDB,是PingCAP公司自主设计、研发的开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP) 的融合型分布式数据库产品

为什么把mysql数据库中的数据放redis缓存中能提升性能?

Redis存储的是k-v格式的数据。时间复杂度是O(1),常数阶,而mysql引擎的底层实现是B+TREE,时间复杂度是O(logn)是对数阶的。Redis会比Mysql快一点点。 Mysql数据存储是存储在表中,查找数据时要先对表进行全局扫描或根据索引查找,这涉及到磁盘的查找,磁盘查找如果是单点查找可能会快点,但是顺序查找就比较慢。而redis不用这么麻烦,本身就是存储在内存中,会根据数据在内存的位置直接取出。 Redis是单线程的多路复用IO,单线程避免了线程切换的开销,而多路复用IO避免了IO等待的开销,在多核处理器下提高处理器的使用效率可以对数据进行分区,然后每个处理器处理不同的数据。

池化技术,减少频繁创建数据库连接的性能损耗。

每次进行数据库操作之前,先建立连接然后再进行数据库操作,最后释放连接。这个过程涉及到网络通信的延时,频繁创建连接对象和销毁对象的性能开销等,当请求量较大时,这块带来的性能影响非常大。

数据存储

磁盘数据访问优化

对于磁盘的操作,无非就是读和写。

比如对于做交易系统的场景来说,一般会设计到对账文件的解析和写入。而对于磁盘的操作,优化方式无非就是

磁盘的页缓存,可以借助缓存 I/O ,充分利用系统缓存,降低实际 I/O 的次数。

顺序读写,可以用追加写代替随机写,减少寻址开销,加快 I/O 写的速度。

SSD代替HDD,固态硬盘的I/O效率远远高于机械硬盘。

在需要频繁读写同一块磁盘空间时,可以用 mmap (内存映射,)代替 read/write,减少内存的拷贝次数

在需要同步写的场景中,尽量将写请求合并,而不是让每个请求都同步写入磁盘,即可以用 fsync() 取代 O_SYNC

合理利用内存

充分利用内存缓存,把一些经常访问的数据和对象保存在内存中,这样可以避免重复加载或者避免数据库访问带来的性能损耗。

调用远程服务

远程服务调用,影响到IO性能的因素有。

远程调用等待返回结果的阻塞 异步通信 网络通信的耗时 内网通信 增加网络带宽 远程服务通信的稳定性 异步化架构

微服务中的逻辑复杂处理时间长的情况,在高并发量下,导致服务线程消耗尽,不能再创建线程处理请求。对这种情况的优化,除了在程序上不断调优(数据库调优,算法调优,缓存等等),可以考虑在架构上做些调整,先返回结果给客户端,让用户可以继续使用客户端的其他操作,再把服务端的复杂逻辑处理模块做异步化处理。这种异步化处理的方式适合于客户端对处理结果不敏感不要求实时的情况,比如群发邮件、群发消息等。

异步化设计的解决方案: 多线程、MQ。

应用服务的拆分

除了上述的手段之外,业务系统往微服务化拆分也非常有必要,原因是:

随着业务的发展,应用程序本身的复杂度会不断增加,同样会产生熵增现象。 业务系统的功能越来越多,参与开发迭代的人员也越多,多个人维护一个非常庞大的项目,很容易出现问题。 单个应用系统很难实现横向扩容,并且由于服务器资源有限,导致所有的请求都集中请求到某个服务器节点,造成资源消耗过大,使得系统不稳定 测试、部署成本越来越高 .....

其实,最终要的是,单个应用在性能上的瓶颈很难突破,也就是说如果我们要支持18000QPS,单个服务节点肯定无法支撑,所以服务拆分的好处,就是可以利用多个计算机阶段组成一个大规模的分布式计算网络,通过网络通信的方式完成一整套业务逻辑。

img

如何拆分服务

如何拆分服务,这个问题看起来简单,很多同学会说,直接按照业务拆分啊。

但是实际在实施的时候,会发现拆分存在一些边界性问题,比如有些数据模型可以存在A模块,也可以存在B模块,这个时候怎么划分呢?另外,服务拆分的粒度应该怎么划分?

一般来说,服务的拆分是按照业务来实现的,然后基于DDD来指导微服务的边界划分。领域驱动就是一套方法论,通过领域驱动设计方法论来定义领域模型,从而确定业务边界和应用边界,保证业务模型和代码模型的一致性。不管是DDD还是微服务,都要遵循软件设计的基本原则:高内聚低耦合。服务内部高内聚,服务之间低耦合,实际上一个领域服务对应了一个功能集合,这些功能一定是有一些共性的。比如,订单服务,那么创建订单、修改订单、查询订单列表,领域的边界越清晰,功能也就越内聚,服务之间的耦合性也就越低。

服务拆分还需要根据当前技术团队和公司所处的状态来进行。

如果是初创团队,不需要过分的追求微服务,否则会导致业务逻辑过于分散,技术架构太过负载,再加上团队的基础设施还不够完善,导致整个交付的时间拉长,对公司的发展来说会造成较大的影响。所以在做服务拆分的时候还需要考虑几个因素。

当前公司业务所处领域的市场性质,如果是市场较为敏感的项目,前期应该是先出来东西,然后再去迭代和优化。 开发团队的成熟度,团队技术能否能够承接。 基础能力是否足够,比如Devops、运维、测试自动化等基础能力。 团队是否有能力来支撑大量服务实例运行带来的运维复杂度,是否可以做好服务的监控。 测试团队的执行效率,如果测试团队不能支持自动化测试、自动回归、压力测试等手段来提高测试效率,那必然会带来测试工作量的大幅度提升从而导致项目上线周期延期

如果是针对一个老的系统进行改造,那可能涉及到的风险和问题更多,所以要开始着手改动之前,需要考虑几个步骤:拆分前准备阶段,设计拆分改造方案,实施拆分计划

拆分之前,先梳理好当前的整个架构,以及各个模块的依赖关系,还有接口

准备阶段主要是梳理清楚了依赖关系和接口,就可以思考如何来拆,第一刀切在哪儿里,即能达到快速把一个复杂单体系统变成两个更小系统的目标,又能对系统的现有业务影响最小。要尽量避免构建出一个分布式的单体应用,一个包含了一大堆互相之间紧耦合的服务,却又必须部署在一起的所谓分布式系统。没分析清楚就强行拆,可能就一不小心剪断了大动脉,立马搞出来一个 A 类大故障,后患无穷。

不同阶段拆分要点不同,每个阶段的关注点要聚焦

拆分本身可以分成三个阶段,核心业务和非业务部分的拆分、核心业务的调整设计、核心业务内部的拆分。

第一阶段将核心业务瘦身,把非核心的部分切开,减少需要处理的系统大小;

第二阶段。重新按照微服务设计核心业务部分;

第三阶段把核心业务部分重构设计落地。

拆分的方式也有三个:代码拆分、部署拆分、数据拆分。

另外,每个阶段需要聚焦到一两个具体的目标,否则目标太多反而很难把一件事儿做通透。例如某个系统的微服务拆分,制定了如下的几个目标:

性能指标(吞吐和延迟):核心交易吞吐提升一倍以上(TPS:1000->10000),A 业务延迟降低一半(Latency:250ms->125ms),B 业务延迟降低一半(Latency:70ms->35ms)。 稳定性指标(可用性,故障恢复时间):可用性>=99.99%,A 类故障恢复时间


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3