这几年,作者在饿了么和阿里本地生活经历了业务快速发展的黄金时期,也遇到了一些令人"出乎意料"的事故。
2018 年的 915 事故,应该是这几年最严重的一次宕机,直接导致技术组织调整和一位技术大牛的离开,公司也赔了数亿的红包;1225 圣诞节宕机事故更是导致骑手罢工,甚至半年后还有城市经理和作者抱怨说:"如果那一天系统不出问题,订单量肯定会创新高。"

2018 年 915 事故官方公开信
经历了大小数以百计的事故后,从 82 原则上看,我发现 20% 是因为人员能力和机制流程的欠缺,80% 则是因为人员的稳定性意识不足,并且故障应对方法不当。而作为技术 Leader 的你,如何认识稳定性、如何应对故障、如何从过往的事故中汲取足够的经验,就成为一个团队能否做好稳定性的关键。
怎么衡量系统稳定性?
一般来讲,通过统计系统不可用的时长或次数就可以对稳定性进行量化,比如业内常说 4 个 9 的可用性(即 1 年内 99.99% 的时间系统是可用的,不可用时长仅为 52.6 分钟)。
在饿了么和阿里,每个财年我们会确定稳定性 KPI,以事故数的计量为准,结合团队情况和过往结果背负不同数量的事故指标。事故按照影响程度的不同会划分为几级,不同级别的事故数指标也不同。所以针对稳定性的提高也可以看作围绕事故的治理,可以从事故发生的前、中、后分阶段来看对应的关键点。
事故的类型:可用性事故、资损类事故。
事故前预防:主动治理减少系统的风险隐患,重点在变更管控、可用性设计、应急预案与演练。
事故中应急:“止血、恢复”是原则。
事故后复盘:目的不是追责,查根因、改进架构、完善应急、总结经验才是我们想要的。
本文,我们来了解一下故障的应急与事故的复盘(预防治理我会在下面两讲分不同事故类型来详细讲解),因为事故的发生要结合具体的上下文背景、系统架构、甚至组织结构来看,并非千篇一律。
希望今天的内容能帮你更深刻地理解稳定性的价值,并结合实际工作更加有条不紊地应对线上故障、有价值地进行事故的复盘总结。
你遇到的事故是什么类型?
从事故特性上看,我们可以分为可用性事故和资损类事故。
可用性事故:技术原因导致系统部分或者全部功能不可用,业务没办法正常完成对应流程或者提供对应服务。比如因为网络、DB、接口 Bug 等原因,用户没办法登录、商品列表不显示等。
资损类事故: 系统的功能都能正常使用,但因为逻辑、计算等原因让业务的某一方产生了资金损失。比如用户支付一律为 0 元、错发 999 无门槛优惠券、商户清结算少打款给商户等等。
那么它们的区别是什么呢?
可用性事故的根因大多在技术本身,包括但不限于: DB 设计、接口实现、链路架构、上下游的依赖、中间件的实现等原因,特点是发现容易、杜绝难、业务影响明显、对应急处理速度要求高。
而资损类事故更多和隐蔽的业务逻辑和架构设计的缺陷有关,可能还涉及产品逻辑或业务错配,特点是非常隐蔽、难发现、往往持续时间长、防控成本高、大部分开发同学意识薄弱。
因为这样的特性差异,两类事故在预防治理的思路和方法上有一定区别,但在故障的应急和事故复盘上,思路相对一致。那么当故障发生时,你要发挥什么作用呢?
故障发生时应该如何应急?
事故现场往往伴随着“混乱”,试想一下,你团队的小伙伴昨晚刚发布上线了新系统,今天上午一切正常,下午 App 核心页面突然无法显示、客服反馈说大量用户来电投诉,可谁都说不清楚到底是怎么回事,十几分钟过去了,系统依然没有恢复,类似的场景你是否经历过?
那么这时,你应该起到“定海神针”的作用,故障发生时控场就是你的核心职责。 要有条不紊地安排同学进行排障、确定信息沟通的秩序、结合信息做好线上同步,并参与决策。
在阿里,故障的处理有一个“ 1-5-10 ”的标准,即 1 分钟发现、5 分钟响应、10 分钟恢复。很明显,故障处理的核心在于“快”,让业务最快止血、恢复、避免影响进一步扩大。
故障处理的生命周期,可以分为 4 个阶段:发现异常、排查问题、判断决策、恢复处理。这 4 个阶段对应的行动并不是完全串行的,虽然有一定的依赖关系,但在实际的处理过程中应该并行展开。类似 fork/join 的模式,不断完成小任务、不断汇总信息,不断做出判断与决策,形成循环直到故障恢复。
接下来,我以外卖点餐的业务为例,讲一下故障的发现、排查、决策与恢复都要注意哪些核心要点,因为故障处理的核心原则相同,所以这些要点在其他业务场景中也适用。(时序图与真实系统间我做了一些模糊处理,但是不影响案例说明。)

我们要点外卖的话,大体的流程是这样的:
用户打开 App,根据用户 LBS 显示餐厅列表,选择进入某一家餐厅;
添加菜品到购物车,进入结算页确定收货人、优惠红包、订单金额,生成订单;
跳转第三方支付,并返回支付结果;
商家接收订单推送,操作接单并备货;
骑手根据调度取餐,送达用户,订单完成。
1. 故障发现
用户来电反馈订单无法支付、App 无法登录,研发发现下单 QPS 曲线同比下跌,这些都是事故发生时的现象,虽然现象不完全等于故障点,但通常最早出现异常现象的地方和故障根因关联最大,所以第一时间发现异常对于锁定问题至关重要。
故障发现就是系统异常反馈到研发的过程,这里我画了一个简单的脑图,分类说明故障发现的几种常见方式:

