设计系统绝不能只关注眼前死板的技术实现,在工业界实际工作中,最核心的能力之一是预判“近期需求”(immediate near future features requirement)。如果技术人员缺乏 business sense,跟偏重业务的 Product Manager 沟通时就会频遭挑战。例如 PM 提出在用户档案里加一个出生日期(Date of Birth)并支持按生日搜索,工程师如果回答“这得全盘重构系统架构(totally restructure)”,就会给人留下极不靠谱的职业印象。因此在面试或实战开局时,建议至少花十分钟搞清楚业务场景(Business Use Case)与常识边界,第二个十分钟梳理系统约束条件(Constraints),随后再将整体架构、API 与数据模型/数据库选型结合起来推导。
```mermaid
flowchart TD
accTitle: System Design Time Allocation Flow
accDescr: Recommended interview roadmap showing time spent on use cases, constraints, and architecture.
subgraph Prep ["前期明确 (20 Mins)"]
A["Business Use Case & Common Sense<br/>(前 10 分钟:搞清业务背景与近期扩展)"] --> B["Constraints & Scale Estimation<br/>(后 10 分钟:量化约束与计算)"]
end
subgraph Arch ["核心落地"]
B --> C["High-Level Architecture<br/>(划分子服务/积木形状)"]
C --> D["Database Choice & Data Models<br/>(积木材质:MySQL vs Cassandra)"]
D --> E["Deep Dive & Bottlenecks<br/>(容量瓶颈/并发控制)"]
end
```
分布式系统设计本质上就像“堆积木”。面试官如果提出要求搭建一座埃菲尔铁塔,并要求塔顶能承受一公斤重的旗帜或糖果:
- **积木的形状**:对应 API 设计和数据结构,必须拼装出契合埃菲尔铁塔外观的轮廓。
- **积木的材质**:对应存储选型与底层基础设施。如果要承载 1000 万用户同时在线并发,你就必须深刻理解 MySQL 与 Cassandra 等不同积木的物理特性与抗压强度差异。
在架构评审或 Hacker News 的技术讨论中,常见的质疑(disagree)通常分为两类:
1. **根本性不可行(Fundamentally not working)**:系统在理论或逻辑底层就存在硬伤。讲者分享了自己的一段真实经历:曾花了一个月时间做方案准备,信心满满地向杰出工程师(Distinguished Engineer)汇报,结果被当场指出并发控制中采用的乐观锁(optimistic locking)机制存在根本性漏洞,完全无法正常 work,方案被直接打回重做。虽然这种场面非常尴尬(embarrassing),但能在架构评审阶段从理论层面提前拦截错误是极其幸运的("You're blessed, you're fortunate to find it from system design level")。代码级 Bug 可以短时间修复,但架构底层的重大缺陷一旦上线,纠偏往往要以年为单位计算。
2. **缺乏最优解的质疑(Not the best solution)**:评审者认为这未必是最佳方案,但自身也拿不出更好的解法。因为现实中的系统设计充斥着各种约束与假设,很多时候只有全知全能的“神”(Oracle)才知道绝对最优解。如同 Google 研发的 AlphaGo 横扫职业棋坛,围棋解说常感慨或许存在技艺更高的“围棋之神”(OracleGo),但人类眼下造不出比 AlphaGo 更强的棋手。只要某种方案是当前边界下最强的 working solution,就是极佳的设计。现实中许多通用工具(如使用起来较为死板的 DynamoDB)并非完全契合特定业务,除非是底层 Infra 团队有资源去彻底重复发明轮子(reinvent the tool),否则大多数业务团队必须在现有工具上做出妥协与权衡。
最后,API 体系必须严格划分为外部公开接口(Public API)与内部微服务接口(Internal API)。面向移动端与 Web 客户端的 Public API 侧重业务表达(如单聊/群聊发起、基础群组元数据 CRUD、群成员进出);而 Internal API 则专注于后端管道间的长生命周期协作(例如内部的 `processMedia` 任务分发与进度回调)。保持内外部 API 的解耦与边界清晰,是保障大型分布式聊天平台可靠演进的核心基石。
```mermaid
flowchart LR
accTitle: Notification Push Pipeline
accDescr: Pipeline showing notification delivery from message queue to mobile devices via third-party providers with DB tracking.
在讨论 User Table 时,通常用户基础信息的更新频率极低,但在类似 Uber 寻找可用司机、或聊天应用展示全量在线状态时,不应在主表上频繁原地更新行数据。推荐将高频波动的状态拆分出单独的 `active_user_table` 以避免行锁竞争。
对于好友关系表(Friendship Table),存在一个经典设计难题:A 和 B 是好友,意味着 B 和 A 互为好友。关系本质上是对等的双向图,但在分库分表环境下无法同时根据两个 User ID 进行哈希路由。如果一张表只存一条单向记录(如 `user_id_1 < user_id_2`),当需要查询某个用户的所有好友列表时,系统就必须对所有 4096 个分库分表执行广播式全表扫描(Fan-out Query),系统开销不可承受。
因此,架构中采取了**双向冗余存储(Duplicate Rows)**的设计:
1. **Sharding 路由友好**:当 A 与 B 建立关系,表内插入两条记录:一条以 A 作为 `user_id_1`,另一条以 B 作为 `user_id_1`。整个 Friendship 表统一只以 `user_id_1` 作为分片键(Sharding Key)。这样查询某位用户的所有好友时,仅需定位到该用户所在的单点分片即可单次读取完毕。
2. **独立的未读游标(Last View Date)**:好友表中包含一个核心时间戳字段 `last_view_date`。这个字段决定了未读消息红点与历史断点的判定:
```mermaid
flowchart TD
accTitle: Friendship Table Row Duplication
accDescr: Diagram showing how A and B friendship is stored in two separate rows with independent view dates.
subgraph Storage ["Friendship 冗余双写"]
R1["Row 1: user_id_1 = A, user_id_2 = B\nlast_view_date = 10:00 (A 查看的时间)"]
R2["Row 2: user_id_1 = B, user_id_2 = A\nlast_view_date = 10:05 (B 查看的时间)"]
end
Q1["查询 A 的好友列表"] -->|命中 Shard(A)| R1
Q2["查询 B 的好友列表"] -->|命中 Shard(B)| R2
```
未读消息绝对不能把“谁读了、谁没读”的状态记录直接打在 Message 实体上。如果是千人甚至五千人的大群,一条消息需要维护数千个用户的阅读状态位,且群成员随时动态进出,存储和并发写回的复杂度将彻底失控。利用 `last_view_date`,任何大于该时间戳的消息均为未读,A 看对话和 B 看对话的游标彼此独立、互不干扰。
---
[01:00:00](https://www.youtube.com/watch?v=-UXWETzHG38&t=3600s) 群组成员未读状态(Group Last View)与高并发读写性能考量
面对现场提问“Slack 是否有像微信一样的 1 对 1 好友体系”,讨论指出:虽然 Slack 核心组织形式是 Channel 与 Direct Message,但底层机制与微信、Telegram、Facebook Messenger 别无二致。所谓“好友表”,其底层元数据就是 Direct Message 的两端映射与游标记录,属于同一类抽象。
针对有学员提问“如果在某个频道停留很久,最后浏览时间(Last View Date)会不会不准确”,讲师指出可以通过双重上报来解决:客户端在进入(打开)频道时更新一次 Last View Date,退出离开时再更新一次即可。关于“Last View Date 能否只保存在客户端”的疑问,讲师明确指出这在多端设备(Multi-platform)场景下是行不通的:用户如果在电脑端看过了最新消息,换到手机端打开时,由于客户端本地数据不互通,未读状态便无法同步,因此必须由服务端统一持久化。
```mermaid
flowchart LR
accTitle: Message Write and Channel Update Flow
accDescr: Message flows from sender through Channel Service to DynamoDB, returns ACK, then updates Redis Channel Table.
subgraph ClientStage["客户端请求"]
C1["发送方 (David)"]
end
这种设计也存在一个明显且略微反直觉的缺陷:无法直接根据 Message ID 主键单点查询一条消息(`Get Message by Message ID`)。不过在实际聊天业务中,通过单一 Message ID 独立查询单条消息的频次极低;如果真有此需求,通常需要借助全局二级索引(Global Secondary Index, GSI)。然而 GSI 大多是异步构建的,往往存在一秒左右的最终一致性延迟(Not immediately available),刚插入的消息无法即刻根据 ID 检索出来。
解决或缓解乱序的常见手段包括:
- **按会话分片(Sharding by Session)**:在网关请求层或者数据库层,强制将同属两人(或同一个群)的所有读写请求固定路由到同一个微服务实例或同一个数据库分片(Shard)。甚至直接依赖单机 MySQL 插入行时由同一物理机时钟自动生成的时间戳,这样冲突概率会骤降。
- **时钟回拨与单调性现实**:即使限定在单节点时钟上,机器本地时钟仍有可能发生跳跃(Jump Back)。讲师直言,除非底层使用像 Google Spanner 这样依赖原子钟与 GPS 硬件级别 TrueTime 的强一致性服务,否则在海量分布式数据面前,“消息绝对时序是永远无法百分之百被保证的(Message order is never guaranteed)”,乱序在概率上必然存在。
在数据建模中,是否可以将私聊也视作只有两人的特殊群聊(Single/Double-member group message)?讲师肯定了这种统一抽象的可行性,但也指出了底层的**二维查找路由两难(Two-dimensional Lookup)**:
- 如果数据库按 Group ID 做了分片(Shard),查找一个群里有哪些用户非常直接(直奔目标分片);但若想反查“某一个 User 到底加入了哪些群”,客户端将无法精准定位物理分片,只能暴力广播扫描全部 1,000 多个物理分片,效率极其低下。
- 解决该问题必须付出代价:要么进行双写,即按 User ID 和 Group ID 分别维护两张独立分片维度的映射表;要么自建全局二级索引(Global Secondary Index),先查出该用户所在的物理分片集合,再做局部精准查询。
此外,针对学员提出“能不能直接把加入的 Group ID 作为标签数组(Tag)塞在 User 记录中”的设想,讲师直接给出了否定意见,指出这是严重的架构暗礁:一旦用户是业务高频核心人员(如公司 CEO 每天被无数人拉进各种临时临时群、移出群聊),系统就必须频繁并发原地更新(Update)该用户的同一行记录(Row),这会瞬间造成严重的数据库单行锁竞争与热点行(Hot Spot)阻塞,引发全盘性能雪崩。
```mermaid
flowchart TD
accTitle: User to Group Routing Dilemma
accDescr: Shows the trade-off of sharding by Group ID versus User ID and how Global Secondary Index bridges them.
subgraph DirectLookup["按 Group ID 分片"]
G1["Group ID: 888"] -->|"精准定位"| S1["分片 Shard 1"]
S1 -->|"包含成员"| UList["[User A, User B, User C]"]
end
subgraph ReverseLookup["反查 User 在哪些 Group"]
UA["User A 请求其加入的群"]
UA -.->|"若无全局索引: 必须广播全部分片"| S1
UA -.->|"广播扫描"| S2["分片 Shard 2"]
UA -.->|"广播扫描"| S3["分片 Shard N (1000+ 个 Shards)"]
end
subgraph Solution["优化解法"]
GSI["自建全局二级索引 (GSI)\n或双写按 UserID 分片表"]
UA ==>|"先查索引获知所属 Shard"| GSI
GSI ==>|"直接提取"| S1
end
```
## Video description
This is the first system design session focus on design slack.
doc: https://docs.google.com/document/d/1h3xoOvewFLf0KJGCNRhFV9HiK1W_ehHc6wG4bjUPTTc/edit
[0:00] Scott Shi: 这得全盘重构系统架构(totally restructure)
[3:45] Scott Shi: You're blessed, you're fortunate to find it from system design level
[25:00] Scott Shi: 在同一个数据记录上进行高频重复更新(Repeatedly update the same record)
[40:00] Scott Shi: 它 work 就 work
[45:00] Scott Shi: 会把你的 performance 降低至少一个数量级,慢了十倍
[75:00] Scott Shi: 消息绝对时序是永远无法百分之百被保证的(Message order is never guaranteed)
[78:40] Scott Shi: 经不起两个明星同时出轨
[90:00] Scott Shi: 无论是 Facebook Messenger 还是微信(WeChat),其核心设计思想和底层脉络都是相通的
[92:15] Scott Shi: 以 Data 服人
[94:45] Scott Shi: 从 SQL 数据库引擎本身的视角来看,它压根不知道什么是 Data Partition
Original transcript
[0:00] 好,然后我先录上音,我把这个folder再发一下,然后大家有空可以看一下然后In case,你没有直接link,然后我们这次呢,我这样propost了一个solution,然后最下面有个Q&A如果你有什么问题的话,不是很urgent的问题,比如说你觉得我这个design不work,你要觉得design不work,你就直接提出来,或者你觉得不是urgent的question,你就直接提到Q&A,然后我们work through这个design以后呢,会go through Q&A谢谢大家好然后我接着谈,就是说很多时候就是如果没有额外的要求,特别面试的时候尤其如此,就是怎么样怎样怎样来。然后但是在比如说工程中,比如在工作中的System Design的话,你需要做一些额外的事情,比如说你要分析可能的很快的,就是immediate near future feature,Near future features requirement,这个是非常重要的,因为如果你比如说你设计一个系统,很多时候跟你讨论的人不是technical oriented,比如说product manager,当你说让你加个额外feature,比如说在user profile里面加一个birth of date,然后能search based on birth of date,然后你说我们必须totally restructure,或者totally那这样的话就会给人一种很不靠谱的印象,所以说你就是当你design的时候,你很快要fool-seen,就是很多near future 的requirements,所以说在system design 里面,就是 business sense 非常重要,你一定要理解就是你的 business sense 所以包括在interview的时候,你可能需要花10分钟时间去搞清楚你的business sales case和business sense然后呢,就是第二个十分钟可能要花在这个constance上,然后就是architecture,api,database choice,data model,很多时候你architecture和database choice可以放一起,就是当你画完那个graph的时候,你就知道就是用什么database了。所以你这个后面的时间其实还是要稍微长一点,但是我还是强烈建议大家花点十分钟时间,去focus on the use case和 common sense,还有 business use case,这是非常重要的,就是所有的design都是business oriented,所以business sense非常重要非常重要啊,然后这个后面的Design呢,其实有一系列就是有迹可循,我喜欢把这个design变成堆积木,就比如说面试官让你堆一个埃菲尔铁塔,然后面试官要求埃菲尔铁塔上面能放一个1公斤的旗帜或1公斤的糖果,是吧?那么你就得选择,首先你得看这个埃菲尔铁塔形状是什么样子,是吧?你得选择积木的形状符合埃菲尔铁塔然后这是相当于选database,选API design,这个形状向FH大。然后同时有一些subtile要求,比如说这个user要求10个million user同时上来,那你就必须对你的积木的材质有所了解。那你就必须对你这个比如说你这个database我到底选用mySQL呢还是选用Cassandra呢,我们必须对这个积木的材质有所了解,所以说这个system,distribute system design和那个传统的engineering design和对积木其实没有本质的区别,就是你知道你的tool是什么,然后你把它tool combine起来,然后然后堆层一个能解决问题的积木能堆层一个符合甲方要求的积木符合面试官要求的积木然后其次的话在System Design中你会发现你看到System Design work不work,比如说,Present your system design,用你的common sense,用你的back knowledge,想它work,是难度是远远低于你自己设计一个的,知道吧?所以面试关键System Design中,其实工作会远远比被面试的人要容易,因为你只要看这个人设计的容易,就是work不work就行了,满不满足,说要求checklist就可以了,但是你要设计一个就很难,所以说这个就是Hacker News,我喜欢读Hacker News,Hacker News上有一种成熟的观点就是挺多人观点,包括我有时候在跟组里面讨论的时候说,System Design有两种disagree一种 disagree是我认为它是不正常的工作。 基本上,它不正常在做的。这种是一种design,这种一般你就得搞清楚为什么fundamentally don't work,有可能是提问题的人有问题,提的问题有问题,有可能是你的设计有问题,这是非常常见的。我有这种经验,就是我设计的一个系统,然后fundamentally didn't work,然后被打回去从设计的,这样是很不错的,因为你从series的角度,
[5:00] 就说你的System don't work,有点 embarrassing,但是确实有这种情况,就是我花了一个月时间准备,然后去给那个Distinguished Engineer President去了,然后说这个不 work,然后想了想确实不 work,有个optimistic locking的一个,就是concurrency control的一个设计,确实不 work, embarrassing,但是你花一周以后再解决这个问题的时候,你会设计一个从Series去work的系统,因为很多时候我觉得,就是比如说,你发现BIG的话Coding 的 bug 你都是能解决的,但是System Design 的 bug 你除非 architecture 那可能花以年来计算了。所以这个如果能从 theory 的角度就发现不 work 的话,那需要就是,you're blessed, you're fortunate to find it from system design level。然后另一种disgrade,我们说过两种disgrade,一种disgrade是 fundamentally not working,另一种 discrete是I don't know what's the best solution, but I don't think your system design is actually a best solution.就是他不知道你这个design solution是不是best,但是他觉得你的design solution不是best,如果他能提出Best Solution,那当然就好了,但是很有可能很多时候提不出Best Solution,为什么提出Best Solution呢?因为你这个System Design是有很多Construents,有很多假设的Assumption的,可能只有Oracle,可能只有Oracle知道Better Solution或Best Solution,就比如说你下那个,围棋像alphaGo就是出来砍瓜切菜,就是Google研发的alphaGo砍瓜切菜。但是我看了好多职业围棋的评论说,他们假设有一种叫围棋之神的话,就是Oracle,OracleGo,那觉得alphaGo是不到围棋之神的水平的,但是他没有办法去beatalphaGo。你知道吧,就是没有办法去beat AlphaGo,但是他们觉得AlphaGo没到围棋之神的水平。那他就是最强的但是没有办法,但是他没有就是最强的,但是他们不是最优的。对吧这是那种,如果是这种的话,像AlphaGo别人觉得别人不赞同他是最强的但是别人早不见比他强的,那就是一个最好的design,至少现在是最好的design。但是还有另一种情况,不是best design原因,是best design一般是要求你下面积木非常customized,但是我们看到已有的比如说database啊,已有的tools啊,其实它很多都是非常僵硬的,就是比如说denomodb就用起来非常僵硬很多时候。这是有多种原因的,所以你不是bestdesign很有可能是因为已有的tools不是能 support 你变成 best design 是吧?你可能会有个 better solution 如果你 reinvented tool的话 你可能会有个 better solution 但你不可能花时间去 reinvented tool 除非你是做 infra 是吧?做 infra 你可能就是根据你的 case 来 fundamentally reinvented your tool但是很多时候你没有这个时间,所以你必须将就一下,所以说这种的话你就可能不是best solution,但 is a working solution.所以说,包括不管是工作的时候还是系统面试的时候,要分清两种一种是这个design fundamentally not working,那你就该discuss,该改改,然后另一种是It's not the best solution,但是我可能没有想到better solution,或者是我觉得有可能有better solution,但我就是总是没有想到,那其实这个是design to work,你到时候跟面试官讨论的时候也可以这么说,就是我觉得,I don't think my design is fundamentally incorrect,but I think there might be a better solution in this one,but I couldn't find one.你就说可能会有个围棋之神下的比α够好,但是短时间内早不见是这样子的所以说System Design只要有找一个working solution那就很不错了好,有什么问题吗?大家有什么问题吗?我觉得有什么想法,有什么问题?好,那我继续了。我们上周讨论了一下Design Slack,满足以下几个functionality。通过有Direct Message,我们当时谈的这个Requirement就是Direct Message可能有 group message 或 individual message 然后您可以有 link preview然后你有 multimedia message,然后 message notification, highlight on-rate message首先就面对这个,如果从这个Design的这个System Design Interview的Trix来讲的话,就是你觉得首先这个问题就太大了。很多时候我会拿这个问题问三年工作以上的同学的,就是你遇见这个以后你会发现这个问题太大了,说它会reduce it to a smaller group,就是把它分成一个MVP feature和bonus feature,这是第一点。所以我们到时候可以说我们MVP feature是以下几个,然后bonus feature是剩下的。然后其次呢,你要分析出这个有什么assignment,是在这个design里面,因为所有design问题都非常非常非常笼统
[10:00] 你要导清楚一些 assumption 比如说你可以说这个里面我们就assume有 authentication authorization done 了 你这个shoulder request都有个user token就是Verify A是 A, B是 B了然后你也可以信我们说这个Mobile方面或web client方面就是不在这个design讨论中,我们这次所有的design discussion都没有mobile和web client,因为我对这块不熟。如果有同学改进去organize办法,我也会挺感兴趣的,但是我对这块不熟,所以我可能就bypass所有的mobile和web client的design。它们有不同的 product designSystem Design好,然后另一点的话,在这些都搞定了以后,你要明确你这个Design是为什么目的服务的,我觉得上次那个孙长同学说的很好,说这个Slack首先是要high available就是什么时候在在线,然后 high reliable 就是你Message不应该lost 是吧 然后Consistent 你不能就是Consistent的话 最后就是你不能出现这个inconsistent message 是吧,就是A和B看的不一样,这样会出现很多问题,然后最后一点是real time,就是message应该deliver相对比较及时一点,然后我们上一次讨论的时候呢是,讨论了一个就是把它量化,这块前面都是定向那个问题,然后讨论一下这个Use Case,然后把这个问题量化,量化比如说我们可能有Daily Active User可能是10 million Daily Active User,然后我们这时候就做了一些简单计算假设一个user每天发100个信息的话然后高峰时段是平均时段的5倍的话,那我们平均下来的message per second是5万个message per second,这个系统需要处理5万个message per second,然后这个throughput是假设一个message 100byte,100byte可能稍微有点小,但是假设的话就是差不多throughput 100MB/sStorage 需要10个TB的Data然后我们这会还调了一些其他的条件,比如说一个group里面有多少个member,我们说可能1000个member,这个都是条件,你要跟你的面试官问这个问主要focus上两个方面,一个是QPS比如说这块QPS就是message角度是5万个message每秒,然后从data的角度是100个message每秒,然后另一个角度就是total data size,然后你这个storage可能就是10个T,这块应该是assume你有time to leave的,有的message app是没有storage limitation的,它是guarantee forever store store message的,那个的话可能就得,那个的话可能就比如说discord,那种你就可能得计算10年以后的storage有多少或者是五年之内的Storage growth多少,因为你可以通过其他方式,虽然你是stall all message,但没有人会看两年前的message,一般来说。所以可能有通过其他的优化的方式。然后我看到一个常见的问题就是有同学问说,怎么样来判断这个数值大小,该什么时候做什么了?这个其实这个数值是一直在变化的,我觉得常见的 rule of sum就是,如果你有超过1000个message, QPS 每秒的话,那你多半是要多于两台机器的,首先你不能只有一台机器,因为只有一台机器有Single Pied Failure,所以你一定要至少两台。但是如果你是要多于1000的话,1000到10000之间的话,你多半是要把它在就是further partition,但是这个只是一个大概的数字,就是比如说1万是基本上一定要做partition的,就需要就是多台机器来处理的但是这个数字其实一直在变的,我这块列了一个Further Reading,这个是Expensify的CEO写的,非常有趣,他是用一个非常powerful的single,Sequal Lite,一个非常powerful的single signal light,然后可以处理4个million的QPS,它是用192个核,然后这个storage是3个T的SSD,然后一个T的memory,它是可以处理4个million的QPS,但是这个所有的QPS都是read的,它有write的话,可能会更复杂一点,所以你就可以看到,你如果在AWS上做一个相似的机器,我查了一下,大概一年花24万美元,大概一年花24万美元就可能比这个稍微差一点因为它这个机器实在有点过于powerful了这是18年1月分析的,这个机器过有点powerful了,可能需要24万多美元。但是它可以达到4个million的QPS,就是一台机器,Skill up可以到4个million,所以感觉很有趣。这篇文章我也很适合读啊,我觉得它design非常泥石流,就是不用DNS,然后把说的那个server lookup放到那个exactual host里面,然后换成一个JSON,然后copy它那个configuration,就是SCP copy。
[15:00] For example, besidehub DNS, domain name server loophub service. I think the design is very nice, but work is very nice, I think it's pretty interesting. So you can see, you can probably see that if you can scale up, 4 million will be able to reach out, but for Ray, 1 million will be able to reach out. So this is a very expensive skill up. And you can probably say that if you're a tablet, you can use 1000 to 1 million. And this one million is only read, right? You can use it for a little bit. Um, So, usually, if you have 1000 of the KPS, you need to do a partition. And the other thing, You have to look at data. There are QPSs very low. For example, I do data. It's only three hours, but every request is 2 gigabytes. So you can read a minute to 10,000,000 requests. And then you can read a byte of data size. So you can read a little bit more. But your data size is a big. So you can see if data size is QPS, or bottleneck, or QPS. to bootle neck, you know, you're gonna be able to do it. And storage size, For a MySQL server, it's about 500 gigabytes. 500pkg, it may be a little more small. Like the skill app server, storage is 3TB. It has to be divided for the system and other things. You can't use it all, because you have other needs. So you have a device, I think it's more reasonable. I will talk about B-tray, B-tray is 3 or 4. If you have B-tray level is 3, it's about 50 gigabytes. Estimated data size. If you have B-tray level is 4, it's about 50 gigabytes. Constraints should consider every device network bandwidth. This is what we should consider. But this is not the main limitation. There are some people who are dependent on that. A lot of these are not the limitations. Why is it not the limitation? Basically, your limitation is usually the database read and write Because you have to be asked for disk, so the response time is the most slow. Network bandwidth, no, you're not sure what's going on. You know, you're not sure what's going on. uh If you think about the constraints, it's CPU utilization rate, memory utilization rate, disk utilization rate, network bandwidth, from the computer perspective. And if it's over 50% then it's usually less than 50% or 40% because you may have data center fill over. If you have 50% of the time, you need to horizontally scale, you need to open up a different device. How? I can see there are some people who are using this. You can sit here, it's fine. Let's talk about architecture. This is the design of the architecture. It's much more complicated than the first topic. It's also more complicated than the other topic. If you have any questions, you can ask. We break up from micro services, break up from many. For example, you have account services. It's manager user account information. You have a user service, maybe manage user, friendship information. You may not have Slack, because you have Slack, you can talk with any other organization. But you can use WeChat in the chat. or you can also use verify friends You can call a group service, manage group membership. Media Service is a network of multimedia and linked information. Message Storage Service is a network of messages. This is not responsible for the information. Notification Service is a service for notification users. And Channel Service is a service for Master Service, so you can use the request from user to channel service. This is my design. If you have a design, work, then it will be fine. And the last one is Message Search Service. This is the message searching. So in this design, we all break into small parts. So it looks more complicated. There are many micro services. But if you want to see all the functions, it will not be very complicated. And in architecture, we will talk about the database. In this case
[20:00] 我们先讨论简单的Account Service, Friendship Service和 Group Service呢,这个可以用SQL来存储它的Data,因为它就是非常relational database,比如说这个Account的一些metadata information,然后谁和谁是friend,谁在哪一个group里面,这个可以用SQL来存。然后因为它这个Lookup有非常多的,显著的最新的Data Lookup更frequent的所以你可以在上面加一层Redis和Mem Cache 做Caching所有的Caching都是优化 你可以最后再提啊你可以先设计一个working solution 然后最后加Caching一般加了Caching以后你的Lookup Time会缩小至少一个数量级是10倍到20倍甚至100倍也可能所以这是开现的主要目的然后Medium Service的话比如说你upload一个message或upload一个video的话说你要uploadAmazon S3然后你可以在上面加一个CDN, Content Delivery Network这个是为了更快的deliver data的谢谢谢谢大家这个是为了更快deliver data的请这位同学mute一下如果你有问题的话请问然后 Notification Service的话,你可以通过传统的Storage去做,你可以把它变成一个API。然后靠 external notification 或者你可以做成 Kafka,Uber 是有个 Kafka notification service,你只要往这个 Kafka message queue 里面卡夫卡这个message topic 里面发message,然后他就会guarantee delivery,然后这个notification这块exactly wants是非常复杂的,因为很多时候你要设计靠一个 third party API,比如说一般 notification 的话,比如说 Apple 它的 Apple notification API,然后然后Android有它自己的API,然后如果你要Exactly wants的话,如果你要用Exactly Wants的话,你需要做Two-Face Commit,非常复杂,not worth it,除非你有这个需求Exactly Wants,所以一般不做Exactly Wants,你如果出现了Duplation Notification, that's fineMeshed Story Service的话Message Story Service的话,这块的话就主要存储Message的,我是用Amazon的DenimalDB存储的,这个DenimalDB的话有多方面原因,一方面原因是DenimalDB呢,就是它Originally support horizontal scale,它基本上可以 almost can be infinite scale,但DenimalDB在工业界中已经常见,就是scale to a thousand nodes,然后就是它skewability相对来说,你比sql来说,sql其实你也可以infinite scale,但是你就是不需要花那么多额外的精力去管理它,然后其次的话DenimalDB的API呢对这个message storage有额外的好处,我们会稍后讨论。你有它那个Zaml的定位,Zaml的那个cacher叫DAXDenemo是closed source,是AWS版的Cassandra,Cassandra是open source版的Denemo,但是中间有一些不一样,就是它有一些implementation details不一样,比如说它那个Hinterly handoff是一样的,但是它conflict resolution,Cassandra是用last right wing的,DenemoDB是可以支持其他的不同的conflict resolution logic,但是很多地方是相似的,然后其次的话DenemoDB其实没有开源,你只知道它大概的设计,它有一个Denemo paper,我会把它打开,是就很简单一读啊,你知道它的API,但是还有很多地方它其实细节是不知道的。然后其次的话,最后一点的话是,DenableDB的Secondary Support比较好,Cassandra的Secondary Support不是太好Secondary index support不是太好。然后就是你可以再,然后我们接着谈,这个message呢,读的时候你是很有跟时间性相关,你很常见的是读最近的message。所以你可能要在message上面加个cashCashLayer,然后Dynamo有自己的CashLayer叫DAX,然后如果没有的话你可以自己加一个Redis也可以的,这个也可以加个CashLayer。我们说加CashLayer有多种原因,一种原因就是它不skill。那你要加CacheLayer,一种原因是Reduce Cost,比如说他这个Access有fundamentally有迹可循,比如说他就是经常是读最新的Data,比如说最新的数据,那你可以加个Cache,然后可以减少Cost,或者是他那个反应时间太长,你可以加Cache加Cost。Twitter这个Caching做特别好,Twitter有很多Cache文章,写的非常非常好Twitter有很多blog讲他用caching的经验他们很多时候加了caching以后能减少90%的traffic就是看Twitter的文章Twitter对caching那块有很多优化因为很多时候你发一个message Twitter以后你其实很少去modify他们发Twitter是常见的一个操作包括你modify其实也可以cache是吧modify总体来说这个Twitter是它开始里面做的比较好,你如果读开始有兴趣的话,你可以读一些推特写的文章,写的还是很不错的。
[25:00] 然后这个你Message写完以后,你可能需要Asynchronize用Message Search Service去index一下,然后这个主要是为了search,比如说我要search一些Message,然后它背后呢是用Elastic Search这个Engine来power的,也是用Losing,估计能力 然后这个 elastic search 用 inverted index比如说我说一个我打了一个message说System Design is fine然后就是article a 然后又打了一个message说啊I like chocolate,就是message 2,然后它那个inverted index呢就是说当我search i的时候I就在Message1和Message2同时出现了,然后在Message1的Offset是什么,在Message2的Offset是什么,它就是做这个Inverted Index。原理比较简单,不是很复杂,你可以自己search一下。然后这块的话,我又加了一个额外的EmojiService,就是你可以在Message后面加Emoji,我觉得还是挺Pock的一个feature。这个你可以放到Redis里面,因为这个Redis有一个fundamental issue是Redis有个fundamental issue是什么呢?它就是不reliable,在data可能会loss,但是emoji的话,loss就loss了,无所谓。为什么emoji services separate from message storage system?好问题。我觉得在system design中有很多我们之前说过的big red flag大约大约其中最大的一个Red Flag是你repeatly update the same record如果你把这个emoji放到这个message里面,那你其实在repeatly update这个message。那这样的话就是一个非常大的Big Red Flag这个人的design功力很有问题,你不停的再update一个record。还有另一种常见的就是system design中很大的一个red flag就是比如说你在这个Uber,Design Uber里面,你会不停的说,我想看一下这个Current User的GPS Center,在哪个location。然后如果你把这个GPS的location存到user table里面然后你就不停的update这个user帖帮你的这个role,那也是非常大的big red flag。所以这个emoji service separate from message service,因为message很少去update,但是你如果把emoji和message从story的角度连在一起的话,那你就不停的update同一个message,就是big red flag。好然后我们看到还有同学说这个Friends service use graph database,不用 graph database,因为这个graph database是擅长叫search app of friends of friends,我建议你,这块有很多衍生阅读啊嗯这块有两个衍生阅读我觉得你可以对Graph Database的理解比较深,一个是Design Data Intensity Application里面在Data Model那块关于Graph Database的一种常见的那个Model描述和他常见的use case描述你会看到他最擅长的是search friends of friends或者是search 就是那个nested search这种你可以用MySQL来实践,你可以用SQL实践,MySQL支持不太好,Postgres有一些非常powerful的feature可以实践,Postgres有一些非常powerful的feature可以实践Graph database。所以很多时候,你包括Facebook是典型的Graph的一种case,他也不用Graph database,他用的是sharded MySQL,他也用的sharded MySQL,像Uber一样用的scheme类似用sharded MySQL,因为所有的Graph那个Graph的Application 即是你是GraphLookup GraphApplication你可以用graph database 但是很多时候你可以用这个normalized database也可以实现并且没有本质上的问题,没有说这个就是非常大的performance degradation并且这个更 reliable 一点对,这个emoji是给message.3,counter。所以你首先要了解这个graph database,然后知道它为什么用graph database,然后如果你没有时间去了解这个graph database,你可以这么说,这句话是绝对不会有问题的,就是说我读了这个taught paper,就是Facebooktaught paper,说我觉得所有的graph的问题可以用normalized的messicle来解决,没有fundamentally red flag,这是非常好的一个回答。然后这个Channel Service需要一个Redis去Quickly Initialize OneWap,我们会讲一下这个Channel Service的一会儿会讲一些细节,然后包括为什么讲用Redis来这个catch 就是可以quickly lookup好,我们现在结合这个,有什么question吗?在这个在这个architecture这块有什么question吗我有question 就是和那个channel里chat聊的那个question一样,emojiupdate为什么是update,非常会频繁呢?emoji在我们看来不是emoji的每个emoji icon是不变的,你只要把它放成一个text,类似于text in band到message里面就行。哦,这个是like的意思是吗?
[30:00] 对啊,这就是Emoji啊你看到如果这样的话,如果update到同一个message里面的话,像我们大的组的话,几千个人同时在update,可能有1000QPS在一个data上不停的update。我们是以为是在那个message里面本身inband的emoji。不是的,那个其实就是message,比如说在这块发一个emoji,这和message没有本质区别。这就和Message没有本质区别它就不是emoji,这个就不是message emoji service,这就是message,说白了是一个你看的是emoji,但实际上它就是一个sum up这种你想像这就是一个tax?嗯对对Emoji本身存在哪里,Emoji这块提了Redis 0,我们之后会提这个就是它那个Storage 和怎么样存然后我们之后会贴,然后就是DevDev一下谢谢大家对就是每个message被点赞的意思好,我估计没有问题了,我们来看一眼这个API。这个API呢,我自己有时候也容易写漏,所以我还仔细研究了一下这块。比如说当你打开这个界面的时候,就是我这个界面,打开这个界面的时候你实际上是有个Most Recently Channel就是你的Most Recently Channel 或者Last View的Channel然后你有丽丝的 channels就是List of channels然后有一些是 direct message,有一些是 group message,然后如果你有Unread group message的话,你还会就是highlight一下然后有人mention你的话你还会有个number是吧然后这是你的这个 service 所以你这块 API 呢你可能最常见的一个API就是getChannels,然后给个UserID然后你需要start last view channel你这个就是你return的这个data model呢你要有last view channel然后呢你需要install visible channels比如说哪些channel是visible的 然后包括这个就包括什么呢 recent direct message membership group你需要收 unread message对吧,你需要说onread message,我这块没有,我没有列的很细啊,这块应该的话,你比如说你可能会选择一个不管是 REST API 我建议大家面试用 Swift 或Swift 或者是 其实Swift 没有那个GRPC 好 可能用GRPC 这种比较好一点如果你习惯用 REST API 用 REST API 也可以 但是你这块应该把那个Data列的细一点,就是你比如说,input payload是什么,output payload是什么,我这块就省略了,我觉得这个不是难点所在啊Protobuf比Swift要好一点,Swift的英诡过于复杂,然后我现在看大家工业界的普遍观点是觉得Protobuf是比较好的,Swift问题相对比较多一点然后并且Protobuf我感觉好像Document相对比Srift好一点有各种各样原因,它说白了是个encoding,但是总体来说,感觉ProBuff的项目可能比Swift更 popular一点。但是这个popularity也是风水淋流转的然后你进化的话还要show onread message 和 last message preview这是你这个就是一开始就是你打开那个页面的一个API,GateChannel,给一个UserIDreturn data,我们稍后会看到这个在database里面怎么存,然后你可能有一个message,比如send message,比如说我可以有个人自己说,send message,是吧,比如说hello什么的。然后这个里面有什么呢?比如说有Sender ID, Receiver ID, Message Type, Message Type可能是Direct Message还是Goof Message然后还有media ID,media的话比如说你可以就是连一个videoAudio或者是URL然后在Internal里 你的Channel Service 我们讨论过Channel Service是User Face-in Service这是Channel Service,你的Channel Service会把它append很多额外的MetaData,比如说什么时候create的什么时候updated 什么时候expired然后State是什么?State可能是received的,比如说这个Message的server端received掉了,这个Message的server端send给recipient了,这个Message的server端的recipient,就是你的responder read了然后这个MessageDeleted然后这个有的是有requirement的,比如说你用whatsapp的话他会明确的告诉你这个message什么时候send了,然后这个message什么时候view的了,就是额外的feature为什么要分成receive和send呢?因为你这个有的multimedia language,multimedia message你需要额外的做
[35:00] Close as a. You may need to add some additional details, like you need to fix it, add a thumbnail, add a photo of the photo. For example, it may be a URL, you need to crawl, preview the URL. So you have to do an additional solution. So in this situation, you will need to receive and then send You need to receive and then you can send it. So if we look at this message, this message is a good feeling. If the user is just signed, the user is just signed. If it's just a single time, it has had a 4-day update. The first one is received and sent. If you need to fix it, you need to receive. Then send it to the user and then send it to send. And then again called viewed and then again deleted If you don't have the features, like what is the view, what is the sound? You can receive it, not okay. Receive it is not okay. Like, if you send it, you will send it. If you send it, you will send it. If you send it, you will send it. If you send it, you will send it. If you send it, you will send it. or the same thing, the server is not in the same way. Just to show me server done, no received. So the same thing is very small for the user to update the same message. There are 90% cases of the cases that only one update. Then you can update message and update message and delete message. Then you can upload files, for example, I want to send a file to a URL, for example, I want to send a URL to a URL. I was writing it UIL and image are not exactly the same. image is a Just need to put it I put it in a state, but I don't need to use it. But when I found it, it was a preview. Upload file, you can upload an image, an audio, or a video. upload a video And these are all post-processing. The most known upload image is used to be a message queue. It's using message queue or worker pool. When you upload it, you first request a request for Amazon S3. I'll just request a call And then you put the file Take a look. and then turn to M1c and then turn it off after it will be replicated I'll just replicate it, if not replicate it, it will be lost. Replicated it, and then post processing. When it's done well, you can say message is ready, and send it to the recipient. And you may need some metadata, upload file. You can do some metadata. You don't need to worry about it. Media service is a very complicated service. Safety team has been spent a long time. It's been a engineer. So it's pretty long. Media services are not easy to do, but rather complicated. And you may need a loopup entity. For example, I need a loopup entity. I need a user. I need a user. And you can quickly return. This is a try to create a new tool. You can use a join group. Just like you said, you can change your group membership. You can get direct message, for example, I'm getting direct message, I'm going to click on my channel. Then I'll get out of my direct messages. You can get group messages. I open a group message. I just opened a group message. I just opened a group message. And then there are some of the most famous crude for group information and crude for User information, I don't want to read this. This is a non-design area. It's too much. It's time to be able to grow. Group information, crude is create, read, update, delete. And in the design API, you should be careful about that, like this service, it is a public API and internal API. For example, the public API is what we said about mobile support. Internal API, for example, you have Process Media, internal API, when you upload it after When you upload file is completed, you may have a process media process. Then you can do it in the same way. And this is the reliability and post processing and general preview. General Proviews has a very interesting reading. The dating app is a evaluation of the data. And the generated preview is a bug. It's interesting. You can use the "distribute to CDN" to do it. You may be able to distribute to CDNs. This is to make multi-media in CDN.
[40:00] CDN其实是我觉得用户日常生活中使用最多的,比如说你去看YouTube,看B站,很多这个Data全部都是chop层非常小的一部分,比如说是好十几秒或一秒然后把这个放到CDN上去传的然后我看了没有人问怎么样design cracklist 啊 其实cracklist 是纯粹用cdn 实践的就是当你post一个新的cracklist post的时候,其实cracklist就有一个worker就把它放在一个那个queue里面然后他每小时呢,把所有的新的post结合之前的postgenerated成一个static的一个HTML就是一个静态的网页,然后把它distribute到CDN上,然后别人当你访问cracklist的时候,他就把静态的网页给你。cracklist就是用通过CDN时间的,所以可以很快。也是一个非常女士流的时间方式,但是他work就work。非常有趣,然后 Notification Service 也是另一种User Service 你可能需要Notify User 比如说你需要Notify User 这个有一个message,需要 notify user,是哪一个被notify的user,然后你可能需要有个sender ID,是吧?就是谁sender给你的然后需要Message ID,然后Message Preview,很多时候你的那个就是 Notification 用户,你不需要把Full Message Pass 过去,你就只要一个Message Preview 就可以了。然后我们这块这个Stretch Hold 没有谈很多,就是Security 角度的,比如说这个Message不能超过两个Key 的一个Corrector 啊这种包括你这个user是send user,就是你不能一个user一秒钟发1000个message,然后DDOS这个server,都是有这种假设的如果面试官问到的话你可以提一下,但是你就假设这个你要根据你的那个用户是吧,说我这个用户,因为我要去那个throttle这个用户,或者rate limiting这个用户,他一秒钟只能发10个message,如果他一秒钟发10个message以上的话,我就应该throttle,然后你每小时可能还要throttle,比如说这个user每小时只能发300个message,一般这种搜索是非常非常多的,因为你如果被人知道这个搜索怎么搜索的话,你总有办法来DDoS你。所以它这个搜索会很多层限制在上面。所以这个design rate limiter也是一个比较有趣的问题,单独的一个问题然后这块呢就是notify user呢可以用kafka,就是像uber的话就用kafka,这个相当于request through message queue了你用卡夫卡就是你这块send notification就是Insert一个Message到Kafka里面,然后Kafka会有个Handler,然后自动帮你去Send Notification,然后有Retri嗯现在的变化是很难的,可能是两个 phase commit好,我们这时候就要看到table层面了。我在table 层面之前想问一下大家对这个API 这块有什么问题吗?能不能 end to end 讲一下 notification 的 retry 是怎么样的一个 workflow 呢?因为 notification 你 send out 出去以后,收到的肯定会收到一个 received thing,我 notification send successfully,然后它存到哪里,然后再怎么样 retry,对这个我们可以用卡夫卡卡夫卡汉诺了或者是这种UserDB,我就说用Message Queue和UserDB吧,因为我对Message Queue更熟一点,我自己个人是对Message Queue更熟一点。假设你用Message Queue的话,你实际上是,当你说我要Send一个就是,呃,呃,呃,比如说Sending notification message,你通过靠Message Queue,你是Asynchronized的交给另一个worker,就是从worker pool里面那个worker来处理的。如果大家对worker pool不熟悉的话,我建议补一下那个就是有一个worker pool model,或者Asynchronized ProcessingWorker pool和Asynchronized Processing很多时候相互等价的,就是很多时候说的两个意思很接近。这个您可以读一下,我看有没有情况,或者是你就是我也可以花点时间解释,看大家都有没有这个Worker pool或者Asynchronized Processing这块有没有问题。然后你删了一个Message, NotificationMessage,到这个WorkerPool里面,会有一个WorkerPickup, and then it will be sent to the third-party API, for example,你可以去Tradio删了Message, you can see if there is a WebSocket, if you have active WebSocket, you can just就是 notify mobile或者是您可以request API 卡到 Apple 的 notification 或者是Android Notification我看到有同学人说Android是Firebase的Clock Messaging Notification然后它会给你个Receipt表示自己个成功了然后他会去
[45:00] 当你接到这个resheet的时候你可以mark这个 database 说这个 complete, work complete然后把它mark成complete,如果你要guaranteeat least once的话,很有可能你需要一个database来存储,就是说我这个message,比如说我们在这个design里面,我们在刚才这个Design里面,这块可以加一个这个State这里面可以有个message notification center notification sent可以加到这个state里面对吧那他如果require notification然后我们就去靠 Trilio, Trilio,或者是那个 third party API return success 了我们再把这个 Notification Sign 填成 Yes如果这个比如说这个 third party API没有响应,它可能 sent 了,也可能没有 sent 了。那我们都不填yes 那我们可能会有一个sweeper 就是有一个background process 重新再处理一下这个message 重新再处理下这个notification上只有当你这个Sign Notification写成true的时候,当且紧张,只有一种情况就是你靠ThirtyParty API成功了,然后同时你写入Database成功了,这个Notification才是Sign。所以这是at least once的这个 notification send回答你的问题了吗嗯,差不多。还有还有其他问题问大家How about use egg?亨瑞你能聊一下这个AC吗?我对这话不熟悉我不知道这个ack指的是哪一种ack我记得以前好像是做那个Storm的时候,他好像说说,好像卡普卡里面也有,你可以那个open它那个那个aclogment的选项,它就会就是那个好像跟那个exactly once相关,但是那个东西它会让那个系统会比较慢一点。但是跟你刚才讲的一个就是用那个third party service 好像不大一样,我不知道有什么不同,我其实也不在弄。我对这块也不懂,你只要懂一个就可以了,我觉得没必要都懂,只要懂一个就可以了,因为毕竟有不同的处理方式然后我再次强调一下Exactions是非常非常复杂的一件事情,它是一定要一定要request特别是这种这个2-phase,这种3-party API的话,它是要require2-phase commits的。2-phase commits它效率非常非常低就就是一两个数量级就会把你的performance降低至少一个数量级啊,很多时候就降低至少一个数量级,就慢了10倍。所以如果你没有这个需求的话,不应该做 exactly ones我记得常见的一个就是这个Exactly Once,包括这个Two-Phase Committer有个常见的Protocol叫XA ProtocolOpenAC,对,XT你要是感兴趣的话,你可以读一下Extended Architecture去做这个其实挺早的,1991年的。很多 service 会实践这个 API,它有不同的 language API,这样的话它可以帮助你实践这个帮助你实践这个two-phase locking和这个two-phase commit和two-phase locking不要可以帮助你实践这个two-phase commit和exact events 我把它做到这块做为衍生阅读好了Cafka是类似的,Cafka,sorry加这个作为演示音读好了Kafka是类似的,您可能你可能到时候如果它repeated fill 可能是要放了一个什么地方 为了blocking notification 不管是Message Queue Kafka的话 很多时候可以把两个问题相互等价转换我个人对Message Queue少有数点,我可能更多用Message Queue来解释。如果对Cafka2也熟的话,也可以用Cafka来解释一遍这个问题。好我们接着看,然后就看到这个Data Model这块了。我们之前讨论过,如果我们看一下我们的Architecture的话,谢谢大家就是 user service, account service,User Service, Account Service, Friend Service, Group Service它是通过一个嗯SEQL来实践的,因为它更像relational database,这个general question的话,大家可以google一下,就是我解释的可能没有google好,就是你google一下,然后search一下。我不讲two-phase commit了
[50:00] 这个相对来说很复杂,Two-Face Commit很复杂,这个是前人研究的非常复杂的一个地方。如果感兴趣的话,你就直接搜Two-Face Commit,看一遍就行了。总体来说,好说白了是这样子,就是你Two-Face Committed有一个Coordinator,然后你有一大堆Group然后你当你当且紧张,所有的GrooveMember都同意说,哎,我Ready to Commit了。然后Coordinator说那我们现在commit然后再send给group member说这个全部commit了如果有一个人about的话,那么这个commit就about所以这个对,这2-phase commit 呢,很多时候在payment system, 其实也不用规定,完全用2-phase commit。它也可以通过其他的方式来解决,因为你只要你只要 item potent 的话 你就不需要 two-fifths commit 的就是你重复一个request 没有set effect的话就不用two-phase commit的MessageState通过ChannelService来update,会不会形成Service间的CircularDependency?Message是中国Channel的update 会为行用ServiceChannel应该不会,你这块其实是不会call back to channel service,你只有channel service call 这些其他的service这个direction很多时候是万位的就是你不会有个 account service 在 call back to channel service就是我们不会有一个request from account service trigger backdoc channel service这个很多时候是万味的嗯你的 notification 应该靠的不是 channel 你这个 notification update 的时候很有可能update 是 message story service当你notify完的时候你去updateMessageStoryServiceOK就是我们就不讨论这个太过于细节了就是你总有办法能解决的这个太过于细节这个问题这个问题还是挺大的考虑太过于细节的东西了我们接着讨论刚才这个Data Model的问题好我们在这讨论 database 这个里面 account service, friendship service, group service, relationship 都比较relational的一些关系,所以就放到信号里面。相对来说,它的 data 不多, account user 的话,我们像之前讨论过,10个 million,不是很多,然后Fanshift的话,一个人有100个Fund的话,大概也就是个10 billion也不是很多Group membership也不是一个非常夸张的东西它这里面data量最大的可能是message这个message很大所以你可能放到DynamoDB里面其次的话,即使它group量很大,你这个Sycle是可以通过horizontal sharding or vertical sharding来解决的,vertical sharding就是说你account service可以放到自己的sqlshard里面FriendService可以放到自己的设计圈里面,然后GroupService可以放自己设计圈里面,然后Horizontal Sharding就是说你可以把这个,比如说你把这个Friendship service呢brick 成 1000 个 shard,然后你上来就分成 1000 个 shard,然后这样的话也是可以基本上可以 infinite skill,所以sql 也是可以 infinite skill,sql 是可以 infinite skill,Depends on 还是有跟着不同的需求来说,很多时候他们很多时候说Sycle不skill是假设你不做sharing的话那是不skill的但是你可以infinite skill就是你做sharing的话可以infinite skillsharding 有些额外的technique 我觉得有三四种就是怎么样做sharding我推荐大家如果是没有时间理解的话,你就可以这么说,就用Schemeless的sharding,Schemeless就是把所有的那个SQLSharz分成4096分你就直接说根据你的那个估计未来10年data的估计直接分成多少分?分成固定的X分然后你把那个 id hash 以后,根据最后几位磨这个X 然后知道它在哪个shard这是最简单的shard 计方式 有其他shard 计方式我们可能会有空的话再聊好 所以相对比较relational的意味就是放在SQL里面然后你Multimedia肯定放到AMLON S3里面然后Message放DenomDB里面因为Message有一些额外的featureDenomDB比较能满足比如说这个user table里面我就不work 塑造这个user table 相对比较简单注意到这个user table 你create了以后其实很少去update它的你不会反复的update这个user你可能只有一个status是可能一天update几次但是总体来说你不会是反复的update这个table,很多时候很有可能,如果你有说,我要有一个方便的方式找见所有的online user,你可能需要有个active user table,当你这个user online的时候,你去update active user table,就是很多时候比如说你Uber,你靠一个Uber的话,我想知道哪些driver在online的,我们有几百万个driver,
[55:00] 是吧我没有我们有几亿的rider,那怎么办呢?你应该有个active user table来找一些active user那这种情况下你反复update同一个roll的话你就应该把那个这个column从这个table里面取出来,然后形成一个自己的table可能会有个active user table,但是因为我们这个相对来说frequency不是太高,我们就可以放在一个user table里面。然后find table,计划稍微有点复杂,Find的table相对于复杂为什么相对有点复杂呢?就是你仔细想一下这个Find呢可能是说我A和B是犯的 B和A也是犯的是吧这个关系是相互的那你这个user怎么存呢?如果你只有一个的话也可以,就是说你user只存一个的话也可以,但是你share的时候会有问题,因为你没办法来说根据两个key来刷的因为你lookup的时候很有可能说,我lookup根据userid1来lookup,或者lookup根据userid2来lookup。你可能听过这样的lookup,所以说这个france相对比较复杂,我这块采取的处理方式是,比如说我和David的connect,然后然后我把它Duplicate成两个Role,就相当于是我和David Crank,David作为User1一个Role,我作为User1一个Role然后我这块同时呢把lastViewData分成两个这个主要的原因是为了Infinite Skills做的,就是为了sharding处理的,这样的话我可以说sharding可以是UserID1这样的话我就是比如说我想找所有的我和其他人发的message,对吧?所有属于我的message,不是,我想找所有属于我的friends,是吧?我想找所有我的friends,那么我就可以只根据UserID1来找就可以了我不用根据UZID2来找我可以只根据User ID 1来找,因为所有的Friend一定会User ID 1存一遍我对于任何一个Friendship来说,一定会User ID一存一遍,User ID二存一遍。但是我share的时候我只能根据一个column来share的,所以我可能说我就根据useride来刷的话,因为我duplicate,所以我只要根据useride来刷就可以了其次这个last view date这个很重要这个是什么呢,这个是用来处理这个highlight unread message 的highlight unread message 的这个data,比如说,我给你举个例子,我这块有一个onread message那我不知道我上次read在哪 是吧我往前走的时候他很快他会告诉我说你上次read在哪 是吧那这个data是不能存在message上的,为什么不能存在message上?如果你是点对点的 directmessage还可以相对来说还ok是吧点对点的嘛但是如果你是group message的话这个message里面要存一千多个人读了没有读的状态,这是非常糟糕的。所以呢,我这个设计呢,说,我这个unread message是存在last view date 上的。就说白了,比如说我send David 的一个message,我这个message有没有读,是depends上我上次什么时候看我和David之间的message的。我后来last view date 的timestamp。然后如果这个所有的新的message所有的message在这个timestamp之后,那我是没有读过的如果所有的message在timestamp之前,那我是读过的那这样的话,这个last view data其实是两种,一个是David看我们之间对话是一种一个是我看我和David之间对话是两种,是个双向的,所以说我把它分成两个了。所以这个这个Fineship Duplicate的原因是两个原因一个是为了sharing方便一点,share方便一点,你不想就是你不想读一个人的Friendship的时候,你就把这个request去所有的Sharer,去4000个Sharer一次读一遍,这个效率很低。然后第二个原因是我读我和David的Message和David读他和我的Message的last view date呢这两个data是不一样的,有各自的意义,所以我这块把它分成两个roll的话,也可以把这两个意义明确一点。这样比较好。有不同的设计也是可以满足的。但是我觉得这块我的focus,包括我出这个体面试题的考量点就是,你这个unread message的tag是不应该放到message上的。因为这个Message上的group 比如说我上了一个group的话 如果你这个Message,这个unreadMessage这个 status 放到这个 message 上的话你这个 message 要 keep 比如说一个 group 有1000 个人比如说我在这个group有5000个人那这个message他要单独处理这5000个人哪些人读了哪些人没有读这是非常累的其次,membership还是在动态变换的,复杂度就更高了,这是boowork的。谢谢大家slack有没有EV1好友这个概念我问问这个问题是什么宇航你建议解释一下这个问题吗
[60:00] 就是说能评价吧就是想问一下这slack里面就是有家好友这个概念吗还是说就是slack是没有好友的概念但是比如说你微信是有好友的概念吗这个微信不是和slack没有本质的区别吗哦,ok,所以说是扩展到其他的那个我们这把focus说这个API的focus是focus on Slack 但是你看这个这个可以用在微信可以用在telegram可以用在Facebook message可以用在WChat去吧都是相似的东西没有本质的不同,所以这是相似的,所以我说这话这个好友的概念可能是很多时候比如说WeChat啊或者什么其他的好友的概念其次的话,您可以用Find来存DirectMessage,DirectMessage有些Metadata,A和B发,B和A发,A看了A和B之间对话哪了,B看了A和B之间哪了,这个有一些metadata也是可以存在这个里面好OK检视group 里面 last meal message 怎么实践呢简单假设我们就举这个例子来说,我们就讲的group了,group membership是类似的,和上面这个非常相似,这两个是非常相似的。谢谢大家比如说我加入这个 Random Group,这叫 Random Group,所以我在这个 Random Group 里面 insert 了一个新的 row,那就是Random group 我在这个里面 我什么时候加入 我刚刚加入的 我的肉是什么 我是个user那我last view of date 就是我刚刚看过的这个那比如说divid 我们在这块加一个新的肉David刚加入这个RandomViewGroup那这块就有一个David是吧然后它JoinDate是什么然后它的肉是什么,然后它lastviewdate是什么,它lastviewdate上一秒是吧,last second那我的last view data和David last view data不一样那我在这个里面比如说这个group里面我读到所有的message因为我有个last field date我当打开这个的时候,我就refresh了last field data,但是我可以用旧的last field data来determine哪些message我读过,哪些message我没有读过。当我打开这个channel的时候,我之前有个last view date我读一下我自己的我读一下我自己的table说Scout 在这个Random这个Group 里面,LastViewData 是这个时间段,那所有信息这个时间段都是UnreadMessage但我打开这个channel以后,我把我的last view data refresh一下,refresh成刚刚我再打开的时候,新的message又不一样了那我的last field data和那个David last field data不一样,因为David还有自己的membership role它自己的Membership Row呢是它有自己的last view date解释到这个问题吗?好这个问题呢,这个问题呢和这个find table是完全,这个问题其实和这个last field data,这个group last field data和find last field data是相似的相似的,但我们这个group里面没有duplicate存,是因为这个,我觉得是为了减少一些lookup,因为你想这个事儿的话,你想一下这个user事的话,如果是这么存的话我要查一个group membership的话我只有两种方式,我去所有的group membership table 里面去找,那我能找到所有的user,或者说我可能单独存一个额外的table,专门存这个group user relationship 可能能存。Last View Date 什么时候更新? 当我Click的时候更新,比如说我这时候我click的时候那我last view of data 更新了我再click random on last view 再更新了等了吗?谢谢大家谢谢大家对于每一个登录的Client需要maintain一个long-league的collection你可以用Hardbit或者是那个WebSocket sorry好像这块逻辑呢好像Mobile 端好像会 maintain 一个 Web Socket, Web Socket 也方便 server 端进行 notificationLastView可以timestamp频繁更新CPU单独拿出来不会单独拿出来,因为你其实更新不会太频繁,你思考一下,你注意一下,每个user是有自己的那个last update,last view data的,比如说这个group是random group,每个user有自己的那个这个什么的,我在这个Random Group 更新我这个Last Mill Date 我refresh一下 我更新一次你知道吗这个更新不会太频繁的我不是说一个group只有一个roll那所有优在更新这一个肉那是有问题的但我现在是Normalize了,我是User加上GroupID加上UserID这个group有5000个user,其实是5000个肉,我只更新我自己的肉,所以这个更新不会太频繁。谢谢大家每个group上有多少条未读信息什么时候更新的这就是我们这个channel service了channel table很好,我们就接着读这个Channel这一把
[65:00] 相对比较复杂一点我看这是一个Channel Table 这个Channel Table 呢这个Channel Table是Virulize一些额外的事情,比如说我这个Channel Table就显示什么呢?显示我所有的Channel,比如说我在SafetyChannel,RandomChannel,David发了我一个东西,Uncle发了我一个东西对吧那我打开的时候或者您可以想象成微协的一个friendlist然后你会看到说哪一个饭子散了一个什么东西了然后这个Channel Table是放在Redis上的因为这个data如果丢失的话你可以通过假设这个data丢失的话这个redis crash 掉的话您可以通过 Group Membership和Funtable去reconstruct你可以通过这两个东西去reconstruct,还有Messages和MessageStoryTable,你可以去reconstruct但是你放在redis上呢你就可以快速的得到这个比如说我一打开这个我很快就得到这些所有的信息了可以快速的得到这个结果那这个Channel Table是denormalized的,是纯粹是为了一个API Request,就是能看到你,比如说你可以把它想象成一个微信是吧你读你自己的时候读你自己的微信谁发给你message然后你就会看到我在Safety 组有10个Message 未读最新的一个Message Preview是什么样子的Timestamp是什么样子的David发给我有什么messageHongray的蜡蜡蜡Uncle发给我什么时候message然后可能Uncle这个expired了如果他expired的话可能需要delete掉然后就把它不vigualize出来这个messageUnreadMessage什么时候读比如说我send一个message比如说我们就拿Davidsend给我message来说Davidsend了我一个新message对吧我们看David send me a new message, the request is to channel service, see architecture这个Request is essentially channel service,然后这个request去MessageStoryService把它作为Message存下来就是说这个Message先存到这个DenomDB里面当Message存好以后然后他在update Redis这种情况下就当你message已经存好了这个message不会丢了你不会丢了你就可以显示给David说David这个message已经send了已经send了然后这时候你再把它updateonread message变成1last message reveal变成了什么如果你在这个channel端很长时间, last view of date就不对啊,你可以离开那个channel的时候,然后再把last view of date更新啊,你可以在打开的时候更新一遍,离开的时候再更新一遍。你去更新两遍就可以了LastViewData可以只在可以指在client端做吗last build data 在client专做也行但如果你换一个client呢比如说你在手机端看的时候和你在比如说我在这看我偶尔还用手机端看那你这样的话你不是就两个之间不沟通了吗你在这块看的时候我知道有哪些message你这个multiplatform就不work了谢谢大家我估计这个Channel Table会有挺多问题的大家计划有问题吗谢谢大家 谢谢大家好 那我们接着看Message Story Table呢,这个是用DynamoDB的,DynamoDB是支持一个PartitionKey和SouthKey,这个Message expiration是在Message story 特别表里面的这个partition 支持一个partition key和soft key这块需要对denomoDB有一些额外的了解FenableDB呢它是有一个partition key和sodic key,然后partition key是决定这个data是在哪几个partition上在哪个partition上,然后它同一个partition是会replicateN点,这个N是可以定义的,一般是三变,但是你可以customize它,定义它。比如说message1,它partition可能是2,message1假设partition是1,Message 2,它partition是2,就在不同的partition上,但每个Message同时又replicate了三遍然后这个partitionK如果你partitionK不一样的话你多半不在一个partition上你就说说partition key要非常小心的定义然后sout key是什么呢?sout key叫partition key是primary key然后这个是要 Unique 的所以这个Sought Key呢,就是常见的Sought Key呢就是,比如说常见的,我给你举个例子,比如说你要读你的Shorting Card你是Amazon user,你住你的shopping card那你就可以读你的partition case user id那你读你的UZID的时候,你SoutK就是你的ShoppingCart里面Shopping cart里面你添加了item的顺序是吧那你这样读的话我就可以读出我这个user 我都要有shipping card 都有哪些item 就可以读一下partition cache就读出来了然后你也可以根据它那个partition key + third key来读出来SoughtKey好处是它在Denamo里面
[70:00] 上的好的所以您可以有比如说Binary Search啊或者是你Rent Search是比较方便的如果你没有Sought好的话,您可能需要Scan一个整个就是整个一个table才能找件所以有时候效率可能会比较低那你Sought好的话你就可以就是非常快的不用 Binary Search Transition, Index 好,你直接去就早见了不需要BinarySearcher你就index好就直接找见了所以这个SoughtK有自己额外的好处所以我们看一下我们这个request pattern我们之前看到过这个request pattern常见的是我和一个friend的message和我看一个grouply message对吧这是常见两种pattern我看一个group里面的message 就是我读list的message和我看我和一个贩子建了Message 这是常见两段read pattern如果他是这么锐的的话我们可以PartitionK,DynamalDB PartitionK变成什么呢?叫你 container ID比如说如果在group里面你可以叫group+groupid,那你就是当我读这个grouping message 的时候我就可以说我get这个partition key equal to group ID 是吧那你就读出来一个group里面所有的message然后你这个SoutKit可以变timestamp然后你可以做pagination 是吧 比如说我先读前100个messages是吧然后再读下一百个message这是getGroupMessage 如果是directMessage呢你可以把userId1和userId2 sort一下就是小的永远在前那么你用这个sought好的这个作为partition key那你这样的话也可以非常快的说我读一下我和David之间的direct message 我可以很快的读出来因为 container id 就是两个user id concordinated所以你可以很快地出出来因为它timestamp效的好的你可以非常容易的做pagination这块有不同的设计,这块的Design设计有不同的设计很多就是design不一样的还是挺复杂的这块我觉得最常见的一个design就是这个问题是什么?这块这个design有个问题是什么呢?你这个message ID你其实是没法直接找见的你这个Message ID很有可能需要做Global Index你很可能要做 Global Index, which is Secondary Key Index你是没法直接找件的,因为你这个你其实你思考一下这个里面没有一个直接的request是get message by message id有点反直觉,但是我想一下好像没有一个直接的用的case是get message by message 不是一个很常见的一个request所以你这个global secondary index 应该是work的嗯不是一个 common pattern 不是一个就是不是一个 use case and frequent pattern你更多的是get一个两个人对话之间的direct message和get一个group message,所以它这个partition可以使用Group ID或者是两个UserID concordinated起来更容易实践就是做这个然后Global Secondary Index有什么问题呢?Global Secondary Index的主要问题是它这个index可能不会immediately available比如说你刚insert了一个message你read message by ID 可能不会 immediately available,很多时候这个 global secondary index都是 asynchronous 做的,它可能一秒延迟,一秒后才能读它。所以它这个可能会有其他问题我不会太多cover global secondary index 就额外的考量那我们回答这个问题这个 message expiration 怎么实践呢你可以在这实践在这一块加一个 message expiration timestamp然后呢因为你是用DenimalDB的,DenimalDB的API是time to leave的,你可以直接说这个message time to leave是什么时候他过了这段时间以后他就直接expire了这是用database就是从database层面支持还有一种什么办法支持呢你可以从client层面支持比如说像这种情况下是你在这个channels里面是存了这些的然后你读了一大堆message,但你看到这个message已经expired的时候,你就不返回给client端了你在server端就filter掉了同时你也把Redis updated把它给删掉你懂吗?就是我现在这个Redis server在 under我这个User ID 下有一大堆message,有list of messageA sad home message嗯我读定message,我看哪些message已经expire了把它filter掉我在update Redis,我在返回给user同时,我会update Redis但我就不嫌谁不显示给user这个expired message了这是可以在client端实践所以这个你有多种实验方式你可以在database端用database已有的feature实验你可以在client端实验你这种情况看也可以叫 RAID repair将进管RagerPot更多时候指的是那个Dynamo
[75:00] 没有不同的那个不同的那个nodes 它让data不一样然后再把这个data convert 成一样的我觉得这个叫radery part本身也没有什么本质的问题好How about message search would group message be used?对这个可能会用所以很有可能是你这个Message这个DataStructure很有可能是你给一个termHappy hour比如search happy hour然后它这个happy hour指向message ID的这个Message Search很有可能可以用到的你这个Message ID如果用在Global Secondary Index的话它主要的问题就是它的index时间有延迟Message search不会一个很严重的问题,因为Message search你要放到那个你要放到Elasticsearch 上 Elasticsearch Index 的时间和这个Denimal Global Secondary Index的时间机会是相似的这个都需要一些时间都需要一两秒特别是那个 Elasticsearch index时间可能会更长一点,我之前起过一个SID project用过ElexicSert,它那个Index时间就是很多时候read or write 其实会有问题就是你刚写了读你的你根据你写的东西index search 这些东西会有问题的就不会immediately available 我觉得这个大家应该有影响 比如说你在一个系统里面刚加了一个record 然后你搜你自己的刚加的record 你搜不到 在search里面搜不到 因为它这个elastic search需要一些延迟的好我一套一套读啊这里的timetable是哪个timestamp试试的好哪个node支持的那你应该是在哪一个channel service就是哪一个node哪一个channel service的node服务给你就在哪个node生存User A看到User A的Message 1回复Message 2会不会出现Message 2在Message 1之前有可能嗯这是有可能的 你这个timestamp 我觉得 big red flag 是你用clienttimestamp 这个是不可以的 因为你clienttimestamp 你可以trick it你要是melicious user的话我可以set一个timestamp在两天后那我会两天后再看到这个message吗或者你算了一个timestime在15天前那你这个是这个是一个red flag所以这个timestamp一定是在server.node生存的所以你如果有两个server.node如果你sign的时间很接近的话你很有可能会看到这个Message 1应该在Message 2之前发的但是显示的时候是Message2在Message1之前这个其实我觉得你如果就是用过一些chatting app的话你有发现这个问题这个还是挺常见的一个问题我觉得有一种方式能解决就是看你怎么事的看你怎么事的比如说你可以在request level share的是吧或者是你在DB level 12的你在DP Level 上的也可以,比如说我和送样发短信比如说送样后发短信我们俩每次在database里面都去同一个事的那就不会有问题这个timestamp也可以在那个database层面生存MySQL 都支持那个timestamp生存的就是你insert这个Role的时候它自动给你生成timestamp也可以在database层面生存这也是可以的可能在database层面生存出现的conflict可能性更小一点因为你可以通过sharding时间嘛比如说我和书雅说的对话都在一个shard里面如果是用database的生存的话那就是同一个shard里面用同一个clock那生存的话相对来说不太会有问题但是我们之前讨论过即使是同一个node的clock它有可能jumpback对吧他有可能jumpback虽然这个概率很低那也还是会出现这个问题的所以这个message order是永远不可能guarantee的这是很难guarantee的除非你用那个spanner这种这种Service 负责你的Message order 这个是给足够大的数据一定会出现message order, auto order的好,羊羊说,每次有人发一个message,所有的membersChannel推保都会被更新一下吗我们来看一看你在group里面发一个message的时候比如说你发一个message的时候你要经过哪些东西首先你要去把它message insert到Insert the message story table你这个里面把Message insert进去当你 insert 成功以后你去把所有的 membership 的 user 比如说这个 safety group 你把所有所有那个Safety Group的User全部更新一下更新怎么了更新unread message和更新last message preview是的社会更新一下但是呢这个很快,为什么很快呢?因为Redis能 handle 1秒钟可以很轻松的 handle 10.5 次 found request很快
[80:00] 就是假设你这个group 里面像我们之前assumption 是1000个user1000个user的话并且你Redis也可以sharelet,你Redis本身可以sharelet,你1000个user,你需要更新1000个user的Redis record你可能share到三个redis server里面你每个service只要更新300次然后那你其实就是0.233秒就供应完了,其实很快的非常快乐是的 如果我们 你如果看过那个推特的那个Design 的话你会知道Twitter的Design,包括现在也是,他可能后来做了一些额外的优化比如说创这种有30个M或者是300个million的follower当川普发一条文件我的时候他所有的follower都会被insert一遍同一条微博在早些年后面他好像说做了一些优化, Twitter好像做了一些优化,但是你就是比如说你作为一个网络大看,你作为一个popular的那个influencer,你发了一条推特你所有的 follower 都会更新一点所以说他们说那个开玩笑微博说那个不能支持两个明星同事出轨吗就是可能就是这么来的因为你两个明星同时出轨的时候发了一条message然后这个message呢你所有的follower全部更新一遍,然后你瞬间这个read file就报表了,这个就报表了。这块有问题,Commitual Direct Message as a SingleDoubleMember group message.所以我们不需要不同的 video message 这是可以做到的这是可以做到的这是可以做到的,包括其实这里面有很多,比如说这属于这个Random里面属于有Name的Group,你要是随便拉两个人这属于Unname的Group,对吧,这属于TemporaryGroup,你TemporaryCreative的一个Group,是吧,这个Random Group有区别,就是可能Temporary Group没有Group Name什么的但是你direct message也可以认为是就是直接是一个group只有一个人,这都是可以做到的,这是一种optimization但是 Direct Message 如果和 Group 分起来的话, 有一个问题是你这个怎么事儿那你这话可能得对这个group id 也进行一事儿的了你可能得全两遍我来解释一下为什么存量变你这块group ID和user ID,你必须share其中一个但是你这个group membership可能从user来找,比如说我作为一个user,我在哪个group里面也可能从group来找我这个group里有哪些user这个是二位的lookup 的假设我们上的是 group id假设我们share的group id,那你lookup的时候,根据你的group id,你就知道去哪个share去找了。你比如说你group id,你去share的1找就行了,注意我们可能有1000多个share的是吧?很常见可能有1000多个事的你根据你的group id就知道去哪个事就走了但如果你找这个user在哪个group呢那你就没法去具定的一个groupshare找了你可能要把所有的groupshare全部找一遍你可能知道这个user在哪个group对吧所以这就是问题所在所以这种情况下的话你可能这两个得存两遍或者是你这个UserID做一个Global你自己做一个Global Secondary Index,如果用Syncl的话自己做Global Secondary Index就是你就根据UZID你就知道在哪几个share之间了Channel Table里面存全变量的user还是只是online的user这个不是个Redis吗?您可以Redis 的话,我觉得这个是 trade-off了,您可以只存 online user,但是有一个新 user online 的时候,你需要生存一个 generate 一个新的这个。所以这个table是可以通过Message Storage Tablegroups table和friends table generate出来的那你这种情况下你当你新打开一个app的时候你就很慢或者您可以把这个redis shard 成,就是redis也可以shard 了吗?shart成支持所有的Redis,然后你所有的user都存一个这个也可以这个是一个 trade-off好处就是如果支撑 Active Online User 的话你的这个data量比较小但是你新user login的时候时间比较长如果你存所有的user的话,你data存的量比较大,然后可能你需要不停的有slipper,比如说把这expired message clean up 掉但是你user login 的时候特别快能不能把 group ID 当成一个 tag user tag这个是个问题,那你group id 如果当成一个 user tag 你是存在user table 里吗? 那如果我不停的 frequently加入一个group或者是离开group的话那我是不是得不停的update同一个table了
[85:00] 也可以但是还是有问题就是如果你是一个比如说相对来我的message比较少了,比如说我们CEO Daryl的话经常有人拼他那如果你被别人聘的话被人拉到一个组里面就相当于temporary这样的一个group,那你是不是也会出现我们之前说的ref flag是不停的update同一个roll,就是daro这个userroll不停的在被update说他加入这个group了然后他要离开那个group了,他要加入这个group了,这个是不是一个red flag会不会把这个变成一个hot spot然后会不会使这个database变得很慢我觉得把group IP变成一个tag不是一个很好的一个事件谢谢大家谢谢大家一般的群组合。Secondary index有两种一zone secondary index是 global secondary index这个SQL是没有Global Cycle,因为Sycle不知道shard,shard是我们额外在SQL的表度实践的所以这个Global Secondary Index要你单独去实践这个什么叫Global Secondary Index呢给一个column给一个value他告诉你在哪个shard里面有这个值比如说比如说如果我们这个shard是这个group id 的话,您可以,如果你是lookup by group id,你可以直接找,但你如果是UserID的,找UserID的话,你可能去Global Secondary Index,然后先说在哪个share里面有scot的这个group membership然后再去那个share里面再去找这个你需要自己实践 global secondary index如果是 local secondary index的话那就很容易了因为你这个Local secondary index 说完了就是 MySQL的index 就是你在这个share的里面index好了 你就只有MySQL的index就可以了这是有不同的,你如果这个不太熟的话,你可以search一下什么是global secondary index,local secondary index,然后如果这块就是,我觉得这个相对来说中间还是有很多context,大家不懂的话就是可以反复的查一下这个信息,然后查一下这个keyword,然后查一下viki啊,或者查什么的,很多解释解释的很好,我觉得可能解释比我好多了。谢谢大家没有看到有Q&A还有什么问题吗谢谢大家好我想我想问一下那个status更新比如说谁是谁在线这个功能的workflow能能讲一遍吗安推安的就我觉得这个更新的frequency是很高的然后放在那个table里面会比较不合理我这边 again 说一下我看到有人问就是说这个QPS会不会太高放这个table里面是不合理如果不合理的话,单独立到一个active user table,这个跟我们之后可能会讲Design uber,这个和Uber design是类似的,大部分user都是not online的话可以就是放了一个active user table如果它这个 你分析 你可以就是quantify的计算下这个QPS比如说你要是10个million的user的话那你实际上我一天大部分时间可能就是在线是吧?一个user可能一小时更新一下status,你算一下这个QPS然后这个QPS注意到你这个是要翻就是翻奥子的因为你这个user table 本身就是二个了你比如说放了十个事在里面那你放到每个GPS合不合理好其实对于每一个user来说,你如果一小时update一次的话,你放了一个肉里无所谓去吧你一小时或十几分钟update一次你放了一个肉里无所备那你就是说你额外的对于这个User Table 额外的QPS放到user这合不合理我觉得还是相对来说不是一个red flagUber 那个 case 可能是 red flag 因为Uber 大部分的用者都是 offline而且你online是非常random的行为就是那个online的detect你是怎么detect的,是用heartbeat还是用就是它自己send,主动push给suffice一个notice, saying I'm offline, I'm online, that's it.我知道了,你这个online detect,其实是那个,你可以用那个web socket,就是首先我对这话不是太熟,但是我看到的一些已有的实践是用web socket的就是说当我online的时候谢谢大家我建立了一个socket然后呢这个websocket没有X bar 你可以用heartbeat来不停的detect没有X bar别人发过message的时候我可以pushNotification,就是server可以push notification给我这个web socket,然后notify我。如果从这个角度来讲的话,我觉得status是可以放到一个单独的table。OKQPS 会不会太大?你QPS 分两种吗?一种是机器上的QPS,一个是每个人的,不会太大,因为你这个web socket的话,你这个server端可以不停的skill吗?
[90:00] 你就是有专门有server管这个web socket这块的,你可以比如说你现在有一个million的user,你可以分成100个server,然后每个server maintain 10万个web socket,是吧?然后你根据user id 能知道哪个server管你的web socket,然后来发你的notification,是吧?这个是可以解决的。好 我觉得还是大家消化一下如果没有问题的话大家消化一下这个有很多内容这个其实想想还是挺复杂的还是挺复杂的这些思考还是挺复杂的我也是今天下午花了两个多小时写出来我觉得还是挺复杂的有很多内容可以思考一下然后有很多trade off可以想一下我觉得想通这个问题的话所有的Facebook message然后WeChat这些都是相似的然后你也仔细思考一下我觉得因为我这个design的时候这个问题我前后想过几次吧我很多东西是没有直接写明的比如说availability, reliability我这没有直接写明的没有直接明确的说过几次的然后像这个high performance real time 应该assume 因为这个中间加了很多cash 是吧这个design里面加了很多cash,这个时间是非常快的你像你用sicle的话,sicle mysicle的话,average p50可能就是10毫秒10ms,你用redis更快了可能不到1毫秒是吧不到1毫秒,那你整个end to end flow 其实非常快的非常快的,DenimalDB它其实批评挺多的,DenimalDB相对来说,它那个lendness也比较高一点但是它更scale,DenimalDB是infinite scale,如果你从skill的角度来讲的话,其实这个design是infinite scaleDenimalDB是可以scale到几千个nodes上,就是它本身就是native的支持的,以及prove了这个,它并且如果你看那个很多research paperresource paper 做那个resource的话它skill它那个nodes数量是和它performance是线性的你不会说就是突然就不horizontal skill了它这个Cassandra和这个Late Dynamo skill level是线性的skill非常好是最好的database skill从skill的角度来讲是最好的database了你说吗然后这个amazon S3这个也是可以无线skill你可以不停地往push里面push那个resource嘛然后这个sicle的话你可以vertical sharding和horizontal sharding,你也可以infinite skill,所有这些都可以infinite skill你像这种情况下Redis这个你也可以shard是吧,因为你这个user,你不会你作为一个user,我作为一个scout user,我不会看到David的界面,所以我这个也是可以infinite skill怎么样得到这个tool capacity, latency, maximum concurrency读很多东西比如说我读了这篇文章才知道现在有人能非常疯狂的能支持一个单机能支持4个million 2ps了你读它的时候你要思考一下如果我是个normal request的话那4个million肯定是达不到的我觉得如果作为一个读和写的normal request的话比如说一个正常的有读有写normal request可能要出一个40可能不是400万,可能是个10万左右的QPS,应该是没问题的就是他出一个40万,因为他这块只有读所以你需要日常经验积累然后如果大家有经验的话,比如在Uber的时候有很多dashboard可以读比如说我没事的时看一眼就是看一眼卡夫卡你希望对自己的service这些数字了记于心比如说你design的时候说我要design real time service我要进行卡夫卡别人问为什么要进行卡夫卡那你就可以说如果在卡夫卡如果是single data center的话Lentency就是5毫秒左右是吧P50你看一下dashboard P50就5毫秒左右你如果是单data center就没问题但是我们不是有个cross data center那个Aggregate Kafka cluster其实它那个Lentency我记得也是个P50也就是一百来毫秒还是多少的不是太大但我个人体验过就是那个Cross Data CenterAggregate Kafka cluster有过几分钟的延迟但是可能就是P999那种了所以这个你要读知道吗很多时候你这个Persivate and Product Manager或者Persivate Science Engineer的时候你对这个理解很深的话你就说就抛出一些Data以Data服人如果Kafka在AWS很贵可以用Kinesis, AWS有自己的Kafka, AWS有一个SNS和SQS它好像还出了一个,Kinesis好像是新的,它有个SNS和SQS,我不是太熟
[95:00] 我没有做过这个方面的Sed Project我只是读过一些,对我没有读过这个这个每个人经验时间是有限的嘛只知道有一些那个对用SQL的话会默认他支持Data Partition吗?Data Partition不是NoSQL的Vantage吗?你Data Partition从SQL的角度来讲他不知道什么是Data PartitionData Partition是要你Engineer自己做的Infrastructure Engineer自己做的说白了Data Partition来说对于SQL来说每个partition对于假设我是个MySQL Server我是partition1我不知道有partition2,3,4的存在这个你是不知道的所以你做Data Partition的时候实际上你要做两个东西一个是你要把MySQL就是Native就是你要有个Membership Management这个Membership Management就是哪一个partition在哪一个MySQL哪一个partitionMySQL Server Partition在哪一个Node上这一般是用什么稿呢一般这个是用Rulekeeper稿Rulekeeper一般是做这个Membership Management还有这个Partition这种MetaData这种东西所以当你sharedMetSQL的时候很多时候你要实践一个MetSQL就是你用Rulekembership management还有partition这种metadata这种东西所以当你扫着mySQL的时候很多时候你要实践一个mySQL就是你用rutekeeper来就是加一个mySQL你就可以实践了对于任何一个mySQL来说他是不知道partition的他是不知道有多少partition的但是rutekeeper是知道哪个partition哪个data在哪个partition上哪个partition在哪个node上然后还有包括什么对在哪个node上然后你同时要有个lookup servicelookup service 你可以有多种时间方式但是很多时候你lookup service 要去那个rootkeeper来找见这个membership如果你这个对data partition没有读过的话我非常推荐你读一下uber的schemelessuberschemeless在16年发了三篇paper 写的很不错不是paper 这是uberengineeringblock写的很不错 其实写的挺不错的还是 skimless engineering blocks所以还不错好 我觉得差不多到这里了我现在 stop recording