那些不可规模化的事:软件版

cover

摘要

本次对谈由 Y Combinator 合伙人迈克尔·赛贝尔 (Michael Seibel) 与达顿·考德威尔 (Dalton Caldwell) 共同呈现,探讨“做那些不可规模化的事”在软件领域的实践。核心观点源自保罗·布赫海特 (Paul Buchheit) 的 “90/10 解决方案”——用 10% 的工程投入换取 90% 的产品收益。

布赫海特创造 Gmail 的故事最为典型:他将个人邮箱强行塞进 Google Groups 的界面,根据自己收发邮件的需求逐步添加功能,而非一开始就构建完整邮件系统。Facebook 早期为每个学校独立部署一套 PHP、MySQL 和 Memcache 实例,靠复制粘贴代码和购买便宜服务器来应对增长,直到多年后才构建全局用户表。Twitch 在面对名人直播带来的瞬间百倍流量时,紧急把页面切换为完全静态,只保留视频和聊天功能;又在名 streamer 开播时故意延后视频画面几秒,先悄悄将流分发到足够多的视频服务器,再同时推给观众。Friendster 曾经实时计算每个人的二度人脉导致数据库崩溃,而 MySpace 的解决方案仅仅是一行判断:在好友列表里就显示“好友”,不在则显示“扩展网络”。

imeem 用 Flash 视频播放器播放音频,把所有音乐文件转码成零视频比特率的 .flv 文件,用视频工具解决了音频服务问题。Twitch 还利用免费对等互联 (Free Peering) 降低带宽成本,甚至在与某国 ISP 谈判失败后直接向用户展示其客服电话和邮箱,成功倒逼 ISP 合作;又通过搭建一个小网站,让全球社区自愿翻译产品字符串,零成本完成多语言支持。谷歌在 2001 年前后因 Web 页面爆炸式增长,批处理索引脚本反复失败,导致搜索引擎静默地使用过时结果长达数月,这一危机催生了 MapReduce 和后来的 Hadoop 技术,但在此之前他们一直靠单机脚本支撑,直到彻底崩溃才重构。

这些故事统一指向一个哲理:别用完美阻挡可行。先“打开水龙头”,让真实流量冲进系统,哪里漏水就修哪里,重复这个过程。所有巨头都曾经用肮脏但有效的 hack 验证了用户需求,才赢得构建可扩展架构的资格。

正文

引言:90/10 解决方案与保罗·布赫海特

迈克尔开场便提到一个常见场景:创业者问他如何在产品上线前测试以保证能正常工作。他的回答总是一句比喻:如果你的房子装满了管道,你知道有些管子破了会漏水,你可以花大量时间去逐一检查每根管子的好坏并修补,也可以直接打开总水阀。水一开,你立刻就知道哪里需要修。

这正是保罗·布赫海特一贯的理念。保罗是 Gmail 的发明者,作为一个谷歌的“副项目”,他创造了一款如今被 15 亿人每天使用的产品,而整个开发过程从头到尾充满了“不可规模化”的操作。他常对创业者说:“你们如何用 10% 的功夫拿到 90% 的收益?”——也就是所谓的 90/10 解决方案 (90/10 Solution)。虽然创业者们听到这种话往往会面露难色,但保罗的资格足以让这句话变得无法反驳。

Gmail:从个人邮箱到十亿用户

保罗最初之所以动手做 Gmail,是因为他对当时公司配发的邮件客户端极度不满。谷歌内部要求员工在 Windows 上运行 Outlook,而保罗喜欢在 Linux 桌面工作,于是他想了个极其粗暴的办法:把谷歌已有的新闻组产品 Google Groups 的用户界面抢过来,把自己的个人邮箱塞进那个界面,并在其中阅读邮件。当他第一次在这个丑陋的替换 UI 里看到自己的邮件时,他突然意识到这就是他要的。从此他抛弃了原先的邮件客户端,并在接下来的一段时间里只根据自己需要来添加功能:比如他突然想写一封回信,才去实现了“撰写邮件”功能。其实保罗可能几天都不回信,但他先解决了自己最痛的“读”的问题,直到必须回复才做写邮件的模块。