开发同学往往更关注技术类指标,比如 QPS、CPU LOAD,可 Leader 除此之外应该更多地从业务场景出发,结合需求来看系统的业务监控覆盖是否完全。业务监控往往更加敏锐,但是要想用好,就需要对业务和系统有较长链路的理解和掌握,而这恰恰是技术 Leader 的优势。
比如用户进入餐厅后会添加菜品到购物车,并跳转到结算页完成下单。菜品服务会提供一个查询接口,根据餐厅 ID 返回菜品信息。假设这个查询接口最近做了变更,在库存逻辑的处理中埋下了一个 Bug,导致实际库存小于 50 时,库存的返回值被默认为 0,而其他数据则一切正常。
那么此时类似 CPU、内存、I/O 等技术指标可能都不会异常。而因为是部分实际库存小于 50 的菜品被影响,用户依然可以添加其他菜品到购物车,但因为部分菜品库存为 0,用户想吃却没办法下单,那么这个时候订单成交量的环比、同比曲线就有可能下跌,而这个现象会让我们感知到异常,进而排查问题处理。
总的来说,人工的被动反馈在时间和速度上有较强的不确定性,很容易出现“小故障 * 长时间 = 大事故”的情形。而纯粹的技术指标监控又会忽略掉接口正常响应,但是业务异常的场景,只有两者结合,通过监控告警,最大程度上缩短故障感知的时间,才能早发现早解决,减少业务影响。
2. 故障排查
发现异常,接下来就是排查故障点和故障原因,故障排查最直接有效的核心思路就是直接锁定 + 排除。
直接锁定:最近的变更点与异常现象间有直接的逻辑关联,进而可以直接锁定到故障点。比如,刚对下单接口进行了发布变更,接口的 QPS 曲线就暴跌,可以基本断定是刚才的发布导致。
排除法:当干扰因素过多(用户、订单等几个系统同时发生变更,引起订单下跌),很难直接锁定到故障点,就要结合业务场景,让整条架构链路上的所有关联方进行自查自证,通过排除法锁定故障。
这里你要注意的是,要敢于先怀疑、排查自己的系统,再去考虑上下游关联方的问题,为的就是在信息混乱的现场,减少信息的不确定性,以身作则,带领团队成员将范围缩小,针对性地找到问题。
如果你负责的是优惠券相关的系统,在下单的核心路径上,主要的场景就是优惠券领取、发放、展示、核销。你即使不是全部熟悉,也应该与团队同学共同协同,逐个确认这几个场景的核心接口是否有异常,通过对应的监控、日志收集信息并找问题,如果某一处没有发现问题,就排除并继续循环。

如果还是没办法确定问题,或者只能确定大致范围,就要充分利用之前的事故经验了,此时一定要果断,可以结合情况启用标准应急手段(比如服务重启、发布回滚、非关键链路降级)。总的来说,在排障的过程中,如果团队成员都没有头绪,你一定要起到主导作用,可以参考我总结的一些要点做好“控场”。

故障排查三要点
3. 故障决策
既然故障的表现是业务功能有损,那么在故障决策时为了让业务最快止血和恢复,就无法追求完美,一些为了抢时间的有损决策,就需要 CaseByCase 的人为处理。
比如平台要搞一个 517 红包雨的活动 ,但是红包系统逻辑错误,导致满 50-10 的红包发放成满 10-50,此时已发的红包要作废吗 ?不作废有大额资损,作废会导致大量客诉。类似红包错配的场景,业务决策非常复杂,能否第一时间止损很大程度上取决于技术 Leader 的现场反应和操作, 要注意故障决策的两个关键点 :
一定要有明确的决策人、主导者和有效的沟通方式(钉钉群、多人电话会议、紧急作战会议室等),让信息可以通畅地交流出来,并且决策人可以根据情况做判断与取舍,形成所有人明确的处理结论。 比如,第一时间停止错误红包的发放,确保故障没有增量,并把决策第一时间同步给团队成员,并同步相关负责人后续的动作,对已发放的红包,明确要求负责人汇总各类关键信息(红包数量、涉及金额、涉及用户数、有效时长、可能资损等)。
所有的信息一定要数据化,不同的数据量级会导致决策不同,比如红包错发 50W 可能只是暂停发放,但是存量红包依然可以核销,损失公司可以承担。但是如果错发 5000W,大概就要涉及一系列的调整,这是非常影响决策的。
4. 故障恢复
往往业务决策后就需要执行相应的技术操作,最好的情况当然是在系统设计时就准备了预案,那么此时可以安全且快速地执行,并且对不涉及业务决策的问题可以技术直接操作,节省时间,比如常见的应急“三板斧”:变更回滚、服务重启、降级&限流。
而如果没有预案或情况比较复杂,就涉及线上Fix,比如因代码不兼容所以无法回滚,或者故障导致脏数据进而影响正常的业务流程推进,又或者红包金额错误需要做数据订正。你在这个环节要额外注意, 因为一来这种操作相当于一次“紧急变更”,有可能引入新的风险,二来不同的实现 Fix 成本和用时可能不同,Leader 需要给出自己的判断。
比如刚刚提到的红包错配、错发的问题,假设影响金额过大,公司决定对存量红包作废止损,那么站在技术角度有很多方法,你需要让团队成员明确使用哪种方式:
下单环节,在“我的优惠券”中通过前端隐藏对应的红包,让用户无法选择 ;
根据红包批次 ID 或者类型,通过脚本刷数,将红包批量作废 ;
在下单接口的校验环节,增加逻辑判断,禁止这批红包核销 ;
通过风控系统拦截使用这类红包的订单。
技术 Leader 要考虑不同恢复手段引入的新风险、操作用时、用户体验影响的不同,结合当前紧急程度、系统、具体操作人的情况,给出一个技术方面确定的判断。类似的问题,如果完全没有预案,我们之前常见的做法还是不动线上系统以免引入新问题,主要通过刷数据解决。

