一个单体应用的扩容之路

一个单体应用的扩容之路

一、前言

对于一个初期的系统来说,功能较少,为了能够快速上线进行推广,此时一般会单体应用架构来进行开发。此时应用系统和数据库很可能都是部署在同一台服务器上。如下图所示。

image

对于这样的系统,经过一段时间的使用后,用户越来越多,网站的流量会增加,单台服务器无法处理那么大的访问流量。此时会使用分而治之的思想来解决问题。

  • 第一步,通过简单扩容来解决(如垂直扩容、水平扩容)。
  • 第二步,第一步搞不定,则通过水平拆分/垂直拆分应用/数据来提升系统性能。
  • 第三步,第二步还搞不定,那么根据现有系统特性,从架构层面进行重构甚至是重新设计(推倒重来)。

对于系统设计,理想情况下应支持线性扩容/弹性扩容,即在系统遇到瓶颈时,只需增加机器就能解决问题,如降低延迟提高吞吐量,从而实现扩容需求。

二、单体应用垂直扩容

当用户量开始增多,现有系统无法处理那么大访问流量,在成本不高的情况下,应该通过硬件扩容来解决。如升级现有服务器,4核8G升级为16核32G,磁盘扩容等。但不管怎么扩容,单机总会有瓶颈,而分布式技术是提升系统扩容能力的更好方法。

三、单体应用水平扩容

随着用户量越来越多,垂直扩容已无法再解决问题。需要进行应用水平扩容以及数据库简单扩容(读写分离)。

image

3.1 应用层面扩容

可以通过线性扩展机器,集群化部署应用实例。由单个应用实例对外提供服务变为多个应用实例向外提供服务。此时需要添加负载均衡层,比如通过nginx实现负载均衡,将用户的请求会分发到多个应用实例上,多个实例一起分担负载。

3.2 数据库层面扩容

如果是数据库瓶颈是读造成的,那么此时可以使用数据库主从架构进行读写分离。写数据时写到主数据库,读数据时读到更多的从数据库。

四、应用拆分

随着业务量的增加,单体应用这么一个大系统肯定会有很多人来维护,这就造成修改代码会出现冲突,上线必须一起上线,风险较大。因此,当单体应用发展到一定地步时,会按照业务进行拆分。

在这里插入图片描述

如上图所示,按照业务将一个大的系统拆分成多个子系统。拆分时进行代码解耦,将功能分离到不同的子系统上。各个子系统之间通过webservice进行进行调用。

在这里插入图片描述

随着系统流量越来越大,业务功能越来越复杂,会在业务系统拆分的基础上进行更细粒度的拆分。对于购物车服务、商品服务等更趋于基础化、通用化。

拆分后,系统之间需要使用带注册/发现功能的SOA框架来进行交互,如Dubbo。服务化后,服务提供者可以根据当前网站状况随时动态扩容/缩容。

另外,由于进行系统拆分,主数据库向缓存或者ES同步时会有一定的延迟,如果需要强一致性的读,那就只能读主库。随着应用部署的增多,数据库连接也会成为瓶颈,一般会通过主从架构来提升连接数。或者使用mycat数据库中间件提升连接数。所有应用只调读/写服务中间件(mycat),由读/写服务中间件(mycat)来访问数据库,减少整体连接数。然后通过MQ异构数据,从而不访问有瓶颈的数据库。

随着流量变大,缓存、限流、防刷需求变得越来越多,此时可以将缓存/限流/从各个应用抽取出来放到单独系统实现,比如可以放到nginx接入层实现。入下图所示。

在这里插入图片描述

五、数据库拆分

5.1 数据库垂直拆分

随着流量的增加,数据库压力也会随之而来,一般会伴随着应用拆分进行数据库拆分。首先会按照业务维度进行垂直拆分,解决多个表之间的IO竞争、单机容量等问题。
在这里插入图片描述

拆分后原来可以进行单库join查询,现在不可以了,需要解决跨库join和分布式事务等问题。跨库join可以考虑使用全局表、ES搜索等数据异构 机制来实现。

5.2 数据库读写分离

业务数据库垂直拆分后,像商品这种读多写少的数据库会遇到读瓶颈。此时需要用读写分离来解决,一个主库(写)挂几个从库(读)。

在这里插入图片描述

5.3 数据库水平拆分

随着流量和数据量的增加,单库单表会遇到磁盘/带宽IO瓶颈,单表随着数据量增长出现性能瓶颈,为了解决单表大数据量的问题,此时需要分库、分表(水平拆分)。

在这里插入图片描述

分库分表是一种水平数据拆分,一般按照ID、用户、时间等维度进行数据拆分。拆分算法可以是取模、哈希、区间等。

这也导致前文所说跨库/跨表join、排序分页、自增ID、分布式事务问题。对于跨库跨表可以对所有表进行扫描然后做聚合,或者生成全局表、进行查询维度的数据异构(比如,订单库按照查询维度异构出商家订单库、用户订单库),再或者将数据同步到ES搜索。自增ID可以通过分布式ID生成生成器生成。而分布式事务应尽量考虑事务表、补偿机制(执行/回滚)、TCC模式(预占、确认、取消)等。业务应尽量设计为最终一致性而不是强一致性。

六、 NoSQL集群引入

6.1 Mongodb集群引入

对于一些特殊数据,可以考虑NoSQL,比如商品介绍就很适合放在mongodb集群中。

在这里插入图片描述

6.2 Redis集群引入

数据库本身不是用来承载高并发业务场景的,所以当数据库层面读并发越来越大时,完全可以引入redis缓存集群扛住那些写少读多的业务场景。

例如,对于单个商品查询这种可以考虑用Redis进行数据缓存。

还有如购物车数据,是用户维度数据,完全可以全量存入redis缓存集群中。为了数据安全性,还可以使用双写架构。

在这里插入图片描述

七、 MQ集群引入

数据库本身不是用来承载高并发业务场景的,所以当数据库层面写并发越来越大时,此时,对那些写一致性要求不高的业务场景。可以引入mq集群,做异步化处理,进行流量肖峰。

如上文中购物车数据变更时,可以使用异步双写的方式把变更发布到mq集群。优点是性能好,缺点数据是有一定时延,数据一致性差一些。应考虑用一致性hash把用户调度到同一个集群,防止用户多次刷新看到不一样的数据。

八、总结

至此,单体应用扩容就告一段落,扩容思路大体也就是以上几种方案,上述几种方案的使用不一定根据本文的顺序进行,切记切记。


  • 参考资料:《亿级流量网站架构核心技术》

猜你喜欢

转载自blog.csdn.net/lixiaohai_918/article/details/89404176