设计一个在 AWS 上管理千万级别的用户系统

1. 概述用例和约束,计算

假设这题有以下用例:

  • 用户发出读取或写入请求

    • 服务进行处理,存储用户数据,然后返回结果
  • 服务需要从负载少量用户发展到千万用户

    • 思考处理大量用户和请求时,架构设计和一般的扩展模式
  • 服务高可用

状态假设:

  • 用户流量分布不均匀

  • 需要关系型数据

  • 从1个用户扩展到1千万用户

    • 每月 10 亿次写请求

    • 每月 1000 亿次读请求

    • 读写比例 100 : 1

    • 每次写请求存储 1kb 内容

计算:

  • 每月 1T 内容存储:每次写 1kb * 10亿次写 = 1T,3 年就是 36 T

  • 平均每秒 400 次写请求

  • 平均每秒 40000 次读请求

1
2
3
4
5
1G ≈ 1000M ≈ 1000000K(100万k) ≈ 1000000000b(10亿byte)

1T ≈ 10亿kb

30 天 = 24 * 30 h = 24 * 30 * 3600 s ≈ 250万 s

2. 抽象设计

各个组件

3. 核心组件设计

对于 1 - 2 个用户,只需要简单得设计即可:

  • 一台 Web Server

  • 必要时垂直扩容

  • 监控以确定瓶颈

在单个机器里,部署服务,部署 MySQL 数据库存放用户数据。

当性能不足时 (cpu, memory等) ,垂直扩容,换更好的机器。

将服务器分配一个公共静态 IP。

添加 DNS 域名服务,指向服务器的 IP。

安全方面,服务器仅打开必要的端口,比如 http 请求的 80 端口,https 请求的 443 端口,以及 ssh 白名单用户的 22 端口。

4. 扩展服务设计

User+

用户增多,单机的负载增加。用户静态资源占用过多磁盘,MySQL 也占用越来越多的内存CPU资源。

因为垂直扩展已到极限,换再好的硬件机器也无法对 MySQL 数据库和 Web 服务器进行独立扩展。

目标与对策:

  • 减轻单机负载,并允许独立扩容。

    • 考虑 S3 对象存储,单独保存 JS, CSS, Html, Images, Videos 等静态资源

    • Mysql 单独部署,使用 RDS 服务来管理 Mysql 实例


User++

用户再次增多,高峰时段的单个Web服务器瓶颈,导致响应缓慢,在某些情况下还会导致停机。希望朝着更高的可用性和更低的延时性发展。

目标与对策:

  • 使用水平扩容应对不断增加的负载,解决单点故障

    • 添加 Load Balancer 负载均衡器,例如亚马逊的 ELB 或 HAProxy

    • Web Server 多服务多地域部署

    • Mysql 采用多实例部署,主从模式。

  • 将 Web Server 与 Application Server 分开

    • 两层分别配置

    • Web Server 可以作为一个反向代理

    • 比如可以增加 Application Server 专门负责读请求,其他的负责写请求

  • 将静态(和一些动态)内容移动到 CDN 上,减少加载和延时


User+++

用户仍在增多,而且的读取量很大(读写比例100:1),我们的数据库性能不佳。需要提高读请求的性能。

目标与对策:

  • 减少负载和延迟

    • 将热门数据缓存到 Elasticache 之内的内存中 (首先可尝试先开始 MySQL 缓存,观察是否足以缓解瓶颈),session 数据的缓存

    • Web Server 设成无状态,允许自动缩放。

    • 从内存顺序读取 1MB大约需要 250 微秒,而从 SSD 读取则需要 4 倍,而从磁盘读取则需要 80 倍的时间

  • 添加 MySQL 读库,减少写主库上的负载

  • 添加更多Web服务器和应用服务器以提高响应速度


User++++

用户在一天的某段时间比如工作时间会达到峰值,而其他时间流量会大幅下降,希望尽可能多的 DevOps 自动化以实现自动缩放和常规操作。

目标与对策:

  • 根据流量自动扩展机器

    • 跟上流量高峰

    • 通过关闭未使用的实例来降低成本

  • 自动化DevOps

  • 继续监控指标以解决瓶颈

    • 主机级别-查看单个EC2实例

    • 聚合级别-查看负载均衡器统计信息

    • 日志分析-CloudWatch,CloudTrail,Loggly,Splunk,Sumo

    • 外部站点性能-Pingdom或New Relic

    • 错误报告-sentry


User+++++

用户进一步增长,持续监测并做一些优化

  • 如果 MySQL 数据库开始变得太大,考虑仅在数据库中存储有限时间段的数据,其余存储在 Redshift 等数据仓库中

  • 每秒平均 40000 个读取请求,通过扩展内存缓存来解决热门数据的读取流量,这对于分布不均的流量和流量峰值也很有用

  • 每秒 400 次平均写入,对于单个主从模式写请求而言,可能很难,需要其他扩展技术,比如queue

  • 为了进一步解决高读写请求,我们还应该考虑将适当的数据移动到 NoSQL 数据库(例如 DynamoDB)

5. 进一步讨论

取决于问题领域和剩余时间。

SQL扩展模式 (读写分离,分库分表,分片,sql 调优)

NoSql (kv 存储,document 存储,宽表存储,图形数据库)

缓存 (client,cdn,web server,database)

消息队列,微服务,服务发现

其他方面的权衡取舍

6. 其他

持续对系统进行基准测试和监控,发现瓶颈,并解决它,服务扩展时一个持续的过程。

以上。