故障恢复时 Leader 的关注点
讲到这儿,你是不是觉得处理完事故就万事大吉了呢?并不是,你还要对这次事故做一个全面彻底的复盘,不让类似的问题重复发生,那么复盘都要注意哪些关键点呢?
如何有价值地做事后复盘?
复盘的核心不是为了追责或者甩锅,而是最大程度榨干事故的剩余价值,通过全盘的思考与总结,来看看系统设计、流程机制、应急处理、人员安排等各方面有哪些不足,哪些可以提升的地方,哪些问题是共性的,需要在各团队进行“大扫除”。
通过一次事故,解决一类问题,让一个人(团队)踩过的坑变成所有人踩过的坑,正所谓“一次学费,受益终身”。
你可以从时长、现象、处理时间轴、根因、改进计划这几个维度进行复盘, 在以下几个方面进行深究:
事故时长:1-5-10 是否达成,如果没有是为什么?哪个环节用时最多,如何提高和改善?
事故根因:根因不等于直接原因,一个事故的直接原因往往并不复杂,但是根因可能是多个维度的缺失,需要像剥洋葱一样一层层找下去。拿库存接口变更这个Case来说,直接原因就是某段代码逻辑变更导致,但是应该在测试、发布、监控、应急影响、预案设计等多个环节展开去看,根因的挖掘并不忌讳“吹毛求疵”。
事故改进措施:由点推到面、明确到人、明确时间。与根因类似,要结合多个维度形成组合拳的改进点,避免一次性动作,要将重点放在对未来、对同类问题的预防上。核心就是如果再一次发生类似的问题,这些改进措施是不是能起到作用。
关于事后复盘,你可以这样理解,我们要深挖事故如何发生的、如何处理的、未来怎么预防。但要避免情绪化,在复盘会上的反思、感悟、懊恼没有任何意义,如何带领团队把精力放在改进措施的落实以及事故前的治理上更有价值, 另外,你需要留出时间让团队伙伴进行内部的 Review,避免为了开会而复盘。
小结
虽然你会尽全力保障系统的稳定性,但是按照墨菲定律来说故障又一定会发生,这就形成了一个悖论,即不管你怎么努力还是会出问题,所以我一直强调:“稳定性是一个先有意识后有能力的事儿”,这一点尤为重要,毕竟你的态度和认识决定了团队的重视程度。 本文我想强调这样几个重点:
毫不夸张地说,系统稳定性对于研发而言一条生死线,这方面做得不好,其他再好也是枉然,因为稳定性问题“下岗”被优化的 Leader 不在少数。
对于故障的应急响应,业务的止损与恢复是最重要的,决断就是要付出一些代价的。
事故的复盘不是为了追责过去,而是为了在未来避免类似的情况发生。

业务快速发展的同时,技术必然存在妥协。 业务上需要快速的需求交付,技术上需要架构的可扩展,但速度和质量在工程领域总是存在冲突,而稳定性往往就是问题爆发的冲突点。作为技术 Leader,你要平衡好这两种诉求,让技术与业务协调发展的同时,最大程度确保系统的稳定运行,毕竟“没有质量的交付,再多再快都毫无意义”。

留个作业: 把最近半年或一年你印象最深刻的事故重新复盘一遍吧,从事故根因、应急处理以及复盘改进几个角度去 Review,未来类似的事故是否还会发生,如果发生你能更好的应对吗?
最后,感谢你的阅读,如果这节课让你有收获,欢迎你将它分享给其他的朋友,我们下一部分见。
前文我们学习了事故的应急和复盘,但“预防胜于治疗”,所以今天我想从事故预防的角度和你聊一聊可用性治理的关键动作。
可能你听过这样一句俗语:只有千日做贼,没有千日防贼。但是在系统稳定性建设上,却无法通过一次优化完全杜绝事故发生的可能。因为业务发展会带动系统演进,它是一个动态变化的过程,并非一个常量,所以系统可用性的治理是持久战,需要你“保持敬畏,坚持防范”。如果把事故比作火灾,那么技术 Leader 日常工作的核心就是围绕系统的风险隐患,建立“防火墙”。
我们从研发的流程阶段来看,确定产品需求后,会通过架构设计、编码、测试、上线几阶段来交付系统。在这个过程中,上线环节是事故的高发阶段,因为随着变更的加入,原本系统稳定的运行状态会被打破;编码与测试阶段主要是实现功能,但不合理的实现是在架构设计阶段就被埋下的。当然,除了关注技术外,还要从团队、机制等管理手段出发,逐步建立你团队的稳定性军规。
所以今天我想从变更管控、架构设计、管理手段分享系统可用性的治理经验,希望能对你治理系统稳定性有所帮助。
变更会引起90%以上的故障
我们曾以年为单位统计了事故,得出了 90% 的数字,而且理论上说,公司越大、发布越多、这个数字就越大。因为互联网公司的研发模式基本都是“小步快走、高速迭代”,一个业务一周十几次的发布变更很正常,而每一次变更会都会打破系统原本的“稳定运行态”,引入了新的变量,所以发布变更是研发最“高危”的动作之一。
除了研发模式导致的高频变更外,随着业务发展,系统也会逐渐复杂,链路越来越长、完成一个功能相关联的系统服务越来越多。系统复杂度的提高,增加了变更时所带来的不确定性,为了应对这样的情况,这几年,我们在团队内部实施了严格的发布 SOP,简称为“发布三板斧”:

