现状

一个用户的典型请求链路

如下所示展示了一个用户的请求的典型请求链路图。

保证业务HA下的数据库方案 - 图1

链路中的问题

  1. 流量在网络层的处理能力,依靠 SLB 和 K8s Ingress 可以水平扩展,且处理无压力
  2. 业务真正的处理都落在数据层。DB 单点,没有水平伸缩能力。没有读写分离。
  3. 除 saas 外 90% 以上的应用业务,只有一个数据库实例支持。

结论

  1. 业务逻辑真正的压力都在数据库上
  2. DB的处理能力反应业务真正处理能力和吞吐量
  3. 保证业务HA的方案,其实是保证链路中每一个链条的HA,现阶段情况是保证数据库的HA
  4. 提升数据库HA的手段如下
    1. 硬件层面,提升硬件规格,例如 CPU和内存等
    2. 技术架构层面
      1. 主从分流
      2. 主从切换
      3. 增加缓存分流
      4. 业务拆分,同时拆分数据库实例,多数据库实例,多库
      5. 数据备份容灾

分步走业务HA策略

1.单纯考虑在单台高负载时的数据库HA

方案 步骤 优点 缺点
Plan1 1. 利用 DTS 另外同步一个数据库实例,同步的实例可以是 RDS 也可以是 PoloarDB
2. 在主库 RDS CPU 负载高导致业务阻塞时,利用脚本切换所有应用的数据库连接配置。
架构拓扑简单
改动较小
数据库实例只能接受一侧写入,切换业务配置时要做到全部成功,且同时往新的数据库写
方案实施难度较大
不建议使用,单纯列出比较
Plan2 1. 新建主从实例,同时用DTS同步主导另外一套数据库的主从实例
2. 使用HAProxy作代理,代理主从实例
3. 被写的主实例负载满后,变换HAProxy配置,代理至备用的主从
业务侧只需使用HAProxy地址,切换时无感知,无需改动,只需要支持客户端重连
引入新的一层代理依赖,增加了运维成本
引入新的备用实例,增加了支出成本
Plan3 1. 使用现有的 RDS 实例,在主实例满负载时,手动切换(也可以写成脚本调用Aliyun OpenAPI)将备份实例调整为主实例 业务侧切换时无感知,无需改动,只需要支持客户端重连 切换后,主从实例可能会有延迟

以上3个方案,并没有真正解决数据库单点问题。考虑到数据库满负载的根本原因是所有业务侧的应用流量映射增删改查到数据库一侧,而数据库的计算资源无法满足日益增长或瞬时增长的业务请求导致的。

单纯做以上改动,并没有真正提升业务的HA。如业务继续高峰,切换后仍然引起数据库CPU负载高,没有从根本上解决问题。

2.考虑业务读多写少的业务场景配置业务使用数据库读写分离模式

考虑到我们企业业务的真实场景,读多写少,可以升级数据库为一主多从,通过设置读写分离,来显著降低单台计算节点的负载。

方案 步骤 优点 缺点
Plan1 1. RDS 从库升级配置,升级为最好2从只读实例
2. 配置数据库读写流量规则
3. 业务侧配置使用RDS集群代理,由集群代理提供数据库读写分离能力
1. 数据无需迁移
2. 业务侧改动较小,只需更改数据库地址为集群代理地址
1. 增加了支出成本
2. 考虑到极端场景,在特殊场景的查询可能出现数据不一致现象(可以保证最终一致性)
Plan2 1. RDS升级至PolarDB
2. PolarDB配置多从分配流量规则
3. 业务使用PolarDB连接。同时保留DTS通道,向RDS或MySQL同步数据
1. 其本身存算分离,提升了计算能力,且底层有多种优化
2. 对大表查询友好,底层支持分片和并发计算,索引下推等
3. 能保证数据在会话内的一致性
4. 业务侧改动较小,仅需修改数据库连接
5. 方便弹性扩展,,最多支持15从实例,可以大幅减低单台计算节点的负载
1. 迁移至PolarDB时可能需要停机(为了保持一侧写入,需要更改所有应用的数据库连接,此时停机是必要的,否则双侧写可能会造成主键冲突)
2. 增加了支出成本

