这一章没有故障,只有 deadline。你会画出全书的第一张架构图——一张简单到有点丢人的图。后面十五章都在改它,而它每一次被改,都是因为挨了打。
你是「秒抢」的第 3 号员工,后端工程师。公司总共三个人:老陈(CTO,也是你面试官)、一个前端、你。办公室是租来的一间十二平米的房子,白板上没有架构图,只有一个日期——两周后的周五。那天有一场小型音乐节要开票,主办方答应给「秒抢」独家。这是公司的第一笔生意,也可能是最后一笔。
老陈把需求写成了四行字,就是全部产品文档:
这是全书唯一一次,危机不是「系统坏了」,而是「系统还不存在」。但它和后面十五章遵守同一条规律:在几乎每一个增长阶段,你的资源都小于需求;工程的本质是决定先付哪一笔账。
A 是对的,理由只有一条:它保住了完整的主链路。「看场次 → 下单 → 付钱 → 拿到票」这条链路上任何一环缺失,产品就不是「功能少」,而是不成立。选座是体验,随机分配也能卖票;运营后台是效率,只有一场演出时你手写四条 INSERT 完全够用。砍功能,不砍链路——这是 MVP 唯一的裁剪准则。
B 砍掉了链路本身。线下转账听起来"也能收到钱",但它把一个自动化系统降级成了一门手工生意:对账要人、退款要人、并发售卖时你无法知道哪张票已经卖出去了。更要命的是它掩盖了库存问题——而库存正是这门生意最难的地方(第 10 章会把这个问题正面拆开)。
C 可行,但它埋了一颗有名字的雷。匿名下单确实能跑通链路,代价是订单没有稳定的归属主体:用户换个手机号就找不回订单,客服无法验证身份,后面接入退款和实名购票时要做一次数据迁移。如果只做一场演出,C 是可以接受的;一旦你打算做第二场,这笔账就会来收。
D 是最诱人的错误答案,因为它听起来最努力。四个八成不等于八成的产品,等于零个能用的链路——用户能选座却付不了钱,和网站没上线没有区别。工期不够时,正确动作是缩小范围,不是降低每件事的完成度。
在写第一行代码前,你需要回答两个问题:一次请求会经过哪些环节,以及这些环节要准备多大的量。前者决定架构长什么样,后者决定它要多贵。
用户在手机上点「立即购买」,到他看见「下单成功」,中间发生的事按顺序是:
miaoqiang.com 变成一个 IP。结果会被缓存,通常不是瓶颈,但它是你改不动的第一环。「你的系统要扛多少?」是面试和真实工作里都绕不开的问题。它不需要精确,需要的是量级正确。三步:
老陈没有给你数字,只给了三句业务描述:「秒抢」首月要撑得住一场和这次差不多量级的独家开票;这场演出在售 5,000 张;他的原话是「先把量级估对,机器我批」。
要交什么:① DAU 区间 ② 人均请求数(次/天)区间 ③ 日常峰值倍数区间 ④ 单次响应大小区间 ⑤ 平均响应时间(RT)区间——五个数都要给上下界,不要给单点值;⑥ 这五个假设里你最不确定的是哪一个,为什么;⑦ 再单独估一次开票瞬间的尖峰 QPS——这需要你自己补三个题面没给的假设:蹲点人数、抢购窗口(秒)、人均瞬时请求数,把这三个也写出来;⑧ 用你估出的尖峰 QPS(不是日常峰值——决定你要买多少机器的是尖峰),除以一个你自己假设的「单机安全吞吐」,给出机器数区间。把这个吞吐值标成假设:本章 ·6 复盘才会告诉你它的来处,现在你只能先拍一个区间。
写不出上下界、只写得出一个「拍脑袋数字」也要写——那本身就是有用的诊断。这五个数没有标准答案,「秒抢」的取值只是一份参考解,不是唯一解。答案不会被保存,刷新即失。
你还没写。这一页是这类练习专门检验你的独立输出,先写下来再看——再点一次仍会展开。
先说清楚这份参考解怎么用:下面每个数「秒抢」都只取了一个点值,因为它要拿来往下算;而你交的应该是区间——两者不冲突,正确的做法是先给区间,再在区间里挑一个点值往下推,并写明挑的理由。例如 DAU 合理区间 1–5 万,「秒抢」取 2 万(早期获客保守估计);人均请求 20–50 次,取 30;日常峰值倍数 6–15(同类电商大促经验区间),取 10(中间偏保守)。如果你的区间把「秒抢」的取值包含在内,这一项就算过。
按这组取值往下推:
7 QPS。日常峰值 70 QPS。一台四核机器打着哈欠就能接住。如果故事到此为止,这本书就只有一章。
但「秒抢」不是普通网站。它的流量不是一条平缓的曲线,而是一根针。把开票那一刻单独算一次:
50,000 × 3 = 150,000 次请求,压在 10 秒里,就是 15,000 QPS。它和你算出来的平均值 7 QPS 之间差了约 2,000 倍。
容量估算不只有 QPS。同样的三步法换个乘数就能用——响应大小和 RT 就是①②③之外你要交的另外两个数:
| 估什么 | 怎么算 | 「秒抢」首年 | 它告诉你什么 |
|---|---|---|---|
| 存储 | 单行字节 × 日增行数 × 天数 | 订单行约 500 B,日均约 2,000 单 → 每天约 1 MB,一年约 365 MB | 订单表一年不到 1 GB。单机 MySQL 能撑很久——所以分库分表不是第 2 章的事,是第 8 章的事 |
| 带宽 | 单次响应字节 × QPS | 详情页首屏约 300 KB。日常峰值 70 QPS → 约 21 MB/s(约 168 Mbps) | 日常可控。但开票瞬间 15,000 QPS × 300 KB ≈ 4.5 GB/s——这个数字不可能由你的机房出,只能由 CDN 出(第 12 章) |
| 连接 | 并发数 ≈ QPS × 平均响应时间 | 70 QPS × 0.2s ≈ 14 个并发;15,000 QPS × 0.2s ≈ 3,000 个并发 | 这条式子(Little's Law)是第 2 章的主角,也是你第一次宕机的死因 |
15,000 QPS 本身不能拿去跟老陈要机器——你要的是机器数区间。此刻你还没做过真实压测,只能先给一个假设区间:单机实测安全吞吐约 400–500 QPS。这个区间不是拍的:本章 ·6 复盘会给你这个数的来处——上线那夜 23:48 你会打一轮压测,200 QPS 时 P99 180ms、CPU 只到 40%,说明离饱和还有余量,线性外推到 CPU 打满大约是 400–500 QPS。15,000 ÷ 500 ≈ 30 台,15,000 ÷ 400 ≈ 38 台,区间取 30–38 台,并留出压测误差的富余,报给老陈的数字可以直接说「30 到 40 台,最终以压测收敛」。到了 ·6 你会看到,次日真实开票只跑到约 900 QPS、远低于 15,000——那不是估算错了,是这次入口只放了一个链接、人流被自然摊平;预算是按「被打满」的那种日子备的,不是按「运气好」的那种日子备的。
五个数在公式里都是线性放大器,误差 1 倍、结果就错 1 倍——单看数学,谁都一样敏感。真正拉开差距的是你能把误差压到多窄:DAU 是老陈拍的业务目标,人均请求有同类产品的经验值兜底,两者一般能收敛到 ±50% 以内;蹲点人数相对在售票量的倍数(这里假设 10 倍超卖需求)却是纯粹的猜测——没开过票,你不知道会不会来 5 倍人,也不知道会不会来 30 倍人。同样是「差一倍」,DAU 差一倍是没做好市场调研,这个倍数差一倍是正常。它被低估到 5 倍,瞬时 QPS 腰斩到约 7,500;被低估到 20 倍,瞬时 QPS 翻倍到约 30,000——机器数区间从「30–38 台」摆到「15–19 台」或「60–75 台」。这就是它最不确定的原因:不是数学上更敏感,是取值范围本身没有底。
两件事按顺序做:先在生产同规格机器上把单接口打到 P99 明显劣化的拐点,把「单机实测安全吞吐 400–500 QPS」这个假设换成真实数字——本章 ·6 复盘那次 23:48 的压测就是这一步的雏形,只是强度不够、没打到拐点,真要收敛这个数得往上继续加压。再找一场更小的演出提前开票,实测真实的蹲点人数/在售票量比例,把「10 倍超卖需求」这个最敏感的假设换成有数据支撑的区间。两件事换掉的分别是分母(单机吞吐)和分子(真实峰值 QPS)里误差最大的那个。
无论你算出的具体倍数是多少,这一章要教的规律都成立:日常均值和真正的抢购尖峰之间,差的不是百分之几十,而是数量级——这也是后面十五章要处理的核心矛盾。三笔账算完,结论出奇地一致:日常流量根本不需要复杂架构,而峰值流量再复杂的架构也未必扛得住。所以第一版就该简单——简单到你能在两周内写完、能在出事时看懂。
两周后上线的东西长这样。把它记住,因为接下来的每一章都是在这张图上加东西——而每一次加东西,都是因为它被打疼了。
这张图会让任何一个做过大系统的人皱眉。但在两周工期、3 个人、0 用户的条件下,它是对的。理由是三条:
接下来会发生什么,这张图已经写好了剧本:
| 这张图的哪个位置 | 会在第几章挨打 | 被打成什么样 |
|---|---|---|
| 应用 → MySQL 的连接池 | 第 2 章 | 连接池打满,全站 502 |
| 单台 MySQL | 第 3 章 | 加从库,然后被复制延迟坑 |
| 「没有缓存」这一栏 | 第 4–5 章 | 加了缓存,然后被缓存杀死 |
| Nginx 这一层 | 第 6 章 | 被迫学会说「不」 |
| 「一个进程」 | 第 13 章 | 拆成几十个,然后为此付账 |
| 整张图 | 第 16 章 | 你会亲手把它打一遍 |
第 16 章的收尾,我们会把这张图和它的最终形态并排放在一起。到那时你会发现:中间那十四章加进去的每一个方框,都对应一次真实的疼痛——没有一个是因为「架构应该这样」而加上去的。
图画完了,还剩一个问题:用什么写。老陈把选择权给了你,附带一句「别让我三个月后招不到人」。三个候选都能在两周内交付,每一个都是对的,每一个都有账单。
A · 成熟全家桶:两周工期下开发速度最快,管理后台、鉴权、数据库迁移这些"必须有但不产生差异化"的东西开箱即用。招人也最容易——市面上会的人多。账单:框架替你做的决定,出问题时你也得替它背;性能调优会撞上你没写过的那一层(连接池默认值、ORM 生成的 SQL、自带的会话机制)。第 2 章那次宕机,起因就藏在一个你没看过的默认值里。适合业务形态清晰、团队要跑得快的早期阶段。
B · 轻量自组:每一层都是你选的,因此每一层你都知道它的边界在哪;性能上限高,依赖少,升级时不会被框架的大版本绑架。账单:两周里你要花掉相当一部分时间造「所有人都造过」的轮子(后台、鉴权、迁移工具),而这些轮子没有一个能变成「秒抢」的竞争力;新人上手成本高,知识全在你脑子里——这在 3 人公司是最大的单点。适合团队里有明确的技术主张、且工期不是最紧约束时。
C · 云托管优先:托管数据库省掉备份、主从、监控的搭建,3 人团队里没有专职运维,这笔省下来的时间是真金白银。账单:单位成本明显更高,且成本随流量非线性上涨;你对底层的可观测性和调参能力被平台限制住——等你需要看慢查询日志、调复制参数、做故障演练时(第 3、14、16 章),能不能做取决于平台给不给。适合流量可预期、团队极小、且愿意用钱换时间的阶段。
「秒抢」的选择:A + C 的组合——用成熟全家桶写应用,数据库用云厂商的托管实例。理由很实际:两周工期下,A 省的是"写代码的时间",C 省的是"不写代码但必须做的时间",而 B 省的东西(可控性)在 0 用户阶段还兑现不了价值。代价你也认了:框架的默认值你没有逐个看过。这笔账在第 2 章就会来收——两周后的深夜,连接池会用一次全站 502 提醒你,你没读过的默认值,也是你的架构决策。
两周后的周四深夜,第一版上线。没有告警,没有大盘,只有你和老陈盯着一个 tail -f 的窗口。留意这条时间线的顺序——它是本书所有复盘的第一条,也是最平静的一条:
部署。手动 git pull + 重启进程。整个过程 40 秒,期间站点不可用——但此刻还没有用户,代价为零。这个"代价为零"的假设,会在第 13 章过期。
自测下单。你用自己的手机号买了第一张票,订单号 O-000001。支付回调花了 1.2 秒返回,比预期慢,但能用。你在便签上记了一行:「支付回调超时怎么办?——待定」。
压了一轮。用一个命令行工具打了 200 QPS,持续两分钟。P99 是 180ms,CPU 40%,没有报错。你得出结论「够用了」——这个结论在流量形状变化前一直成立。
开票。5,000 张票在 6 分钟内卖完。峰值大约 900 QPS,比你压测的高,但没到你算的 15,000——因为主办方只放了一个入口链接,人是慢慢来的。你侥幸赢了第一场。
两个产品的日流量曲线:X 的形状是 ▃▄▅▆▅▄▃(白天缓慢起伏、夜间回落);Y 的形状是 ▁▁▁█▁▁▁(全天极低,某个固定时刻一根垂直尖峰)。同样是日均 60 万请求,关于它们的容量规划,哪个判断最准确?
C。容量由峰值决定,不由总量决定——这是三步法里「峰值倍数」那一项存在的全部理由。X 的峰值倍数大概两三倍,Y 可能是几百上千倍。A 是最诱人的错误答案,因为「日均相同」听起来像是同一个规模的系统;但服务器接住的是同时到达的请求,不是一天的总和。B 把「持续有负载」当成风险,实际上平稳负载恰恰是最好规划的一种。D 的 1.5 倍对 X 都嫌紧,对 Y 是灾难。「秒抢」正是 Y。
「秒抢」第二年 DAU 涨到 50 万,人均每天 40 次请求,日常峰值倍数按 8 估。日常峰值 QPS 大约是多少?
B。日请求量 = 500,000 × 40 = 2,000 万;平均 QPS = 20,000,000 ÷ 86,400 ≈ 231;峰值 = 231 × 8 ≈ 1,850。A 是最容易掉进去的坑——它算对了平均 QPS 却忘了乘峰值倍数,而这一步正是三步法里唯一能救命的一步。C 是把 86,400 秒错记成 3,600 的结果——只除了一小时:20,000,000 ÷ 3,600 ≈ 5,556,再乘 8 约 44,000。D 则是完全没除时间:直接拿日请求量 2,000 万乘 8,得到 1.6 亿。记住这个数量级感觉:百万级 DAU 对应的日常峰值通常是几千 QPS,不是几十万。
某接口平均响应时间 200ms,日常峰值 1,850 QPS。峰值时刻大约需要多少个并发处理槽位(连接 / 线程)?
B。并发数 ≈ QPS × 平均响应时间(Little's Law):1,850 × 0.2s ≈ 370。C 很诱人,因为「每秒 1,850 个请求」听起来就该有 1,850 个槽位;但一个槽位在 1 秒内可以服务 5 个 200ms 的请求,所以需要的是 1,850 ÷ 5 = 370。A 把单位搞混了(秒和毫秒)。D 听起来最"严谨",实际是拒绝估算——核数影响的是你能开多少槽位,不影响你需要多少槽位。这两个数字的关系,就是第 2 章的全部内容。
团队工期不足,四个需求必须砍掉两个。下面哪条是最可靠的裁剪准则?
B。MVP 的价值在于「有一条完整的路能走通」,而不是「做了很多东西」。A 是最专业的错误答案——它优化的是"省下多少工时",但如果那个最费时的需求恰好是支付,砍掉它省下的时间一文不值,因为剩下的东西卖不了票。C 的直觉在多数产品里成立,在这里却危险:运营后台确实可以砍(你手写 SQL 顶两周),但「用户看不见」不是判据,链路是否完整才是。D 见 D1 选项 D:四个六成等于零个可用。
并发 ≈ QPS × RT 折成机器数 → 最后单独说一句存储和带宽。反直觉加分点:主动指出峰值形状比总量重要,并问一句「这个业务的流量是平缓型还是尖峰型」。