在我看来,“发布三板斧”的落地与推进,就是可用性治理的第一步(也是最关键的一步)。我们来看一下,发布三板斧的实施具体要注意哪些关键点。
1. 变更需要监控
在 01 讲中我提到过,完善的监控告警比人工反馈响应更快,也会减少故障的持续时间进而降低影响。而没有监控的变更就像盲人摸象,甚至会出现“你负责的系统宕机了,都要用户电话告诉你”的尴尬局面。
在推进监控落地的过程中,你要和团队成员讲明监控的重要性,还要确保监控的完善与有效,而针对某个业务场景,有效的监控要回答三个问题:
是否有问题发生?
哪里发生了问题?
发生了什么问题?
这三者是递进关系,对监控的覆盖程度与范围要求越来越细致。一般情况下,我们监控的都是 API 这一层面,但是单纯的技术指标并不能完整回答上面三个问题,往往要结合业务场景去设计,才能够更加精细化地感知异常。
比如之前饿了么有商家开放平台的系统,用以对接类似星巴克一类的 KA 品牌自己的餐饮系统,开放平台的核心价值就是对外暴露统一的协议,屏蔽各个 KA 品牌内部系统的数据逻辑。假设 A 品牌的系统今天出现了问题,导致 A 品牌餐厅都无法接单,那么从饿了么所有商户的接单曲线上看,可能没有直观的感知,毕竟一个品牌的订单量在平台上可能无法凸显,单一品牌接单量会淹没在海量的数据中。而如果有一个“KA商户接单曲线”的监控,就能很容易看到异常了。

外卖接单曲线图
所以我们要结合业务配置有效的监控,而能否第一时间发现变更导致的异常、缩短异常带来的影响,就要看监控是否完善了。
如果说监控让我们更快地知道系统有没有问题,那灰度就是确保即使有问题也只在小范围产生影响,降低风险的作用范围。
2. 有效灰度必须有耐心
一些技术 Leader 认为“灰度就是在生产环境进行小范围测试”,就算嘴上不这么说,心里也这么想。但这个认知是绝对错误的,灰度从来不是为了测试,也不等于 A/B Test。它本身是为了对抗“未知的不确定性”。

我们之所以在编码完成后,会在测试环境进行测试验证,主要就是在找问题、找错误,而当我们走完一个完整的测试流程后就可以认为,已知的问题都已经解决了,又因为在测试环境,所以没有给线上真实的业务与用户造成影响。
而灰度就是假设“还存在我们不知道的问题”所以你才需要更加谨慎地进行灰度,确保即使问题真的在生产环境出现,造成的影响也是可控的。
在灰度的落地与推进过程中,要注意有效性,因为灰度这个动作很复杂、费时间,稍不注意就会“形式化”。比如一个系统部署在 2 个机房,每个机房 4个集群,正常的灰度顺序应该是单机房单集群中部分节点、单机房单集群中全部节点、单机房中全部集群,然后另外一个机房重复这个步骤。

要想实现灰度的有效性,关键点在于时间和流量。
时间:每个灰度阶段至少有 5 ~ 10 min 的观察,在监控、日志和各方反馈没有异常后再扩大灰度范围,确保一些运行时异常或量变积累质变的问题可以暴露出来。
流量:有时一些业务场景需要特定的触发条件,比如满足某些条件的用户或满足某些条件的订单,那么在灰度时就不能仅通过单位时间内有没有异常来判断,还要确保有足够的有效流量。
有效的灰度可以把问题影响锁定在一个小范围内,但是同样也降低了问题的“明显性”,所以你要通过监控和日志更加仔细、谨慎地去寻找、观测异常并对比发现问题。并且因为动作烦琐,用时也长,还要和开发同学沟通好“这样做有什么意义?”一类投入产出比的问题。
我建议你结合实际的系统情况与风险程度来确定灰度的程度,平衡好时间与效率,“好钢花在刀刃上”,因为以我的经验来看,这些投入的时间无论怎样都会大大少于你在事故复盘会上后悔的时间。
3. 回滚就是变更的“后悔药”
在 01 讲中,我提到过故障恢复最好的手段是各种预案,而回滚则是预案中最普遍、也最有效的。回滚这件事儿,你并不陌生,我重点想强调“何时回滚”以及“如何确保能回滚”。
我记得很清楚,2019 年有一天我刚下班回家,风控的研发负责人就打来电话说:“订单系统前两天的一个变更导致风控有部分订单拦截失败。”涉及风控就意味着资金会有损失,事儿不小,我立刻电话相关的同学,得知是前几天上线的一个功能导致的,因为与风控评估下来影响面不大,所以新功能没有下线,计划过两天修复这个Bug。我顿时有一种三花聚顶的错觉,整个人都不好了。
“已经产生了线上影响,并且可能有资损,怎么能过两天再修复?”
“发现问题第一时间回滚就能解决的事儿,为什么不回滚?”
没人能回答我的问题,而我当时的决定是系统立刻回滚,并第一时间处理系统回滚带来的业务影响,承诺第二天尽快修复后完成新功能的重新上线,同时按照事故进行申报。
这件事儿也让我意识到,在研发对事故的敬畏之心不足时,回滚也会失灵。所以我建议你,除非影响面非常小并且可控,或者涉及重要的商业合同,否则一般情况下应该有立刻回滚止损、业务恢复的意识,不要有侥幸心理,你要成为这种意识的宣讲者与践行者。
那么如何确保变更是可以回滚的呢? 要知道,系统并不是天然可以无缝回滚的,想要系统具备回滚的能力,在设计与实现阶段需要付出额外的精力。可回滚的本质是系统的兼容性设计与实现,比如常见的“只增不改”,一个 API 内要调整很多实现逻辑才能满足新业务的需求,此时不妨直接新增一个 API ,两个 API 保持参数一致,那么一旦新 API 有异常直接切换回旧的 API 即可。
所以,不论是灰度计划还是回滚策略都应该在架构设计阶段就去考虑,结合排期、风险程度、成本投入这些方面,要做好评估与平衡。
坚守 Design For Failure 的架构理念
“Design for failure and nothing will fail”,最早是 AWS 的一条最佳实践,即面向失败进行系统设计。你也可以理解为:考虑系统所有可能发生故障或不可用的情形,并假设这些可能都会发生,倒逼自己设计足够健壮的系统。
其实这个理念在分布式系统中很早就应用了,比如“非关键路径都要可以降级”“核心系统一定要有熔断、限流、超时这些保护手段”“架构上要避免单点”等,而今天我更多地想从正反两个角度来讲讲技术团队如何推行并落地这种理念。
正向:如何形成 Design For Failure 的系统设计习惯?
反向:如何确定系统真的可以 Failover?
1. 将经验教训沉淀下来
大部分技术 Leader 对于系统的参与都在架构设计这一环节,通过业务对焦、方案梳理,敲定系统架构、DB 和 API 的实现。这一过程中,你的重点不能只在功能的实现上,还要敏锐地去感知系统可能存在的风险隐患。
历史是最好的老师,我建议你总结并分析过去发生过的事故,并结合常规分布式系统的可用性风险,以此梳理出一个围绕事故隐患的风险点 Checklist,在需求迭代或者架构设计时,通过它高效地找到系统实现的薄弱环节。当然了,这个 Checklist 需要你结合系统演进不断地完善,以 DB 为例,最基本的你可以考虑下面这些风险点:

