1. 从TikTok全球宕机说起:先别吃瓜,先看影响面
TikTok全球宕机这几个字最近几乎刷屏了技术社区,身边不少不刷短视频就难受的朋友那天都来问我:为什么一个体量这么大的App能说挂就挂?为什么恢复得那么慢?真实原因还能不能信?我自己做高并发服务很多年,看到这类新闻的第一反应不是凑热闹,而是条件反射地打开三样东西:监控大盘、变更记录、依赖关系。因为大多数所谓“全球宕机”,拆开来看都不是什么玄学,而是一套极其复杂的系统在某个点被突破后,依赖链条被逐级点燃。
很多人以为“全球宕机”就是所有服务器一起冒烟,真实情况完全不是。哪怕是最极端的案例,也几乎不会出现全球所有硬件同时损坏的情况。更常见的形态是:某个全局公共组件先挂掉,比如配置中心、账号体系、网关规则库、证书管理,接着所有区域的请求都开始异常;或者某个区域的故障通过重试和流量调度把灾难扩散到了其他区域。TikTok这类产品体量尤其大,用户触达的是App、网页、广告、开放接口多种入口,背后是客户端网关、推荐服务、视频存储、消息推送、数据中台一大串组件。任何一环出问题,用户感受到的故障表现都可能完全不一样。
这篇文章不打算替任何平台下结论,那也不是外人能拿到的信息。我想做的是把“全球宕机”这类事件的排查思路、架构弱点和复盘方法拆开,讲清楚发生了什么、为什么发生、以及如果你是自己团队里负责稳定性的人,能从这次事件里抄走什么作业。无论你是后端开发、SRE、客户端工程师,还是刚入门运维,这篇文章都能给出一些可以直接落地的动作。
先记住一个最核心的判断方法:遇到故障,第一件事不是定位根因,而是收敛影响面。所谓收敛影响面,就是弄清楚挂了哪些入口、影响了多少用户、当前流量跌了多少、有没有办法先止血。我是见过那种一上来就全员翻日志找bug的团队,翻了两小时才发现回滚一下就能恢复大半。这种事一旦经历过一次,你就再也不会把“定位”放在“止血”前面了。
1.1 白屏、转圈、登录失效分别指向哪一层
用户感知到的故障现象,是定位问题的最佳入口。以TikTok这类内容App为例,常见的故障现象可以按层级归类,每一类背后都有比较明确的嫌疑对象。
如果你打开App后首页完全白屏,或者推荐流一直转圈,最常见的问题是API网关或Feed服务异常。这属于业务服务层故障,需要重点看最近是否发布过推荐服务的版本、是否调整过接口字段或超时时间。
如果登录状态集体失效,点了登录也没反应,那问题大概率出在账号体系或SSO会话存储上。账号服务的特点是全局共享,一次缓存集群抖动、一个鉴权配置写错,所有老用户的登录态都能瞬间变成无效。
如果视频能刷出来,但点击播放后黑屏或一直缓冲,那问题往往在媒体网关、CDN或对象存储。这类故障还存在一种隐蔽情况:CDN节点全挂不挂看区域,用户密集的区域先受影响,容易被误判为“局部网络问题”。
如果所有操作都报错,甚至App启动都卡在某个进度条,那就要往公共组件上查了。DNS解析、证书失效、配置中心推送异常、AB实验SDK启动超时,都可能造成全局性不可用。
把现象按层分类之后,再配合后端监控,你就能把排查范围从“整个系统”缩小到“某条链路”。我在处理类似问题时,会随手拉一张影响面表格,记录每层服务的状态、错误率、P99耗时、最近30分钟变更、负责人。这张表不用等监控平台生成,手写更快,关键是它能把团队的信息口径统一起来,避免后端说是网关问题、网关说是网络问题,最后全在群里各说各话。
1.2 “全球”这两个字背后的三种传播路径
“全球同步挂”是外界最容易误解的事。大多数人会想象成每个区域的机房都遭了殃,真实工程里最常见的是三种传播路径。
第一种是配置传播。一个全局开关或一套推荐配置在配置中心被改错,增量推送到所有区域,所有服务在同一时刻读到同一份坏配置。这种故障最凶险,因为它的时间点极其整齐,几乎不会给你反应窗口,而且影响面天然就是全量。
第二种是依赖传播。某个区域的数据中心出问题后,负载均衡会把它的流量切到邻近区域。邻近区域原本已经按水位跑,突然多出来的流量一到,立刻过载。过载区域的失败请求又会触发客户端重试,重试流量再次反向打到原来故障区域,形成跨区域踩踏。这就像一条高速路堵死之后,所有车都绕到小路,小路瞬间也瘫痪,最后整个片区交通归零。
第三种是逻辑传播。一次核心算法模型或权限模型的升级,没有在灰度阶段发现错误,部署到全量后所有区域用同一套错误逻辑做判断。这种问题最难定位,因为服务本身没有崩溃,接口也返回成功,只是返回的内容或鉴权结果全错了。
所以要给“全球”两个字祛魅。它不等于全球硬件同时损坏,而是“全局公共因子”失效。从这个角度来说,“全球故障”反而比“区域故障”更好定位,因为只要找到那个被所有区域共用的组件,根因就浮出水面了。
2. 根因定位:为什么嫌疑总在发布、容量、依赖三者之间
每次大型平台宕机后,媒体和网友都会猜测各种原因,什么攻击、停电、砍预算,都有。但从工程视角看,真实世界的根因绝大多数逃不出三类:发布变更、容量过载、依赖失效。TikTok这类产品迭代速度极快,推荐模型、App版本、服务端配置的发布频率远超普通企业,所以命中发布变更的概率也更高。
我以前处理过一次接口超时事故,当时两拨人吵成一团:一拨说缓存集群有问题,一拨说数据库有问题。我把监控时间轴拉出来之后就安静了——缓存集群刚好在告警前有过一次主从切换,而主从切换和告警出现之间的间隔,正好等于缓存过期时间。这个案例说明,只盯着瞬时快照看问题,会被假象迷惑,必须把时间线和变更记录放在一起看。
发布变更是头号嫌疑人,原因不复杂:系统平时处于稳态,一次变更打破了稳态。如果这次变更没有经过小流量灰度、没有自动回滚阈值、没有分批放量,它就有概率在任意规模上引爆问题。你以为改了个推荐缓存TTL从300秒变成30秒很安全,下游数据库流量可能直接翻了十倍。查这类问题最锋利的工具就是变更记录:把部署时间、配置生效时间、告警开始时间放到同一坐标轴上,偏差通常不超过几分钟,一对比就知道是谁干的。
容量过载是第二类常见根因。容量问题最迷惑人的地方在于表象与实质不一致。很多时候Web服务器CPU不高、负载均衡带宽不忙,但核心数据库的慢查询和连接等待已经爆了。因为真正的瓶颈在底层存储,压力全部堆在数据库一侧,表面看起来云淡风轻,其实已经逼近悬崖。短视频场景的流量突刺尤其厉害,一个热点话题、一位头部作者集中发内容,都会让回源流量暴涨。如果首页Feed接口的多级缓存不够深,大量请求瞬间绕过缓存直接击中数据库,哪怕平时负载只有20%,也能在几分钟内把连接池打穿。
依赖失效是第三类。现代架构没有一个服务是孤立的,所谓的依赖不只是数据库和Redis,还包括对象存储、推送通道、第三方登录、支付回调,甚至一条操作系统调用。一个很经典的例子是:AB实验SDK如果在启动时同步拉取试验配置,而配置中心出现抖动,那么所有App启动都会卡在初始化阶段。这就是“一个公共依赖连坐全客户端”的典型场景。
定位依赖失效有一个技巧,看调用链是不是在等待下游响应。如果P99明显恶化但错误率不高,大概率不是代码逻辑坏了,而是下游变慢。如果错误率也高,再进一步区分是连接失败、超时,还是下游返回异常错误码。不同的失败模式对应不同的问题,排查路径完全不同。
2.1 先回滚还是先定位:一个反直觉的决策模型
我说一句可能让严谨派皱眉的话:故障发生后的前五分钟,正确顺序不是“定位再修复”,而是“能回滚就先回滚”。这不是不讲科学,而是概率问题。大多数全局故障都与最近一次变更强相关,回滚能把八成问题先压下来。回滚有代价,但让故障多持续一小时的代价更大,尤其对全球化产品来说,多一分钟故障就是多一整片市场的用户流失。
我给团队常用的决策模型是这样:0到5分钟,做“熵减”动作,哪里最近改过就先还原哪里;5到15分钟,如果回滚没有立竿见影,再进入深入定位;15分钟以后,团队按“假设-验证”循环并行推进,不要轮流猜。回滚不是认错,它和重启、限流、切流量一样,只是恢复手段之一。正规的发布平台会在每次变更时保留上一个稳定版本,发布进程检测到错误率超过阈值后自动切换回去。这些机制平时不显山露水,关键时刻能救命。
还有一条血泪教训:恢复之后不要立刻宣布“没事了”。有一次我所在的团队恢复了主服务,半小时后故障复现,原因是我们只回滚了A配置,没注意到B服务也依赖同一份配置,B一直处于坏状态。正确做法是,回滚后盘点“与本次变更相关的服务列表”,对每个受影响服务逐一确认健康,再观察15到30分钟,确认核心指标稳定,才对外宣布恢复。宁可让用户多等十分钟,也别让媒体多写一篇“二次宕机”的稿子。
2.2 监控盲区与告警阈值是怎么把故障放大的
很多团队定位根因慢,不是因为技术不行,而是监控根本没把异常暴露出来。最常见的问题是只看单一指标。比如只监控错误率,结果服务不响应、请求全挂在等待队列里,错误率反而很低,灾难从“请求量骤降”开始,你却一无所知。正确的监控应该同时看四个维度:成功量、成功率、耗时、容量水位。这四个指标放在一张图里,才能还原“服务还活着但已经动不了”的真实状态。
告警阈值也要分等级。阈值太灵敏,天天半夜被叫醒,值班人很快麻木;阈值太迟钝,等严重故障出现时已经晚了。我常用的方案是双层告警。第一层是抖动预警,规则类似“P99比基线高两倍但总量低”,只发到值班群,不打断人。第二层是严重告警,规则是“错误率或白屏率连续超过X分钟”,直接打电话。每个团队都应该根据自身业务定义基线,而不是抄别人的模板。告警分级的目的,是让少数真正重要的信号能打断人的正常生活,把注意力留给最关键的故障。
3. 高并发架构的脆弱性:冗余为什么有时反而放大灾难
接下来想聊一些更系统性的问题。很多人一提到大平台宕机,第一反应就是多加服务器、多做副本,好像只要机器够多就不会挂。真实的高并发系统远没有这么简单。冗余有时候不但不救人,还会放大故障。
故障一旦发生,流量调度机制会让请求在可用节点之间重新分配。举个简化模型:假设你有三个可用区,各承担33%流量,其中一个挂了,它那部分流量会自动打到另外两个。如果这两个可用区的冗余余量只有20%,突然多出来的15个百分点就会让它们瞬间过载。如果再叠加重试风暴,客户端自动重试一次、网关重试一次、SDK内部再重试一次,请求会被放大三五倍甚至更多。到这一步,问题已经不再是某个组件坏了,而是整个系统被自己产生的流量淹没。
所以高并发设计有一条铁律:不做无限重试,不依赖“多副本就能扛”。每一层之间都要有明确的流量预算,对下游调用要设置足够短的超时时间,拒绝“失败就立刻重试”的默认写法。短视频场景里最典型的场景是播放器请求媒体地址失败,客户端几秒内自动重试三次;假如播放地址服务每秒承接10万次请求,可用率95%,就意味着每秒有5000次失败,5000次失败再被放大成上万次重试,对下游的冲击非常可观。这还只是正常情况下,一旦某个环节真的抖动,重试会直接把它拖死。
优雅降级是比“保持完整”更重要的能力。一个推荐接口如果依赖了六个子服务,没必要要求全部成功才返回。应该把强依赖压缩到最小,比如“必须有推荐结果才能渲染首页”,其余弱依赖失败时返回降级内容。TikTok这类内容产品特别适合做降级:拿不到个性化推荐,可以返回热门榜;点赞服务失败,可以先让用户正常看视频,把点赞操作放进本地队列延迟同步。降级的本质是把“非要不可”的范围压缩到最小,给系统留出存活的余地。
要特别提醒的是,降级开关本身不要挂在业务主链路上。如果每次请求都要先去远端配置中心拉开关,配置中心一挂,业务也跟着挂,那这个开关就成了一种新的故障源。更稳妥的做法是本地缓存放一份配置,定期异步拉取,拉不到就继续用本地值,只有明确变化时才更新。
限流也一样不能只在入口网关做。入口限流能挡住外部流量冲击,但挡不住服务之间的循环调用。我建议在数据库和缓存前面再套一层轻量限流,防止业务代码里某个bug把流量放大到存储层。更务实的做法是给每个服务设置“最大并发”和“最大QPS”两个硬性数字,超过阈值直接返回“系统繁忙”。让用户看到明确提示,比让用户无限转圈好一百倍。
3.1 全局公共组件和客户端启动,这两个盲区最要命
除了业务服务本身,有两个地方最容易成为全球级故障的火源。第一个是控制平面组件,也就是配置中心、证书管理、消息交换机这类基础设施。它们平时流量低、运维关注少,但一旦故障,影响面远超业务服务。比如证书管理平台故障导致所有边缘节点证书无法续签,客户端握手会大面积失败;配置中心故障会让所有服务拿不到最新配置,只能靠本地缓存硬撑,撑不住就全部回退到默认值。这类组件在做架构设计时必须三副本起步,跨区域部署,而且要定期演练故障切换。
第二个盲区是客户端启动链路。很多故障本身是服务端的错,但用户感知的体验由客户端决定。如果App启动时所有SDK都要同步初始化,一旦其中一个SDK的后端抖动,用户打开App就会白屏三秒甚至更久。正确的分级方式是把依赖分成A、B、C三级:A级依赖,比如首页数据和登录态校验,必须同步等待;B级依赖,比如消息推送、广告、埋点,可以异步初始化;C级依赖,比如配置拉取、热修复,放到后台慢慢做。我做过不止一次这种改造,效果立竿见影:同步改异步后,好几个远程服务失效的情况下,用户依然能看到一个能操作的主界面。
在故障态下,客户端还要学会“体面失败”。服务端全挂时,如果客户端能把本地最近成功的内容缓存先渲染出来,用户感受会好得多。内容型产品特别适合做这种本地缓存,因为推荐流本身是可丢弃数据,宁可给用户看一小时前的内容,也远比白屏强。这里我要说句大实话:这个需求产品经理一般不主动提,需要技术团队把它当成稳定性债务写在迭代清单里,主动去做。
3.2 全球多活没那么简单:数据一致性和容量预算的账
聊到全球容量,就绕不开多活架构。TikTok这类产品要做多活,先要回答一个核心问题:用户数据放哪里。常用的设计是“区域自治,中心聚合”。用户归属某个区域,读写优先访问本区域,跨区域只复制必要元数据或热数据。好处是区域故障时,把用户切到备用区域照样能刷到内容;坏处是跨区域复制有延迟,点赞、关注这类一致性要求高的操作,切换后容易出问题。
很多切换后的用户体验是“视频还能看,但点不了赞、关注列表空了”,这就是典型的浏览可用、写不可用。原因是写链路依赖跨区域强一致,而网络延迟和分区恰恰会破坏强一致。架构上没有银弹,只能在一致性、可用性和延迟之间选。做容量规划时,要提前预估每个区域的故障冗余余量。两个区域各承担50%流量的话,灾备切换后,单区域至少要能扛60%到70%的流量,否则切换就是“把A的故障传成B的故障”。
这个冗余数字不能靠拍脑袋,要靠压测得出真实容量。我记得以前做全链路压测时,会在低峰期用影子流量把核心链路打一遍,记录每个环节的瓶颈点。平时不做压测,故障来了再临时扩容,往往来不及。压测不仅能测容量,还能顺便暴露监控盲区,属于投入产出比很高的稳定性投资。
4. 事故复盘:怎么把一次大宕机变成团队资产而不是批斗会
做复盘的目的是防止再犯,不是找替罪羊。这可能是国内技术团队最容易跑偏的地方,好好的复盘经常演变成甩锅大会。真正有效的复盘要回答三个问题:为什么我们没有提前发现?为什么我们没有更快恢复?未来如何系统性地降低发生率?回答时最忌讳的是“完善监控、加强测试”这种空话,每一项都要落地成可执行、可验证的具体动作。
先看“为什么没提前发现”。原因通常落在三方面:监控盲区、告警太慢、SLO不敏感。很多团队监控了服务端接口错误率,却没监控客户端白屏率;监控了CPU使用率,却没监控用户可感知的启动成功率。正确的思路是把监控体系倒过来建设,以用户体感为核心指标,比如启动成功率、Feed加载成功率、播放成功率,然后往下拆到接口层,再往下才是集群资源。只要把用户可感知的指标监控起来,很多大型故障在用户大面积吐槽之前就能被发现。
再看“为什么恢复慢”。这往往不是技术问题,而是协作问题。经典场景是后端说网关挂了,网关说负载均衡有问题,负载均衡说网络没问题,来来回回一个小时,没人敢拍板。我给团队的建议是设一个事件指挥官,授权他决定回滚、扩容、切流量。指挥官不一定技术最强,但必须能把信息快速收敛成决策。事件群里所有讨论对指挥官透明,但不能干扰决策。复盘时再审视指挥流程是否清晰,而不是只抠某一行代码的问题。
行动项一定要能被验证。比如“优化缓存”太笼统,要写成“在Feed服务Redis前增加本地缓存,目标把Redis QPS降低70%,压测验证后上线”。复盘里的每个行动项都要有负责人、截止时间、验收标准。没有验收标准的事项等于没写。我还会额外加一项演练要求,专门验证回滚和降级开关的有效性。因为系统在演进,依赖在变化,几个月前验证过的恢复方案很可能已经失效,不演练等于纸上谈兵。
复盘文档建议用固定结构:事件概述、详细时间线、根因树、行动项表、经验教训。根因树不要只写单一根因,把所有可能影响因素按因果链路写出来,标明哪些被验证、哪些被排除。最难写的往往是时间线,尤其是“第一次异常是什么时候”。所以平时一定要保存发布、变更、告警的完整记录,并且保留至少半年。真到了复盘时,要能准确翻出“当天凌晨2点发布过一条配置”,不能靠人脑回忆。
4.1 对外沟通:状态页和公共说明比技术修复更容易被记住
全球级故障还牵涉对外沟通,这块做砸了,伤害比技术故障本身更大。平台方应该第一时间更新状态页,模板大致是“我们正在调查XX问题,暂时影响XX功能,恢复时间未知”,然后按时更新“已定位原因,正在恢复”“已恢复,预计稳定”。中英文要同步,避免用户和媒体在信息真空里自己制造恐慌。很多离奇的“原因曝光”都来自对外口径不透明,技术团队应该和公关、客服建立一个高效沟通通道,不要等技术完全定位再对外发声。
对外公告还有一个原则:不要给出未经验证的具体原因。我见过有公司把“疑似数据库过载”发到状态页,后来证实是配置错误,再改口就很被动。更稳妥的说法是“目前已定位到与前段基础设施相关的问题,正在逐步恢复”,原因细节等内部复盘确认后再通过技术博客或新闻稿说明。既透明,又不会给自己埋雷。
4.2 从“故障练兵”到“团队肌肉记忆”
再多说一句演练的事。很多团队的改进项停留在文档里,真正遇到故障还是手忙脚乱。我强烈建议每年做一次故障演练周,挑一个低峰时段,故意停掉核心服务的一个实例,再停一个区域,然后看监控、告警、降级、回滚、沟通链条是不是真的能跑通。第一次通常很狼狈,但狼狈得越彻底,暴露的隐患越多。
演练不能只拔虚拟机的网卡,还要模拟配置中心不可用、证书过期、数据库只读这类和数据相关的故障,因为这类故障最容易让平台“全球同挂”。如果每年能认真做完一轮,下一次真正的全球宕机来临时,团队的反应速度会有肉眼可见的提升。
5. 可以直接抄走的排查清单与实用建议
写到这里,我把自己的经验浓缩成一份可复用的清单。它不局限于短视频平台,任何高并发系统都能参考。
第一,建立基线和时间轴。平时记录核心接口的QPS、P99、错误率,否则故障时你根本不知道“正常”长什么样。同时把所有告警、发布、回滚、DNS、证书记录集中到一个时间标准,方便随时打出一张全局时间线。
第二,预先写好回滚和降级方案。每次变更都保留上一个稳定版本,最好能一键回滚,不要临时写脚本。降级开关平时用不到,但必须定期验证它是“通电”的。
第三,准备好外部通讯模板。故障一旦发生,公关、客服、运维要能秒发。临时手写对外公告是大忌,人紧张的时候是写不出清晰文字的。
第四,按黄金四步处理故障。确认影响面、汇聚信息、执行恢复、验证稳定。第1步和第2步不要在十分钟内无限拖延,如果一时间没有明确结论,先启动恢复动作,边恢复边定位。这套流程我实测下来,可以把平均恢复时间压缩至少三成。
5.1 常见问题速查:天灾还是人祸
很多人会问,为什么这样的大型平台还能出现全局配置错误,难道没有审批吗?审批流只能防住“人为误操作”,防不住“配置值本身与全局默认值冲突”。很多配置平台缺少值校验,比如只允许TTL填写10,但不校验单位是秒还是分钟,一个细微差异都可能酿成事故。
还有人问,为什么故障总发生在“没想到”的时刻?因为所有成熟的系统都是靠“罕见的临界组合”出故障的。单看某个环节,它都经过测试;但配置、流量、依赖、时间四个变量一叠加,就进入了测试没覆盖到的盲区。这也是为什么混沌工程和故障演练那么重要。
5.2 最后一个实操心得:把故障当成年终审验
我在好几家公司最想推动的一件事情,就是把“稳定性”从口号变成预算和人力上的真金白银。每次看到TikTok这类巨量级平台的“宕机原因曝光”,我的第一反应不是嘲讽,而是当成行业公开上演的实战课。大家看到的是新闻标题,我看到的是自己没有踩过的坑。
我的建议是,下次遇到类似事件时,不要只是吃瓜。打开自己负责系统的监控面板,对照本文提到的检查点过一遍:最近有没有大变更没走灰度?降级开关还灵不灵?回滚方案能不能一键执行?客户端启动有没有同步依赖?状态页模板准备好了吗?这些问题里只要有一个答案是“还没”,那这次全球宕机对你来说就是很有价值的提醒。
重大故障是最贵的老师,也是最好的老师。与其等自家系统出事后悔,不如把别人的事故拿来当自己演习的素材,顺手把系统的韧性往前推一档,这才是这类新闻最大的价值。