一般来说,基于成本考虑,在业务平稳期会预留30%-50%的冗余以应对运营活动或者推广可能带来的峰值流量。
那么要如何应对突发的流量呢?架构改造是来不及的,最快的方式就是堆机器。但是需要保证,扩容了三倍的机器后,相应的我们的系统也能支撑三倍的流量。那么需要注意什么呢?
为什么提升扩展性会很复杂
之前提到单机系统中通过增加处理核心的方式,来增加系统个的并行处理能力,但这个方式并不总生效。并行的任务数较多时,系统会因为争抢资源而达到性能上的拐点,系统处理能力不升反降。
对于集群系统来说也是如此。集群系统中,不同的系统分层上可能存在一些“瓶颈点”,这些制约着系统的横线扩展的能力。比如说,系统的流量是每秒1000次请求,对数据库的请求量也是每秒1000次。如果流量增加10倍,虽然系统可以通过扩容正常服务,数据库却成了瓶颈。比如单机网络带宽是50Mbps,如果扩容到30台机器,前段负载均衡的带宽就超过了千兆带宽的限制,也会成为瓶颈点。
其实,无状态的服务和组件更易于扩展,而像MySQL这种存储服务是有状态的,比较难以扩展。因为向存储集群增加或者减少机器时,会涉及大量数据的迁移,而一般传统的关系型数据库都不支持。这就是为什么提升系统扩展性会很复杂的主要原因。
所以说,数据库、缓存、依赖的第三方、负载均衡。交换机带宽等都是系统扩展时需要考虑的因素。我们要知道系统并发到了某一个量级之后,哪一个因素会成为我们的瓶颈点,从而针对性的进行扩展。
高可扩展性的设计思路
拆分是提升系统扩展性最重要的一个思路,他会把庞杂的系统拆分成独立的,有单一职责的模块,将复杂的问题简单化。
但对于不同各类型 大模块,我们在拆分上遵循的原则是不一样的。加入要设计一个社区,那么社区会有几个模块呢?可能有5个模块。
- 用户:负责维护社区用户的信息,注册,登录等。
- 关系:用户之间相互关注、好友、拉黑等关系的维护。
- 内容:社区发的内容,比如微博内容。
- 评论、赞:用户可能会有的两种常规互动操作。
- 搜索:用户的搜索,内容的搜索。
-
存储层的扩展性
无论是存储的数据量还是并发访问量,不同的业务模块之间的量级相差很大,比如成熟的社区中,关系的数据量是远远大于用户数据量的,但是用户数据的访问量却远比关系数据要大。所以假如存储目前的瓶颈点是容量,那么我们只需要针对关系模块的数据做拆分就好了,而不需要拆分用户模块的数据。所以存储拆分首先考虑的维度是业务维度。
按照业务拆分,在一定程度上提升了系统的扩展性,但是系统运行时间长了后,单一的业务数据库在容量和并发请求量上仍然会超过单机的限制。这时我们就需要针对数据库做第二次拆分。
这次的拆分是按照数据特征做水平的拆分,比如说我们可以给数据库增加两个节点,然后按照某些算法将用户的数据拆分到这三个库里面。
水平拆分之后,我们就可以让数据库突破单机的限制了。但是需要注意,我们不能随意的增加节点,因为一旦增加节点就需要手动的迁移数据,成本很高。基于长远考虑,我们最好一次性增加足够的节点以避免频繁的扩容。
当数据库按照业务和数据维度拆分后,我们尽量不要使用事务,因为当一个事务中同时更新不同的数据库时,需要使用二阶段提交,来协调所有的数据要么全部更新成功,要么全部更新失败。这个协调成本会随着资源的扩展不断提高,最终达到无法承受的程度。
-
业务的扩展性
一般从三个维度考虑业务层的拆分方案,分别是:业务维度,重要性维度和请求来源维度。
首先,我们需要把相同业务的服务拆分成单独的业务池,比如上面说到的社区系统中,我们可以按照业务的维度拆分成用户池、内容池、关系池、评论池、点赞池和搜索池。
每个业务依赖独自的数据库资源、不会依赖其他业务的数据库资源。这样当某一个业务的接口成为瓶颈时,我们只需要扩展业务的池子,以及确认上下游的依赖方就可以了,这样就大大减少了扩容的复杂度。
image
除此之外,我们还可以会根据业务接口的重要程度,把业务分为核心池和非核心池。比如,就关系池而言,关注、取关接口相对重要一些,可以放到核心吃里面;拉黑和取消拉黑的操作就相对不那么重要,可以放在非核心池里面。这样我们可以优先保证核心池的性能,当整体流量上升时优先扩容核心池。降级部分非核心池的接口,从而保证整体系统的稳定性。
image.png
最后还可以根据接入客户端类型的不同做业务池的拆分。比如说,服务于客户端接口的业务可以定义为外网池,服务于小程序或者H5页面的业务可以定义为H5池,服务于内部其他部门的业务可以定义为网池,等。
未做拆分的系统虽然可扩展性不强,但是却足够简单,无论是系统公开发和运行维护都不需要投入很大的经理。拆分之后,需求开发需要横跨多个系统多个小团队,排查问题也需要涉及多个系统,运行维护上,可能每个子系统都需要专人来负责,对团队是一个比较大的考验。
网友评论