【系统设计】如何设计 Twitter 时间线和搜索?

 

如何设计 Twitter 时间线和搜索?

1.业务场景

业务场景如下:

  • 用户发布推文
    • 服务将推文推送给关注者,发送推送通知和电子邮件
  • 用户查看用户时间线(来自用户的活动)
  • 用户查看主页时间线(用户关注的人的活动)
  • 用户搜索关键字
  • 服务具有高可用性

其他场景:

  • 服务将推文推送到 Twitter Firehose 和其他流
  • 服务根据用户的可见性设置删除推文
    • 如果用户没有关注被回复的人,则隐藏回复
    • 尊重“隐藏转发”设置
  • 分析

2.业务要求

假设如下:

  • 流量分布不均
  • 发布推文应该很快
    • 向所有关注者发送推文应该很快,除非你有数百万关注者
  • 1亿活跃用户
  • 每天 5 亿条推文或每月 150 亿条推文
    • 每条推文平均扇出 10 次交付
    • 每天通过扇出发送 50 亿条推文
    • 每月通过扇出发送 1500 亿条推文
  • 每月 2500 亿次读取请求
  • 每月 100 亿次搜索

时间线

  • 查看时间线应该很快
  • Twitter 阅读量大于写入量
    • 优化推文的快速阅读
  • 摄取推文写得很重

搜索

  • 搜索应该很快
  • 搜索是重读

简单的对业务要求进行计算,转换成业务指标

  • 每条推文的大小:
    • tweet_id- 8 个字节
    • user_id- 32 字节
    • text- 140 字节
    • media- 平均 10 KB
    • 总计:~10 KB
  • 每月 150 TB 的新推文内容
    • 每条推文 10 KB * 每天 5 亿条推文 * 每月 30 天
    • 3 年内 5.4 PB 的新推文内容
  • 每秒 10 万个读取请求
    • 每月 2500 亿次读取请求 *(每秒 400 次请求 / 每月 10 亿次请求)
  • 每秒 6,000 条推文
    • 每月 150 亿条推文 *(每秒 400 条请求 / 每月 10 亿条请求)
  • 每秒扇出 6 万条推文
    • 每月通过扇出发送 1500 亿条推文 *(每秒 400 个请求 / 每月 10 亿个请求)
  • 每秒 4,000 个搜索请求
    • 每月 100 亿次搜索 *(每秒 400 次请求 / 每月 10 亿次请求)

方便的转换指南:

  • 每月 250 万秒
  • 每秒 1 个请求 = 每月 250 万个请求
  • 每秒 40 个请求 = 每月 1 亿个请求
  • 每秒 400 个请求 = 每月 10 亿个请求

3.系统设计

1.系统设计

img

我们必须进行必要的服务拆分

  • Timeline Service : 时间线服务,获取存储在Memory Cache中的时间线数据,包含用户ID和推文ID

    • TWeet Info Service: 推文信息服务,获取有关推文ID的附加信息
    • User Info Service : 用户信息服务,获取有关UserID的附加信息
  • Fan Out Service:扇出服务,A发布推文后,通知关注了A的所有用户,A发了新推文

    • User Graph Service : 用户关系服务,提供用户之间的关系图,比如A用户关注了哪些用户

    • Search Service : 关键字搜索服务,全文检索(搜索集群,Lucene)

    • Notification Service: 通知服务,向某用户发送推文通知(你关注的用户xx发了新推文)

2.用例实现

用例1:用户发布推文

我们可以将用户自己的推文存储在关系数据库中以填充用户时间线(来自用户的活动)。

我们可以将照片和视频等存储在 Object Store

  • Client将推文发布到Web Server,作为反向代理运行
  • Web Server将请求转发到Write API Server
  • Write API Server将推文存储在SQL 数据库上的用户时间轴中
  • Write API Server 联系 Fan Out 服务,该服务执行以下操作:
    • 查询 User Graph 服务,查找 内存缓存中存储的用户关注者
    • 将推文存储在内存缓存中用户关注者的主页时间线中
      • O(n) 操作:1,000 个关注者 = 1,000 次查找和插入
    • 将推文存储在Search Service中以实现快速搜索
    • Object Store中存储媒体数据
    • 使用Notification Service 服务向关注者发送推送通知:
      • 使用队列(未图示)异步发送通知