第一次把同事叫来试用时,那位邻座的同事评价道:“这东西不错,加载飞快,只有一个问题——里面全是你的邮件,我想看的是我自己的邮件。”保罗这才反应过来:他得为用户构建登录和个人邮箱。这又是一个典型例子:先做出自己能用的东西,再为下一名用户适配。

随着 Gmail 在谷歌内部病毒式扩散,有一天保罗照常晚到办公室,却发现同事都用异样的眼光看他。原来 Gmail 服务已宕机整整一上午。保罗抓起螺丝刀冲进服务器机房,发现一块硬盘损坏,由此他深刻领悟到“人们对电子邮件的稳定性要求极高,必须永远正常工作”。这一教训也影响了他后续的产品优先级。

另一个流传甚广的误解是 Gmail 的邀请制。外界一度认为那是天才的病毒增长策略——用户拿到四个邀请名额,像稀缺货币一样诱发传播。事实却是:当时谷歌没有足够的服务器硬件来支撑全量开放。保罗团队硬盘容量吃紧,再多加用户就会崩。邀请系统完全是受限于硬件的无奈之举。他们并不是先设计了病毒式传播,而是因为“手头就这么多服务器,今天只能邀请这么些人”,压根就没有其他选项。

Facebook:按学校复制粘贴的服务器

达顿随后分享了自己亲眼所见的 Facebook 早期架构。那个年代,初创公司需要自己购买服务器并托管至数据中心。他当时所在的视频公司 imeem 在圣克拉拉的一个数据中心里租用机柜,达顿习惯观察邻居的服务器标签。有一天,隔壁三英尺远的机架顶端贴上了新标签:thefacebook.com。

对面大约只有八台服务器,都是廉价的 Supermicro 机器,线缆凌乱,但交换机指示灯闪烁异常疯狂。达顿注意到每一台服务器的机身上都贴着大学的名字:一台叫“斯坦福”、一台叫“哈佛”……这完全符合当时 Facebook 的产品定位,一个只覆盖几所大学的校园社交网络。

随着他们频繁往返数据中心,Facebook 的机架上不断新增贴着新校名的服务器,于是达顿恍然大悟:Facebook 的规模化策略是——为每一所学校复制一份完整的 PHP 代码,部署完全独立的 MySQL 数据库和 Memcache 实例。每个学校的用户只访问自己的专属 URL,例如 harvard.thefacebook.com,如果试图访问 stanford.facebook.com 会直接报错。这些服务器之间互不干扰,不必面对跨校共享数据带来的扩展性挑战。

当时硬件性能低下,MySQL 在单表承载海量用户时几乎不可用,Facebook 的团队通过“不做全局用户表”绕过了这个不可能的任务。他们用复制粘贴和购买大量廉价服务器把问题无限期往后推,直到许多年后才真正搭建起一个全局用户表。如今 Facebook 内部可能很多工程师都不知道这段历史,但那个粗暴的分离式架构正是通往今日巨头的起点。

Twitch:直播视频的静态页面与延迟推流

切换到 Twitch 的经历,达顿和迈克尔给出了多个类似案例。Twitch 作为直播视频平台面临的流量模式与传统网站完全不同:普通网站的峰值大约是稳态流量的 2—4 倍,而直播平台的名人突袭可以瞬间带来 20 倍的瞬时并发,根本无法预先测试。

当某位明星突然开播,十几万观众在同一秒访问页面,动态生成的用户名、观看数等元素会瞬间击垮应用服务器。Twitch 团队为此开发了一个紧急按钮,可以把任意页面强制切换为完全静态页面——用户名不显示、观看数不更新,只剩下视频播放器和聊天框,而聊天用的是另一套系统,视频又是另一套。静态页可以被极度缓存,任凭再多用户访问也打不垮后端。虽然观众可能发现某些功能暂时失效,但视频正常播放、聊天正常滚动,直到事后他们才慢慢用更优雅的方式去解决局部动态化的问题。这个功能在 Twitch 的早期救过无数次命。