在不断梳理并实践这些风险点的过程中,我们又会形成一些问题的通用解决思路,和标准的设计原则。比如超出预期的主从延迟是分布式系统中很可能出现的情况,如果业务场景上主从延迟的容忍度很高,还不是关键路径,做好降级开关可能就足够了;如果是写完即读的场景,就要考虑是不是让读请求直接绑定主库,并且对主库是否造成较大的负载压力以及缓存是否能起到作用,或者改为消息推送的方式。
当然了,解决一个问题的方案很多,除了完善 Checklist,在团队普及这种设计理念之外,更关键的是将这些解决方案沉淀成设计原则,让研发人员可以在实际中落地。
2. 通过演练验证预案设计
因为 Design For Failure 的设计思想,我们日常在系统中做了很多预案的准备,同时也发现在真实事故发生时,很多设计并没有按照预期那样发挥作用。
在系统正常运行时,我们无法验证自己准备的灾备方案是否有用(比如,这些措施在故障发生时是否真的有效?处理流程与沟通协作是否通畅?),而一旦方案真的有问题,在真实故障发生时也为时已晚。
为此我们在 2016 年还参考了当时 Netflix 的 ChaosMonkey 设计并实现了自己的故障演练系统 Kennel,日常主动制造事故上下文来验证我们的设计与系统是否可靠。
通过这个例子,我想强调的是, 技术Leader 要化被动为主动,有意识地推进故障演练,不论是以注入还是回放的方式制造可控的故障,以此验证应急处理的机制流程和预先设计的灾备方案是否有效,通过持续的日常演练来提高故障发现和恢复的能力,以便在真实事故发生时应对得更加从容。
当然了,我想提醒你,演练是一个逐步发展的过程,不需要一步到位。我们也是从最开始测试环境检验,然后在生产环境进行有预案的演练(即制造一个故障并启动预案看其是否生效),直到最近一两年才开始进行真正的随机故障演练,即运维或稳定性的专项同学在不提前通知的情况下注入不确定的故障,验证对应的团队和系统是否能及时感知、操作并恢复。
以上就是关于 Design For Failure 在团队中推行落地的一些关键点和建议,同时可用性的治理与预防还要结合管理手段,技术 Leader 通过适当的管理手段把理念落地、把执行到位、把结果做好。
把稳定性当作机制与文化去建设
系统稳定性结果好坏很大程度上取决于技术 Leader 的重视程度,如果一个团队的管理者都不能身体力行的去重视它,而仅仅只是喊喊口号,那就不要指望团队成员能认真地对待这件事。
所以要把稳定性当作一个机制和团队的文化去建设,不断加深大家对稳定性的认识以及和每个人切身利益的关联程度,进一步形成团队的氛围与文化。管理的方法需要结合团队与自身的情况去落地,这里我分享几个之前的做法,希望给你一些启发。
1. 新人 Landing 从稳定性学习开始
团队中新入职的同学往往有 1~2 周的适应期,除了熟悉基本情况外,新同学必须学习并通过发布变更 SOP 考试后,才能取得对应系统的发布权限。除此之外,还要学习这个部门最近半年发生的真实事故,并写一篇总结邮件给部门内所有人。
其实这是利用了心理学中的“承诺一致性原则”,人们往往会重视自己公开承诺的事情,并形成对应的行为约束。新人的公开邮件不仅是通报自己的学习总结,某种程度上也是一种承诺“这些错误和教训我认识到了,我不会犯类似的错误”,进一步加强对事故的敬畏之心。
在新人入职 3 个月进行述职转正时,作为评委之一,我一定会对他试用期内稳定性的结果进行评定,也会针对稳定性相关的内容进行问答。
总之,团队的新成员一定要在一开始就对稳定性有足够的认识与敬畏,技术 Leader 要严控这个环节,从人的维度避免风险的主动流入。
2. 每人不低于 35% 的稳定性 KPI
重要且生死攸关的事儿,一定要在 KPI 中体现出来,避免出现口号响亮但是落地无声的情况。一般来讲,我会要求技术Leader的稳定性 KPI 占比在 35% 到 40%,一线研发的同学可能是 50% 以上。
虽然占比上技术 Leader 因为有其他职责可能未必有一线研发同学高,但是其评判标准会更严格,比如达标默认是 B,唯有超出预期才可能到 B+ 或者 A-。
这部分稳定性 KPI 除了会影响最终绩效,也会直接影响年终奖、调薪、晋升等各方面,比如上一年度如果存在发布 SOP 红线违规导致事故,则晋升资格取消。总之,通过稳定性 KPI 的设计,将稳定性的结果与所有人的切身利益实实在在地绑定到一起。
3. 好的坏的都要在阳光之下晒一晒
想要落实一件事儿,往往要奖惩结合,而榜样的力量是无穷的,你可以通过榜样来告诉团队小伙伴,公司需要什么样的人才、不能容忍什么样的行为。
我们之前每个月会做一次红黑榜单,以不同的维度公示部门内各团队的稳定性结果,统计维度可以是:事故数、冒烟数、1-5-10 达成率、本月严重事故……把做得好与不好的都拿出来给大家看看,那些结果好的我们可以学习什么,那些结果不好的我们要规避什么,通过这样的形式让我们共同看见、共同学习。
当然,管理的手段千变万化,你要清楚的是,奖惩不是目的而是手段,要选择合适的手段提高团队成员的稳定性意识,并且最终取得好的结果,我建议你根据今天的内容,结合自己团队的具体情况去设计,“拿来主义”也要有一个本地化的过程才能发挥好的作用。
小结
对于技术 Leader 而言,你不能用一次次的重大事故让团队成员慢慢理解系统稳定性的重要性。同样没发生火灾之前,大部分人都意识不到消防通道的重要性,唯有经历过才知道这些措施的珍贵。可用性的预防与治理需要投入大量的时间和精力,这一点上需要技术 Leader 做好投入产出的评估与平衡。
发布变更、架构设计的 “Design For Failure”以及机制与文化的建立是三个重要的驱动方向,技术 Leader 可以围绕这三个方面不断延伸,用运营与治理的视角去看待可用性的预防,希望本节内容对你有所启发和帮助。

