人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户

Instagram 是一款以图片和短视频分享为主的社交媒体平台,于 2010 年由 Kevin Systrom 和 Mike Krieger 创建。用户可以通过 Instagram 应用发布和编辑照片和视频,添加滤镜和标签,以及与朋友互动。以下故事发生在 Instagram 早期的快速增长阶段。

从 2010 年 10 月到 2011 年 12 月,Instagram 在短短一年多的时间里,用户数量从 0 增长到 1400 万。他们仅用 3 名工程师就做到了这一点。

他们之所以能做到这一点,是因为遵循了 3 个关键原则并拥有可靠的技术栈。

Instagram 的指导原则

  • 一切从简。
  • 不要重复发明轮子。
  • 尽可能使用经过验证的可靠技术。

早期 Instagram 的基础设施运行在 AWS 上,使用 EC2 和 Ubuntu Linux。为了方便起见,让我们通过用户会话(session) 的生命周期来描述整个技术栈。

前端

会话:用户打开 Instagram 应用程序。

Instagram 最初是在 2010 年作为 iOS 应用程序推出的。Instagram 是使用 Objective-C 以及对应的 UIKit 框架来写的。

人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户_第1张图片

负载均衡

会话:打开应用后,抓取主 feed 照片的请求会被发送到后台,在那里会碰到 Instagram 的负载均衡器。

Instagram 使用 AWS 的 Elastic Load Balancer (ELB)。他们有 3 个 NGINX 实例,这些实例会根据其是否健康进行交换。

每个请求在路由到实际应用服务器之前都会首先到达 ELB。

人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户_第2张图片

后端

会话:ELB 将请求发送到应用服务器,应用服务器拥有正确处理请求的逻辑。

Instagram 的应用服务器使用了 Django,它是用 Python 编写的,Gunicorn 是他们的 WSGI 服务器。

作为复习,WSGI(Web Server Gateway Interface,网络服务器网关接口)将请求从网络服务器转发到网络应用程序。

Instagram 使用 Fabric 同时在多个实例上并行运行命令。这样就能在几秒钟内部署代码。

这些实例运行在超过 25 台 AWS CPU High-CPU Extra-Large 的机器上。由于服务器本身是无状态的,因此当他们需要处理更多请求时,可以添加更多机器。

人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户_第3张图片

通用数据存储

会话:应用服务器收到请求,这些请求需要主 feed 上数据。

为此,它大致需要:

  1. 最新的相关照片 ID
  2. 与这些照片 ID 匹配的实际照片
  3. 这些照片的用户数据

数据库:Postgres

会话:应用服务器从 Postgres 中获取最新的相关照片 ID。

应用服务器将从 PostgreSQL 中提取数据,该数据库存储了 Instagram 的大部分数据,如用户和照片元数据。

PostgreSQL 和 Django 之间的连接使用 Pgbouncer 连接池。

由于 Instagram 接收的数据量很大(每秒超过 25 张照片和 90 个赞),他们对数据进行了分片。他们使用代码将几千个「逻辑」分片映射到几个物理分片上。

Instagram 面临并解决的一个有趣挑战是生成可按时间排序的 ID。他们生成的可按时间排序的 ID 是这样的:

  • 41 位比特表示以毫秒为单位的时间(提供 41 年的 ID 和自定义纪元)
  • 13 位比特表示逻辑分片 ID
  • 10 位比特表示自动递增序列,模数为 1024。这意味着我们可以为每个分区每毫秒生成 1024 个 ID

得益于 Postgres 中可按时间排序的 ID,应用服务器成功接收到了最新的相关照片 ID。

照片存储:S3 和 Cloudfront

会话:然后,应用服务器通过快速 CDN 链接获取与这些照片 ID 匹配的实际照片,以便快速加载给用户。

AWS S3 中存储了数 TB 的照片。这些照片通过 AWS CloudFront 快速提供给用户。

缓存:Redis 和 Memcached

会话:为了从 Postgres 中获取用户数据,应用服务器(Django)使用 Redis 将照片 ID 与用户 ID 进行匹配。

Instagram 使用 Redis 存储了约 3 亿张照片与创建这些照片的用户 ID 的映射,以便在为主 feed、活动 feed 等获取照片时知道要查询哪个分片。所有 Redis 都存储在内存中,以减少延迟,并在多台计算机上进行分片。

通过一些巧妙的哈希算法,Instagram 能够在不到 5 GB 的空间内存储 3 亿个键值的映射。

为了知道要查询哪个 Postgres 分区,需要将照片 ID 与用户 ID 进行键值映射。

会话:由于使用了 Memcached 进行高效缓存,从 Postgres 获取用户数据的速度非常快。

对于一般缓存,Instagram 使用的是 Memcached。当时他们有 6 个 Memcached 实例。将 Memcached 架设在 Django 上相对简单。

有趣的事实是:2 年后的 2013 年,Facebook 发布了一篇具有里程碑意义的论文,介绍了他们如何扩展 Memcached 以帮助他们处理每秒数十亿次的请求。

会话:用户现在能看到首页 feed 了,上面有他所关注的人的最新图片。

人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户_第4张图片

主从设置

Postgres 和 Redis 都是用主从设置,并使用亚马逊 EBS(弹性块存储)快照功能对系统进行频繁备份。

推送通知和异步任务

会话:现在,假设用户关闭了应用,但又收到推送通知说朋友发布了一张照片。

该推送通知是使用 pyapns 发送的,同时发送的还有 Instagram 已发送的十亿多条推送通知。Pyapns 是一个开源、通用的苹果推送通知服务(APNS)提供商。

会话:用户非常喜欢这张照片!所以他决定在 Twitter 上分享。

在后端,任务被推送到 Gearman,这是一个任务队列,可以将工作分派给更合适的机器。Instagram 有大约 200 个 Python Workers 在使用 Gearman 任务队列。

Gearman 用于执行多个异步任务,例如向用户的所有关注者推送活动(如发布新照片)(称为 fanout)。

人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户_第5张图片

监控

会话:啊哦!Instagram 挂了,原因是服务器出错,发送了错误的响应。

Instagram 的三名工程师立即收到了警报。

Instagram 使用开源 Django 应用程序 Sentry 实时监控 Python 错误。

Munin 被用来绘制全系统指标图,并对异常情况发出警报。Instagram 有许多自定义 Munin 插件,用于跟踪应用级指标,如每秒发布的照片。

Pingdom 用于外部服务监控,PagerDuty 用于处理事件和通知。

最终的架构概览图

人均身价超 5 亿的 Instagram,如何用 3 个工程师支撑 1400 万用户_第6张图片

译者后记

在之后的 2012 年,Facebook 就以 10 亿美金收购了当时只有 13 名员工的 Instagram,折算成 RMB 的话,人均身价超过 5 亿。当时 Facebook 可以说是下了血本,但现在来看,这笔收购是 Facebook 至今为止最成功的一次收购。

还是那句话,我们应该采用无聊的技术去构建创新的产品,而不是倒过来。


更多资讯,请关注 Bytebase 公号:Bytebase

你可能感兴趣的:(数据库,运维,DBA,开发者,数据库管理,DevOps)