第二个 hack 来自视频流的传播机制。如果一位大主播突然开播,粉丝全员涌入,一台视频服务器根本扛不住。正常做法是根据在线人数逐步扩散视频流,但这要求提前知道流量规模。Twitch 的困境在于,名人宣布开播后,流量的集中涌入是在开播瞬间发生的,算法根本来不及反应,往往第一台视频服务器直接宕机。

卡伊尔 (Kyle) 和艾米特 (Emmitt) 想出的办法既脏又有效:网页端早就知道有多少人正在等待开播,而视频系统不知道。既然如此,让网页在主播点击“开始直播”后延迟几秒才向观众展示画面,同时在那几秒内静默地把原始流分发到足够多的视频服务器上。对观众来说,延迟几乎不可感知,但整个系统不会崩溃。这完全不是一个优雅的方案,却做到了优雅的效果。用达顿的话说,“超级脏,但它能跑”。

Friendster 与 MySpace:扩展网络的一行代码

在讨论 Facebook 的服务器 hack 时,迈克尔回忆起早期社交网络领域的一桩公案。Friendster 的一个招牌功能是:当用户登录时,系统会实时计算并显示该用户的二度人脉人数。这需要查询好友列表,再查询好友的好友,并以新鲜的数据展示出来。创始人乔恩·艾布拉姆斯 (Jon Abrams) 认为这是极具吸引力的功能,然而后台的 MySQL 为此付出了巨大代价——大量复杂查询导致了频繁的数据库死锁和线程耗尽,Friendster 团队不断招揽工程师优化内核和调整 Linux 线程数,却始终无法根治。

同期竞争的 MySpace 则采用了他们自己的“不可规模化”方案:根本不计算。页面上只做一行判断——如果某人已在好友列表中,就显示“某人在您的好友列表中”;如果不在,就显示“某人在您的扩展网络里”。无论真实的关系距离是多少,标签永远只有这两种。这个改动几乎不消耗数据库资源,却给用户提供了相似的社交发现感。Friendster 花了巨量工程资源试图修复的那个“重要特性”,MySpace 用一行条件语句完美替代。结果 MySpace 一路狂飙,Friendster 却倒在了自己硬扛的负载之下。

imeem:用视频播放器放音乐

达顿接着讲述了自己创办 imeem 时的实战。当时在浏览器中嵌入视频播放需要启动外部应用程序如 RealPlayer,体验极差且经常导致浏览器崩溃。YouTube 的创新在于利用 Flash 直接于浏览器内播放视频,无需任何外部依赖,这在当年是一项重大突破。imeem 想做音乐服务,却发现市面上优秀的流媒体工具都围绕视频构建,音频方面的轮子又少又糙。

他们的“脏方案”极为巧妙:直接用开源的视频工具链,把所有用户上传的音频文件转码为 .flv 视频文件,其中视频比特率设为零,音频部分保留。整个网站的播放逻辑完全基于 Flash 视频播放器,对于系统来说,每一首歌都是一个无声视频。im imeem 本质上成了一家没有视频画面的视频网站。这个决定让他们立刻享受到成熟的视频流工具生态,快速上线了产品,而没有陷入从头开发音频专用组件的泥潭。

Twitch(续):免费对等互联与众包翻译

Twitch 还面临巨大的带宽成本压力。初期他们融资能力不佳,钱总是不够用,但全球观众对视频的需求却持续增长。通过从 YouTube 早期团队挖来的网络运维专家,他们学到可以和世界各地的 ISP 建立免费对等互联 (Free Peering) 关系,双方互免流量费,从而绕开昂贵的传输中间商。许多运营商乐意合作,但某个国家的主流 ISP 却拒绝。