留个作业: 相信你在系统中肯定也有很多design for failure的设计,回顾一下它们发挥作用的高光时刻并梳理一下当时是如何思考这些设计的,同时也尝试优化这些设计看是否还有更好的方案。
最后,感谢你的阅读,如果这节课让你有收获,欢迎你将它分享给其他的朋友,我们下一部分见。
今天咱们来聊一下资损事故的防控。
我想你一定很熟悉近几年“百亿补贴”“天降红包”“签到抽奖”这些电商、外卖、打车花样百出的营销活动和用户补贴。而且它们的营销逐渐从特有的大促场景中(比如双 11、618)常态化,红包、返现、抽奖、X 元套餐等活动玩法层出不穷,随之而来的还有各类资损事件,比如:
2020 年 1 月,京东被爆出可以领取 200 元无门槛小家电优惠券,微波炉、电烤箱等小家电甚至可以 0 元购,网传损失 7000W,研发团队整体被裁掉,虽然后来京东官方辟谣,但根据按订单发货和砍单补偿红包的处理措施来看,最终的资损肯定是个天文数字。

6 元的美的电烤箱
除了这个案例,还有商户配错价格、秒杀商品大量超卖等资损类事件,它们主要有这样几个共性的特点:
平台感知能力弱,技术指标不敏感,大部分是舆论爆发后人工反馈;
因为感知困难,往往持续时间长,最终资金损失大;
问题难以第一时间立刻恢复,并且止损后容易引起舆论关注和公关事件。
简单来讲就是感知难、修复慢、影响大。
而我观察后发现,大部分研发同学对资损类事故的了解相对缺乏,因为它比可用性事故更隐蔽,并且很多同学会将其简单归为“线上Bug”,忽略背后的资金损失,也没有量化具体的业务影响。所以,如果一个团队要想在资损上不栽跟头,能结合具体的业务场景在系统上做好防控建设,技术 Leader 的认识和引导就起到关键作用。
本文我会从“如何理解资损事故”和“如何预防、应对资损事故”两个角度出发,围绕红包这个常见的场景分享一下我对资损防控的理解(前文已经涵盖了管理部分,所以本节不会重点强调管理动作)。
建立资损概念的宏观认知
很多 Leader 有这样一个认知误区: 平台有资金外流或者因为平台的系统故障导致某一方客户有资金外流,这才是资损事故。这种只关注真实流失的资金,是狭义的。
从广义上来看,存在理论损失也应该算资损, 比如因为搜索推荐系统出问题(不论什么原因)导致这一阶段广告的收入减少,或者因系统 Bug 导致用户取消订单的申请被默认同意(虽然原本商户可能也会做同意处理,但是申诉的话平台依然要赔付),类似预计收益减少或者因系统问题产生赔付的场景都应算为资损。
我列举了常见的定义与分类,因为资损场景与业务是息息相关的,所以我又简单地举了几个例子,方便你理解。