3.读写分离模式下业务HA的数据库架构

对应上面读写分离的两个方案,分别给出业务HA的数据库架构,即在主节点满载时,不阻塞业务的数据库方案。

主从方案下,分析数据库慢负载的真正原因。

  1. 其中一个只读实例满负载,根据负载均衡的流量配置,后续的读会分配到其他读实例,不会阻塞业务。
  2. 所有读实例满负载,此时流量应该较大,或者慢SQL执行较多,完全执行业务,要根据监控,在其中一个实例负载达到 80%以上时,拉起新的数据库从实例,从而分担流量
  3. 主实例,写操作阻塞。
    1. 大量写,一般都伴随大量业务流量
    2. 写阻塞会影响后续写业务,引起阻塞,此时要针对性分析
      1. 设置主实例硬件资源自动扩容,CPU 规格水平扩容 100%
      2. 若CPU负载仍瞬间飙升至满载,查询数据库详情,根据业务 KILL 阻塞语句,将影响降低至最少
      3. RDS的方案,将备份升级为主实例,PolarDB方案,将从升级为主实例。正常情况下,实例切换后,CPU使用率将降低,如CPU负载仍持续升高,那就预示着现有架构处理不了增长和瞬发的流量,需要再次使用分治法,拆分业务的数据库,将其分散在多个实例上。

针对主实例大量写导致的阻塞

方案 步骤 优点 缺点
Plan1-RDS实例 1. 设置主实例硬件资源自动扩容,CPU 规格水平扩容 100%
2. 查询数据库详情,有针对的处理阻塞,KILL 阻塞语句,降低CPU负载
3. 仍阻塞,升级备份实例为主实例,可通过API接口方式切换,降低人工切换的时间
1. 业务没有中断,只需客户端支持重连 1. 切换时可能导致主从同步延迟,从而对查询造成数据一致性问题(保证最终数据一致性)
Plan2-PolarDB实例 1. 设置主实例硬件资源自动扩容,CPU 规格水平扩容 100%
2. 查询数据库详情,有针对的处理阻塞,KILL 阻塞语句,降低CPU负载
3. 仍阻塞,升级只读实例为主实例,可通过API接口方式切换,降低人工切换的时间
1. 业务没有中断,只需客户端支持重连
2. 数据一致性较好

更多措施

  1. 新增主从实例指标Metrics上报至Prometheus。
  2. 根据采集的指标,新增一分钟内指标持续上升报警
  3. 根据报警的触发,可以设置触发的脚本,比如读阻塞时,新增读实例。写实例指标持续上升,告警,然后可通过脚本触发硬件扩容
  4. 查考更进一步的公库优化策略

4. 考虑业务共库场景实施分库策略

  1. 业务按照业务领域拆分服务,分库且分数据库实例存储
  2. 改造业务,增加缓存层,利用缓存分流请求流量压力
  3. 改造业务,业务间交互使用 rpc 或 HTTP API 接口调用
  4. 增加调用链路,对链路上请求进行记录,为后续性能优化及问题排查提供友好的环境和数据支持

结论

根据分步走策略,结合公司实情现阶段可投入做的事情如下(22年的规划)

  1. 增加 PolarDB 实例,从现有 RDS 迁移数据到 PolarDB,并用 DTS 保持同步(开启双向同步)
  2. PolarDB 新增多从,设置流量规则,业务修改数据库连接实现读写分离,分担单节点的计算资源负载

More

  1. 后续有足够多的人力和支出规划,建议组织实施业务拆分,同时将数据库分库分实例
  2. 改造业务,增加缓存层,增加熔断和限流,彻底告别单节点风险
  3. 压测,模拟用户行为并发压测,得到主从模式下,能支持的业务流量,做到心中有数,同时也能根据压测结果,调整从实例的触发规则
  4. 监控和监控触发的完善,演习遇到写流量导致的阻塞后续处理流程能保证业务的HA
  5. 改造业务,迁移NewSQL,原生分区存储和计算,多实例。