Twitch 在该国流量巨大且完全无法通过广告变现,带宽账单却每月飞涨。他们最终在用户观看直播十分钟后弹出一幅遮挡整个视频的提示,上面写着:“您使用的 ISP 未与我们建立免费对等互联,因此无法继续提供服务。如欲投诉,请拨打以下电话或发送邮件。”并附上该 ISP 的客服号码与邮箱。结果不久后,那家傲慢的 ISP 真的同意建立对等互联。一个名不见经传的旧金山创业公司靠这个看上去无厘头的方式撬动了现实世界的商业谈判。

另一个问题是多语言翻译。Twitch 面向全球用户,需要将界面翻译成数十种语言。咨询专业翻译公司得到的报价高得离谱,他们根本负担不起。达顿团队借鉴了 Reddit 的做法,搭建了一个极简网站,将每一个英文字符串展示给非英语国家的访问者,并询问“您愿意把它翻译成您的母语吗?”起初连质量审核都没有,直接展示社区提交的翻译。面对质疑他们的人,回答始终是:“总比用户看到的全是英文强。”后续他们才慢慢加入多人交叉验证等机制,但用社区众包的方式,零资金投入完成了全产品的多语言覆盖。

Google:当索引停滞——MapReduce 的诞生

达顿补充了一个关于谷歌的二手故事。谷歌最早的搜索算法基于其著名的 PageRank 论文,但在 2001 年前后,互联网上的网页数量疯狂增长,而搜索引擎的索引工作仍依靠一个巨大的批处理脚本——类似于运行 reindex_web.sh。这个脚本是整个搜索更新的唯一通道,一旦失败,谷歌的搜索结果就会停滞。

据说某一天这个脚本开始反复失败,团队尝试修复后重启,但再次崩溃。接下来的三到四个月里,谷歌内部陷入了极度恐慌,因为所有用户看到的一直是旧的索引,新的网站完全无法被检索到,而用户对此毫不知情。正是在这种“搜索引擎已经死了几个月但外界未察觉”的巨大压力下,团队被迫重新思考架构,最终创造出 MapReduce 框架——一种将爬取和索引任务拆分成无数小片段并行处理的系统。后来以此为基石诞生了 Hadoop 等一系列技术,如今几乎每个大型互联网公司都在使用其衍生品。

这个故事最耐人寻味的是:谷歌并没有在早期就建造一个可伸缩的分布式索引系统,而是先用简单的单体脚本撑到极限,直到它完全崩溃之后,才被迫去构建那个日后改变行业的基础设施。如果他们在没有用户的时候就去设计 MapReduce,也许根本不会诞生出那个极致贴合业务痛点的方案。

结语:先开水龙头,再修管道

整期谈话收束于迈克尔反复引用的那副比喻。如果你有一座充满未知漏点管道系统的房子,最好的检测手段永远不是预先检查所有可能,而是直接打开水阀,让水流过每一根管,哪里喷水修哪里。几乎所有创业公司的实际历程都是如此:松开总阀,迎接洪水,抢修爆点,冲洗,重复。人们总以为伟大的企业背后有一套事先书写的宏伟蓝图,其实从来都不是。他们所做的是先做出人们真正想要的东西,用尽所有不可规模化的手段来证明需求,然后才赢得构建可扩展架构的权利。

达顿举了苹果的例子:当年史蒂夫·沃兹尼亚克 (Steve Wozniak) 手工焊接出第一台 Apple I 电脑,与今天设计 AirPods 的精尖工艺形成对照,同样的公司,完全不可同日而语的技术。但若无沃兹尼亚克那原始的手焊板,就不会有后来的工厂流水线。谷歌正是靠极致好用的搜索质量获得了足够的用户,才有资格去打造 MapReduce 等一系列强大的内部工具。Airbnb 的创始人为早期房源拍摄照片,DoorDash 的创始人亲自开车送餐,这些不可规模化的事同样适用于软件世界。不要用完美去妨碍可行,想方设法把产品送到需要它的人手中,再回头一个个解决随之而来的问题,就已经跑在了大多数人的前面。