资损定义与分类
在这里,我想提醒你注意资损分类的“已知资损”。 听起来有点儿怪,怎么还能允许已知资损存在呢?其实这种情况很常见,主要还是因为业务规则与用户体验之间的平衡,比如电商平台有跨店铺满减优惠券,如果你凑单后,退掉不需要的商品就可以跨过“本不满足的优惠券使用条件”。
虽然这种“钻空子”是业务规则允许的,但是也会存在风险,有可能形成超出预期的问题,比如家居这类大客单价的场景,如果忽略“已知资损风险”,发出了 3000-500 的优惠券,和 300-30 带来的风险肯定天上地下了。
讲到这儿,你可能感觉跟自己的关联不大,因为自己负责的不是交易或者营销这类与资金关联很强的系统。可我认为,只要你公司的业务存在资金流,那么体系中的任何一个系统都很难完全规避资损风险,只是概率高低而已。很多时候,你一松懈,也许问题就会发生。
2014 年,我刚加入公司参与开发的是内部运营系统,既没有大流量也没有高并发,订单、支付、红包这些只涉及查询,看着蛮“安全”,但当时在迭代一个菜单编辑功能的时候,因为我的疏忽导致一个前端 Bug:某种情况下菜品的价格传到后端是 0,后端接口也没有完整的校验,上线后第二天大量用户喜提 0 元午餐…… 直到现在,我印象依旧很深刻。
那说了这么多,资损防控的关键是什么呢?
资损防控的三个关键
资损事故感知难、修复慢、影响大,所以更应该把精力投入到预防上,这样才能从根本上解决问题。修复慢是因为缺少预案,只能临时 Fix,时间上紧迫还容易引入新的风险。而想要更早发现问题,就要有监控,这样才能在事发第一时间响应处理,缩短事故持续时间。
所以你要争取做到事前梳理出风险点做好预防、事发可以及时感知响应、事中可以快速止损恢复(简单来讲三个字:防、监、控)。
从阶段上来讲,资损防控的落地动作与可用性治理的有所类似,但是每一部分的关键点和可用性治理又不相同,这些是 Leader 要格外注意的。
1.防:资金视角做风险点识别
核心是引导团队将精力放在风险点的识别与修复上,从我过去经历过的资损事故来看,虽然大部分事故都和技术 Bug、产品逻辑设计、人为配置错误这三部分有关,但是具体的原因非常离散。比如围绕红包可能出现的资损风险点,从红包的生命周期切入,就有很多可能。
配置阶段: 金额、数量、使用条件、补贴结构等信息是否配置正确?
发放阶段: 红包发放人群是否有超出?
核销阶段: 优惠互斥关系是否满足(比如满减和新用户红包不可同享)?单笔订单可使用红包数量是否超出?
退回阶段: 订单取消后,红包是否退回?如果用在 B 订单上的红包是来自 A 订单的奖励,那么 A 订单取消后,B 订单使用的红包是否受影响?
你可以看到,抛开技术问题仅从业务逻辑的角度去假设,在红包的各个阶段都可能出现资损风险,并且问题非常多样。这一方面说明资损风险点与业务场景联系紧密,另一方面也说明穷举风险点有很强的不确定性,我们需要一些可迭代的“套路”去分析和发现问题。
如果说我们通常以 DB 设计、API 契约、架构图作为切入点,梳理可用性的风险点,关注的是流量请求、调用链路以及数据变化(这是一个信息流的视角)。那么与之对应的,在资损方面我们关注的是业务逻辑中隐含的资金逻辑,也就是资金流的视角。
因为不管是技术问题还是业务逻辑问题,资损风险最终的都会反映到资金层面上,而资金的变化是受业务逻辑影响的,二者结合就有了一个很好的切入点。 一般系统中资金的变化主要体现在 4个 方面,分别是资金流、资金账户、资金计算和资金凭据。
资金流:通过梳理资金的流向,来确认资金转移的链路是否有错误。比如用户领取了满 15-5 的红包,其中 3 元平台补贴,2 元商户补贴。用户下单购买了 15 元的商品,那么从红包补贴到用户支付再到商户收款,就形成了一个简单的资金流,这个流向可以形成很多个“等式”,通过梳理和验证这些等式的正确性检查资金流中是否存在错误,比如最简单的:
用户应付/实付(10)= 商品价格(15)- 红包优惠(5)
商户应收/实收(13)=商品价格(15)-商户补贴(2)=用户实付(10)+红包内平台补贴(3)
资金账户,通过流水和记录核对资金账户数据的正确性,比如用户账户、商户收款账户……同样也包含一些虚拟资产的核对,比如积分、优惠券……
资金计算:涉及资金计算的部分需要单独 Review,并通过数据对比进行正确性核算。比如优惠金额的计算、订单金额的计算、商户抽佣的计算……
资金凭据:资金在各系统扭转的过程中一定会落有大量的凭据,比如订单上会存有使用优惠的信息、对一笔订单进行支付理论上在支付系统会存在一笔支付单,这些凭据的数据准确性以及彼此关联的数据正确性都需要验证。
除了从这 4 个资金的角度梳理风险点之外,还要考虑到一些技术实现引入的问题,比如接口契约、计价单位等,这些技术实现所导致的数据不一致和不正确的问题都会引发资损事故,所以你要额外注意并且参考 02 讲提过的方案,不断完善 Checklist 和设计原则,整体的梳理思路可以参考下方的脑图:

资损风险点识别
2. 监:一致性与正确性双核对
虽然资损类事故有点儿“润物无声”的味道,但是无声胜有声。比如一定规模的电商平台,如果红包补贴金额有问题,哪怕一个红包平台只损失 0.01 元,但是在庞大体量和长时间的作用下,最终的损失也会是一个天文数字。
所以在 02 讲我提到过,除了技术指标外,更敏锐的是业务监控。然而它对于短时间资损严重的场景会有比较好的感知能力,但是对于某些细小的资损的场景则无能为力,这就需要我们建立更加敏感、完善的监控体系。
在我看来,针对资损感知的核心思想是:基于线上业务结果收拢进行监控,基于线下业务场景扩散进行核对。