内存缓存如果使用redis,可以使用如下结构的redis列表

           tweet n+2                   tweet n+1                   tweet n
| 8 bytes   8 bytes  1 byte | 8 bytes   8 bytes  1 byte | 8 bytes   8 bytes  1 byte |
| tweet_id  user_id  meta   | tweet_id  user_id  meta   | tweet_id  user_id  meta   |

新的推文也会被放在redis中,该缓存会填充用户的主页时间线(来自用户关注人的活动)

$ curl -X POST --data '{ "user_id": "123", "auth_token": "ABC123", \
    "status": "hello world!", "media_ids": "ABC987" }' \
    https://twitter.com/api/v1/tweet

响应

{
    "created_at": "Wed Sep 05 00:37:15 +0000 2012",
    "status": "hello world!",
    "tweet_id": "987",
    "user_id": "123",
    ...
}

内部通信,可以用grpc

用例2:用户查看主页时间线

  • ClientWeb Server发布主时间线请求
  • Web Server将请求转发到Read API Server
  • Read API Server 与 Timeline Service联系,后者执行以下操作:
    • 获取存储在内存缓存中的时间线数据,包含推文 ID 和用户 ID - O(1)
    • 使用multiget查询Tweet Info Service以获取有关推文 ID 的附加信息 - O(n)
    • 使用 multiget查询User Info Service以获取有关用户 ID 的附加信息 - O(n)
$ curl https://twitter.com/api/v1/home_timeline?user_id=123

响应:

{
    "user_id": "456",
    "tweet_id": "123",
    "status": "foo"
},
{
    "user_id": "789",
    "tweet_id": "456",
    "status": "bar"
},
{
    "user_id": "789",
    "tweet_id": "579",
    "status": "baz"
},

用例3:用户查看用户自己的时间线

  • ClientWeb Server发布用户时间线请求
  • Web Server将请求转发到Read API Server
  • Read API Server SQL 数据库中检索用户时间线

类似于用例2的查看主页时间线,除了所有推文都来自用户自己而不是用户关注的人。

用例4:用户搜索关键字

  • ClientWeb Server发送搜索请求
  • Web Server将请求转发到Search API Server
  • Search API Server 联系Search Service,它执行以下操作 :
    • 解析/标记输入查询,确定需要搜索的内容
      • 删除标记
      • 将文本分解为术语
      • 修正错别字
      • 规范大写
      • 将查询转换为使用布尔运算
    • 查询搜索集群(即Lucene)以获取结果:
      • Scatter 收集集群中的每个服务器以确定是否有任何查询结果
      • 合并、排名、排序并返回结果
$ curl https://twitter.com/api/v1/search?query=hello+world

除了与给定查询匹配的推文外,响应将类似于主时间线的响应。

4.系统优化

img1

优化要点:

  • DNS
  • CDN
  • Load Balancer:负载均衡
  • SQL Read Relicas :读多副本
  • SQL Write Master-Slave :写主从模式

关于扇出服务的性能瓶颈:一个几百万的用户A发推文,可能需要几分钟,才能通知到关注了A的用户,A发送了新的推文:

当用户A关注人数到达一定阈值的时候,可以让Client主动搜我关注的A有没有新发推文

其他优化:

  • 在内存缓存中只保留每个家庭时间线的数百条推文
  • 仅在内存缓存中保留活动用户的主页时间线信息
    • 如果用户在过去 30 天内未处于活动状态,我们可以从SQL 数据库重建时间线
      • 查询User Graph以确定用户正在关注谁
      • 从SQL 数据库中获取推文并将它们添加到内存缓存中
  • Tweet Info Service中仅存储一个月的推文
  • 仅在User Info Service中存储活动用户
  • 搜索集群可能需要将推文保存在内存中以保持低延迟

参考:https://github.com/donnemartin/system-design-primer/blob/master/solutions/system_design/twitter/README.md

posted @   西*风  阅读(508)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示