如何扩容
- 概述
- 单体应用扩容
- 应用拆分
- 数据库拆分
- 数据库分库分表
- 数据异构
概述
1. 对发展初期的系统来说、不太确定商业模型是否可行、最好的办法是按照最小可行产品方法进行验证、刚开始功能可能比较少、是一个比较大的单体应用、一般按照3层架构来开发、使用单数据库、缓存也是可选组件、而应用系统和数据库也很可能部署在同一台物理机上
那么、网站流量增加时怎么办 ?
1) 第一步肯定是扩容来解决
2) 第二步、若简单扩容无法搞定、就需要根据水平拆分和垂直拆分数据/应用来提升系统的伸缩性、即通过扩容来提升系统负载能力
3) 若水平/垂直拆分依然搞不定、那就根据现有的系统特性、从架构层面进行重构甚至是重新设计、即推到重来
想要扩容、支持水平/垂直伸缩是前提、在进行拆分时、一定要清楚自己的目的是什么、拆分后带来的问题如何解决、若拆分的收益不大、就不要为了拆分而拆分
单体应用扩容
1. 垂直扩容
有些时候、若可以通过硬件快速解决、且成本不高、首先应通过硬件扩容来解决问题
eg. 内存扩容:有些缓存服务器CPU利用率低、但内存不够用、可以通过扩容内存来提升单机容量
cpu升级:从32核升级到64核
磁盘扩容:若系统有大量的随机读写、可以把HDD换成SSD、将原来单机1T升级到2T
原来硬盘做了RAID10、现在直接拆分为裸盘使用、通过架构层面提升数据可靠性
此外、核心数据库可以使用PCIe SSD 或者 NVMe SSD
千兆网卡可以升级为万兆网卡、
...
可最终单机依然会成为瓶颈、此时只能使用分布式服务
2. 水平扩容
水平扩容主要是通过部署更多的镜像来实现的、eg. 通过一个系统示例对外提供服务、通过扩容到更多实例后、用户访问时不可能提供多个域名/IP入口、应该提供同一入口、此时可以通过负载均衡来实现
若系统瓶颈是读造成的、可以通过主从架构将读的流量分散到更多的从服务器上、写数据时写主db、读数据时、读从db
应用拆分
随业务量的增加、一个大的系统会有很多人维护、就造成修改代码会出现冲突、上线必须大家一起上、而且风险比较大、导致需求实现速度缓慢、因此单体应用发展到一定地步时、会按照业务进行拆分
数据库拆分
随流量增大、db压力也会随之而来、一般会伴随db拆分
eg. 按照业务维度垂直拆分、目的是解决多个表之间的IO竞争、单机容量等问题
但、拆分后会面临跨库 join、还要解决分布式事务等问题、跨库join可以考虑通过如、全局表、ES搜索等异构数据机制来实现
按照不同业务拆分后、随流量的增加、像商品这种读多写少的db会遇到读瓶颈、可以使用读写分离来解决
数据库分库分表
数据库分库分表后就涉及如何写入和如何读取的问题、应用开发人员主要关心
1. 是否需要在应用层做改造来支持分库分表、即是在应用层支持还是通过中间件呢 ?
2. 如果需要应用层支持、那分库分表的算法是 ?
3. 分库分表后、join是否支持、排序分页是否支持、事务是否支持 ?
使用中间件的好处是:
1) 对应用透明、可以像使用单库单表一样查询中间件、
2) 还可以支持多种语言、不受限于特定的语言、
3) 还可以减少应用的总连接数、从而避免因应用过多而导致数据库连接不够用
缺点是:除了维护中间件、还要考虑中间件的HA/负载均衡等、增加部署和维护的困难
目前开源的db中间件有:基于mysql-proxy开发的360的atlas、阿里的cobar、基于cobar开发的mycat等
这些中间件、目前主要是基于mysql
分库分表的策略:
那么如何分库分表呢 ?因为分库会影响数据的读取和写入、
eg. 安装订单ID分库分表就很难安装客户维度进行订单查询
常见策略:
1. 取模
可以按照数值型主键取模来分库分表、也可以按照字符串主键hash取模进行分库分表、常见的如 订单表按照订单ID分库分表、用户订单表按照用户ID分库分表、产品表按照产品ID来分库分表 等
取模的优点是、数据热点分散
缺点是:按照非主键温度进行查询时、需要跨库跨表、扩容需要建立新的集群并进行数据迁移
若想减少扩容时带来的麻烦可以在初期规划时冗余足够数量的分库分表
2. 分区
可以按照时间分区、范围分区进行分库分表、时间规则如、一个月一张表、一年一个库等
范围分区规则如0~20000w一个表、2000w~4000w一个表等
若分区规则比较复杂、可以使用一个路由表来存储分库分表规则
分区的缺点是存在热点、但是易于水平扩展、可以避免数据迁移
也可以使用取模+分区组合使用
数据异构
分库分表后会带来许多问题、eg. 跨库join、非分库分表维度的条件查询、分页排序等
可以使用扫描全部表通过内存聚合、数据异构(全局表、ES搜索、异构表)等来解决
数据异构主要按照不同查询维度建立表结构、这样就可以按照这种不同的维度进行查询、
在数据量和查询量都很高的情况下、使用数据异构是十分有效的、但增加了架构的复杂度、异构时可以通过订阅MQ或者binlog并解析实现
异构主要存储数据之间的关系 或者 实现数据聚合