基于线上结果收拢进行监控,是忽略“因”聚焦“果”的一种假设方法,通过观察结果的变化来反推因子是否发生变化。我们可以这样思考,假设系统存在资损漏洞或Bug,那么哪些关键的业务指标会发生变化?这样你会发现:
原本满 10-5 的红包,因红包模版Bug,实际生成的红包使用门槛为满 5-5;
原本仅能在星巴克使用的 15 元专享红包,因为配置错误,变成可以在任意门店使用的平台通用红包。
不同原因导致的问题,最终都会促使交易GMV、用户单均实付金额等宏观业务指标与日常的同比环比出现差异,不同原因的业务影响会逐渐往关键的业务指标上汇集。
因此可以结合这样的规律,通过组合观察关键业务指标,形成一个基本的资损监控大盘,这样的大盘对来势汹涌、单位时间内流血量大的资损问题是比较敏感的。
而基于线下业务扩散进行核对,则聚焦到我们在资损预防阶段梳理出的业务场景和业务链路。通过建立实时核对的机制确保每一个红包涉及的资金流动与计算都准确无误。这里实时核对的思想是一种 DoubleCheck 的做法, 比如我们认为虽然系统会将记录中的 A 数据变更为 B 数据,但是我怎么知道它实际操作中是否所有的变都正确呢?那么我就每次变更完都看一下,A 有没有变为 B,只要检验时发现没有变为 B 我就告警出来。
在实际业务中,首先根据不同的业务场景、逻辑规则和资金、资产的变化,梳理出一个个核对公式,这部分其实在风险点梳理时就应该有整理。然后通过系统实时判断等式左右两边本应该相等数据是否一致,一旦不一致则告警出来。举几个简单的例子。
积分发放: 积分系统发放 100 积分给用户 A = A 用户账户中新增 100 积分
库存扣减: 订单系统生成包含 2 件 A 商品的订单 = 商品系统中 A 商品库存减 2
下单返红包: 用户支付金额 * 返还比例 = 用户账户新增红包金额
其中,资产的转移发放是比较清晰简单的,涉及资金组成(比如订单金额计算)的公式就会更复杂一些,但是核对的原理是类似的,都是基于业务场景顺着资金或资产流向建立一个个核对公式,在每一次资金或资产变动时,都会通过核对公式左右是否一致判断是否有资损发生。
这里需要技术 Leader 注意, 与可用性监控围绕接口的技术指标不同,资损更关注的是数据核对,监控的并不是运行状态而是运行结果,并且资损监控的粒度要求非常高,精细到每一笔交易、每一次金额计算、每一个红包发放。所以资损监控的有效性很依赖于场景的覆盖率,仅覆盖几个关键场景是不足以规避资损风险的,除了要定期梳理外,每次系统有变更或者新功能时,都需检查是否有新的核对点,以及旧有核对公式是否需要调整。

资损监控核对
3. 控:资金拦截 + 资产控制
除了防和监,资损防控的关键主要在“控”字上,我们希望在问题发生后第一时间止损,这就需要技术在系统层面对资金和资产有很强的控制能力。这种能力的表现就是: 不仅可以通过预案将某些场景与链路降级,还可以拦截资金的流出和资产的使用,同时具备快速订正错误数据的能力。
在我们开始处理资损事故时,会有三个共性的需求。
问题止血不新增:核心是关闭问题产生源头,往往通过业务场景降级来实现,比如对错误红包或者满减活动进行下线。
控制资金流出:核心是对资金和资产进行拦截与冻结,避免外流后损失无法修正,比如禁止用户下单时勾选使用有问题的红包。
存量数据订正:核心是捞取问题数据后可以快速地批量处理,比如批量更改红包的金额、甚至直接将红包无效。
虽然其中一些操作对用户体验是有损的,但有而不用是一回事儿,无能为力则是另外一回事儿,其中:
资金拦截的能力主要从资金的流入和流出这两端进行把控。以红包而言就是管控其创建与核销。在红包创建时,有预算系统进行管控,避免无限制地生成红包进而超发。在红包核销时,由交易和营销系统进行验证,确保订单上下文以及红包合法,避免问题红包被核销进而造成无法挽回的资损。
资产管控的能力则是资产的快速锁定和数据订正展开,以红包而言,如果不同模版不同活动的红包都有一个统一的批次号,就可以通过这个标记快速捞取某一批有问题的数据。同时如果提前准备批量订正的脚本,或者有订正数据的平台,就可以快速修改红包金额、使用时间、使用门槛等关键信息,甚至批量无效所有问题红包。
你需要注意,这些能力的实现更多依赖于技术 Leader 在日常需求迭代和架构设计时,是否有意识引导团队加强这方面的建设。大部分的预案思路来源于过去已经发生的问题,或者对未来可能发生问题的假设,将预案常态化是你重点关注并推进落地的。
除了建设预案,还要有预案演练,以此保证预案的有效性。技术 Leader 更应该鼓励测试和开发的同学主动做攻防演练,寻找漏洞、验证止损方案、及时发现并修复问题。

资损预案止损
小结
资损事故很特殊,除非之前有过金融行业的从业经验或者在这方面吃过大亏,否则很容易忽视其在系统中的风险。今天本文,我强调这样几个关键点:
技术 Leader 应该加深对资损概念的理解,并引导团队加强这方面的认识;
围绕你负责的业务,构建风险梳理的“套路”,排查隐患;
重点围绕业务场景打造高覆盖度的监控核对,早发现、早治疗、少损失;
尊重墨菲定律,围绕可能发生的问题做好预案和演练。
在稳定性这个领域,不论是可用性还是资损,都是将系统中大量不确定性的风险识别出来,通过各种手段降低风险的过程。想要改变一件事,先要对其有足够的认识和理解,技术 Leader 在这个过程中应该起主导作用,看到别人看不到的,想到别人没想到的,引领团队往正确的方向走。

留个作业: 尝试从资金风险的角度去梳理一下你负责的系统,看看都存在哪些资损风险点,以及如何治理这些问题。
最后,感谢你的阅读,如果这节课让你有收获,欢迎你将它分享给其他的朋友,我们下一部分见。