目录
一、为什么提升扩展性会很复杂
在架构设计之初,为什么不预先考虑好使用多少台机器,支持现有的并发呢?
答案是峰值的流量不可控。基于成本考虑,在业务平稳期,我们会预留30%~50%的冗余以应对运营活动或者推广可能带来的峰值流量,但是当有一个突发事件发生时,流量可能瞬间提升到2~3倍甚至更高。
如何应对突发的流量呢?架构的改造已经来不及了,最快的方式就是堆机器。不过我们需要保证,扩容了三倍的机器之后,相应的我们的系统也能支撑三倍的流量吗? 答案是否定的
集群系统中,不同的系统分层上可能存在一些“瓶颈点”,这些瓶颈点制约着系统的横向扩展能力。
你系统的流量是每秒1000次请求,对数据库的请求量也是每秒1000次。如果流量增加10倍,虽然系统可以通过扩容正常服务,数据库却成了瓶颈。再比方说,单机网络带宽是50Mbps,那么如果扩容到30台机器,前端负载均衡的带宽就超过了千兆带宽的限制,也会成为瓶颈点。
无状态的服务和组件更易于扩展,而像MySQL这种存储服务是有状态的,就比较难以扩展。因为向存储集群中增加或者减少机器时,会涉及大量数据的迁移,而一般传统的关系型数据库都不支持。这就是为什么提升系统扩展性会很复杂的主要原因。
二、高可扩展性的设计思路
1.拆分
拆分 是提升系统扩展性最重要的一个思路,它会把庞杂的系统拆分成独立的,有单一职责的模块,将复杂的问题简单化。
假如你要设计一个社区,那么社区会有几个模块
- 用户:负责维护社区用户信息,注册,登陆等;
- 关系:用户之间关注、好友、拉黑等关系的维护;
- 内容:社区发的内容,就像朋友圈或者微博的内容;
- 评论、赞:用户可能会有的两种常规互动操作;
- 搜索:用户的搜索,内容的搜索。
部署方式按照最简单的三层部署架构,负载均衡负责请求的分发,应用服务器负责业务逻辑的处理,数据库负责数据的存储落地。这时,所有模块的业务代码都混合在一起了,数据也都存储在一个数据库里。
2.存储层的扩展性
无论是存储的数据量,还是并发访问量,不同的业务模块之间的量级相差很大,比如说成熟社区中,关系的数据量是远远大于用户数据量的,但是用户数据的访问量却远比关系数据要大。所以假如存储目前的瓶颈点是容量,那么我们只需要针对关系模块的数据做拆分就好了,而不需要拆分用户模块的数据。所以存储拆分首先考虑的维度是业务维度。这么做还能隔离故障,某一个库“挂了”不会影响到其它的数据库。
按照业务拆分,在一定程度上提升了系统的扩展性,但系统运行时间长了之后,单一的业务数据库在容量和并发请求量上仍然会超过单机的限制。这时,我们就需要针对数据库做第二次拆分。
按照数据特征做水平的拆分,比如说我们可以给用户库增加两个节点,然后按照某些算法将用户的数据拆分到这三个库里面。能随意地增加节点,因为一旦增加节点就需要手动地迁移数据,成本还是很高的。所以基于长远的考虑,我们最好一次性增加足够的节点以避免频繁的扩容。
3.业务层的扩展性
我们一般会从三个维度考虑业务层的拆分方案,它们分别是:业务维度,重要性维度和请求来源维度。
首先,我们需要把相同业务的服务拆分成单独的业务池,比方说上面的社区系统中,我们可以按照业务的维度拆分成用户池、内容池、关系池、评论池、点赞池和搜索池。
每个业务依赖独自的数据库资源,不会依赖其它业务的数据库资源。这样当某一个业务的接口成为瓶颈时,我们只需要扩展业务的池子,以及确认上下游的依赖方就可以了,这样就大大减少了扩容的复杂度。
还可以根据业务接口的重要程度,把业务分为核心池和非核心池。打个比方,就关系池而言,关注、取消关注接口相对重要一些,可以放在核心池里面;拉黑和取消拉黑的操作就相对不那么重要,可以放在非核心池里面。这样,我们可以优先保证核心池的性能,当整体流量上升时优先扩容核心池,降级部分非核心池的接口,从而保证整体系统的稳定性。
还可以根据接入客户端类型的不同做业务池的拆分。比如说,服务于客户端接口的业务可以定义为外网池,服务于小程序或者HTML5页面的业务可以定义为H5池,服务于内部其它部门的业务可以定义为内网池