关于后端:百度搜索稳定性问题分析的故事上

41次阅读

共计 7891 个字符,预计需要花费 20 分钟才能阅读完成。

导读 :百度搜寻零碎是百度历史最悠久、规模最大并且对其的应用曾经植根在大家日常生活中的零碎。坊间有一种乏味的做法:很多人通过关上百度搜寻来验证本人的网络是不是通顺的。这种做法阐明百度搜寻零碎在大家心目中是“稳固”的代表,且事实确是如此。百度搜寻零碎为什么具备如此高的可用性?背地应用了哪些技术?以往的技术文章鲜有介绍。本文立足于大家所相熟的百度搜寻零碎自身,为大家介绍其可用性治理中对于“稳定性问题剖析”方面应用的精密技术,以历史为线索,介绍稳定性问题剖析过程中的困厄之境、破局之道、翻新之法。心愿给读者带来一些启发,更心愿能引起志同道合者的共鸣和探讨。

全文 7741 字,预计浏览工夫 17 分钟。

第 1 章 窘境

在大规模微服务零碎下,如果故障未产生,应该归功于运气好。然而永远不要指望故障不产生,必须把产生故障当作常态。从故障产生到解除过程遵循的基本模式形象如下。

可用性治理次要从这 3 个角度着手晋升:1. 增强零碎韧性;2. 欠缺止损伎俩,晋升止损有效性,减速止损效率;3. 减速起因定位和解除效率。

以上 3 点,每个都是一项专题,限于篇幅,本文仅从【3】开展。

百度搜寻零碎的故障起因定位和解除,是一件相当艰难的事件,也可能是全公司最具备挑战性的一件事件。艰难体现在以下几个方面。

极其简单的零碎 VS. 极其严格的可用性要求

百度搜寻零碎分为在线和离线两局部。离线零碎每天从整个互联网抓取资源,建设索引库,造成倒排、正排和摘要三种重要的数据。而后,在线零碎基于这些数据,接管用户的 query,并以极快的速度为用户找到他想要的内容。如下图所示。

百度搜寻零碎是极其宏大的。让咱们通过几个数字直观感受一下它的规模:

百度搜寻零碎的资源占用量折合成数十万台机器,零碎散布在天南海北的 N 大地区,搜寻微服务零碎蕴含了数百种服务,蕴含的数据量达到数十 PB 级别,天级变更次数达到数十万量级,日常的故障品种达到数百种,搜寻零碎有数百人参加研发,零碎每天面临数十亿级的用户搜寻申请。

尽管零碎是超大规模,然而百度对可用性的要求是极其严格的。百度搜寻零碎的可用性是在 5 个 9 以上的。这是什么概念呢?如果用可提供服务的工夫来掂量,在 5 个 9 的可用性下,零碎一年不可用工夫只有 5 分钟多,而在 6 个 9 的可用性下,一年不可用的工夫只有半分钟左右。所以,能够说百度搜寻是不停服的。

一个 query 达到百度搜寻零碎,要经验上万个节点的解决。下图展现了一个 query 经验的全副节点的一小部分,大略占其经验节点选集的几千分之一。在这种简单的门路下,所有节点都失常的概率是极其小的,异样是常态。

简单的零碎,意味着故障现场的数据收集和剖析是一项盛大的工程。

多样的稳定性问题品种

百度搜寻零碎向来奉行“全”、“新”、“快”、“准”、“稳”五字诀。日常中的故障次要体现在“快”和“稳”方面,大体可归为三类:

  1. PV 损失故障:未按时、正确向用户返回 query 后果,是最重大的故障。
  2. 搜寻成果故障:预期网页未在搜寻后果中展示;或未排序在搜寻后果的正当地位;搜寻后果页面响应速度变慢。
  3. 容量故障:因内部或外部等各种起因,无奈保证系统高可用须要的冗余度,甚至容量水位超过临界点造成解体宕机等状况,未及时预估、告警、修复。

这些品种繁多、畛域各异的问题背地,不变的是对数据采集加工的需要和人工剖析教训的自动化形象。

第 2 章 引进来、本土化:破局


在 2014 年以前,故障起因定位和解除都在和数据较劲,过后所能用到的数据,次要有两种。一是搜寻服务在线日志(logging);二是一些散布零散的监控(metrics)。这两类数据,一方面不够翔实,利用效率低,问题追究有死角;另一方面,对它们的应用强依赖于人工,自动化水平低。以一个例子阐明。

回绝问题的剖析首先通过中控机上部署的脚本定时扫描线上服务抓取单 PV 各模块日志,展示到一个回绝剖析平台(这个平台在过后曾经算是比拟弱小的回绝起因剖析工具了)页面,如下图所示;而后人工浏览抓取到的日志原文进行剖析。这个过程尽管具备肯定的自动化能力,然而 PV 收集量较小,数据量有余,很多回绝的起因无奈精确定位;数据平铺展现须要依赖有教训的同学浏览,剖析效率极其低下。

在问题追究死角和问题追究效率上,前者显得更为迫切。无死角的问题追究呐喊着更多的可观测数据被收集到。如果在非生产环境,获取这些数据是轻而易举的,尽管会有 query 速度上的损失,然而在非生产环境都能容忍,然而,这个速度损失的代价,在生产环境中是接受不起的。在实践基石《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》的领导下,咱们建设了 kepler1.0 零碎,它基于 query 抽样,产出调用链和局部 annotation(query 处理过程中的非调用链的 KV 数据)。同时,基于业界开源的 prometheus 计划,咱们欠缺本人的 metrics 零碎。它们上线后立刻产生了微小的利用价值,关上了搜寻零碎可观测性建设和利用的设想空间。

2.1 kepler1.0 简介

零碎架构如下图所示。

阶段性使命:kepler1.0 在于欠缺搜寻零碎的可观测性,基于开源成熟计划联合公司内组件实现从 0 到 1 的建设,疾速实现可观测性能力空白的补齐,具备依据 queryID 查问 query 处理过程的调用链以及路径服务实例日志的能力。

引进来:从 kepler1.0 的架构不难发现,它从数据通路、存储架构等方面残缺的参考 zipkin

本土化:引进 zipkin 时数据采集 sdk 只反对 c ++,为了满足对非 c ++ 模块的可观测性需求,兼顾 sdk 的多语言保护老本以及 trace 的侵入性,采纳了常驻过程通过日志采集输入格局和 c ++ sdk 兼容的 trace 数据,即图中的日志采集模块。

2.2 通用 metrics 采集计划初步摸索

零碎架构如下图所示。

阶段性使命:2015 年前后搜寻开始摸索大规模在线服务集群容器化混部技术,此时公司内的监控系统对多维度指标汇聚反对较弱,基于机器维度指标的传统容量治理形式曾经难以满足容器化混部场景的需要。

引进来:将开源界成熟的 metrics 计划引入搜寻在线服务混部集群,实现了合乎 prometheus 协定的容器指标 exporter,并依靠 prometheus 的灵便多维度指标查问接口以及 grafana 丰盛的可视化能力,建设了搜寻在线业务混部集群容量治理依赖的底层数据系统。

本土化:容器指标 prometheus-exporter 和搜寻在线 PaaS 零碎深度对接,将服务元信息输入为 prometheus 的 label,实现了容器元信息的指标索引和汇聚能力,满足容器化混部场景下容量治理的需要。指标和 PaaS 元信息关联是云原生 metrics 零碎的初步摸索次要成绩。

2.3 利用成果初显

场景 1:回绝、成果问题

阶段性痛点:人工剖析强依赖日志,从海量调用链、日志数据中准确检索出某些特定 query,通过 ssh 扫线上机器日志效率很低,且对线上服务存在 home 盘 io 打满导致稳定性危险。

解决状况:对命中常态随机抽样回绝问题、可复现的成果问题开启强制抽样采集,通过 queryID 间接从平台查问调用链及日志用于人工剖析起因,根本满足了这个阶段的 trace 需要。

场景 2:速度问题

阶段性痛点:仅有日志数据,不足调用链的精密工夫戳;一个 query 激发的调用链长、扇出度大,日志散落宽泛,难收集。通过日志简直无奈复原残缺的时序过程。这导致速度的优化出现黑盒状态。

解决状况:补全了调用链的精密工夫戳,使 query 的残缺时序复原成为可能。通过调用链能够查找到程序层面耗时长尾阶段或调度层面热点实例等优化点,基于此,孵化并落地了 tcp connect 异步化、业务回调阻塞操作解除等改良我的项目。

场景 3:容量问题

阶段性痛点:多维度指标信息有余(短少容器指标、指标和 PaaS 零碎脱节);短少无效的汇聚、加工、组合、比照、开掘以及可视化伎俩。

解决状况:建设了搜寻在线的容器层面多维度指标数据采集零碎,为容器化的容量治理利用提供了重要的根底输入起源,迈出了指标零碎云原生化摸索的一步。下图为我的项目上线后通过容器指标进行耗费审计性能的截图。

第 3 章 翻新:利用价值的开释


尽管 kepler1.0 和 prometheus 关上了可观测性建设的大门,然而受限于能力,曾经难以低成本地获取更多的应用价值了。

3.1 源能源

基于开源计划的实现在资源老本、采集提早、数据覆盖面等方面无奈满足搜寻服务和流量规模,这影响了稳定性问题解决的彻底性,特地是在搜寻成果问题层面体现尤为重大,诸如无奈稳固复现搜寻后果异样问题、要害后果在索引库层面未预期召回问题等。

稳定性问题是否失去解决永远是可观测性建设的出发点和落脚点,毫不妥协的数据建设始终是重中之重。从 2016 年起,搜寻开始引领可观测性的翻新并将它们做到了极致,使各类问题得以切实解决。

3.2 全量采集

因为搜寻零碎规模太宏大,所以 kepler1.0 只能反对最高 10% 的采样率,在理论应用中,资源老本和问题解决彻底性之间存在矛盾。

(1)搜寻零碎大部分故障都是 query 粒度的。很多 case 无奈稳固复现,但又须要剖析出历史上某个特定 query 的搜寻后果异样的起因。让人无奈的是,过后只有备份下来的日志能力满足任一历史 query 的数据回溯需要,但它面临收集老本高的难题;另外,很多 query 没有命中 kepler1.0 的抽样,其具体的 tracing 数据并未有被激发进去,剖析无从下手。能看到任一历史特定 query 的 tracing 和 logging 信息是简直所有同学的欲望。

(2)公司外部存储服务性价比较低、可维护性不高,通过扩充采样率对上述问题进行笼罩须要的资源老本微小,理论中无奈满足。

对于这个矛盾,业界过后并没有很好的解决方案。于是,咱们通过技术创新实现了 kepler2.0 零碎。零碎从实现上将 tracing 和 logging 两种数据解耦,通过繁多职责设计实现了针对每种数据特点极致优化,以极低的资源开销和极少的耗时增长为老本,换取了全量 query 的 tracing 和 logging 能力,天级别数十 PB 的日志和数十万亿量级的调用链可实现秒查。让大多数故障追究面临的问题迎刃而解。

3.2.1 全量日志索引

首先,咱们介绍全量日志索引,对应于上图中日志索引模块。

搜寻服务的日志都会在线上机器备份相当长一段时间,以往的解决方案都着眼于将日志原文输入到旁路零碎,然而,疏忽了在线集群人造就是一个日志原文的现成的零老本存储场合。于是,咱们翻新的提出了一套计划,外围设计理念概括成一句话:原地建索引。

北斗中通过一个四元组定义一条日志的索引,咱们叫做 location,它由 4 个字段组成:ip(日志所在机器)+inode(日志所在文件)+offset(日志所在偏移量)+length(日志长度)。这四个字段共计 20 字节,且只和日志条数无关,和日志长度无关,由此实现对海量日志的低成本索引。location 由 log-indexer 模块(部署在搜寻在线服务机器上)采集后对原始日志建设索引,索引保留在日志所在容器的磁盘。

北斗本地存储的日志索引逻辑格局如下图所示。

查问时,将 inode、offset、length 发送给索引 ip 所在的机器(即原始日志所在机器),通过机器上日志读取模块,可依据 inode、offset、length 以 O(1) 的工夫复杂度定点查问返回日志原文,防止了对文件的 scan 过程,缩小了不必要的 cpu 和 io 耗费,减小了日志查问对生产环境服务稳定性的影响。

同时,除了反对 location 索引以外,咱们还反对了灵便索引,例如将检索词、用户标识等有业务含意的字段为二级索引,不便问题追究时拿不到 queryID 的场景,可反对依据其余灵便索引中的信息进行查问;在索引的应用形式上,除了用于日志查问以外,咱们还通过索引推送形式构建了流式解决架构,用于反对对日志流式剖析的利用需要。

这里还有一个问题:查问某一 query 的日志时,是不是依然须要向所有实例播送查问申请?答案是:不会。咱们对查问过程做了优化,办法是:通过下文介绍的 callgraph 全量调用链辅助,来确定 query 的日志位于哪些实例上,实现定点发送,防止播送。

3.2.2 全量调用链

在 dapper 论文提供的计划中,同时存在调用链和 annotation 两种类型的数据。通过从新扫视,咱们发现,annotation 的实质是 logging,能够通过 logging 来表白;而调用链既能够满足剖析问题的须要,又因为它具备参差统一的数据格式而极易创立和压缩,达到资源的高性价比利用。所以,callgraph 零碎(kepler2.0 架构图中红色局部)就带着数据最简、最纯净的特点应运而生。全量调用链的外围使命在于将搜寻全副 query 的调用链数据在正当的资源开销下存储下来并高效查问。

在 tracing 的数据逻辑模型中,调用链的外围元素为 span,一个 span 由 4 局部组成:父节点 span\_id、本节点 span\_id、本节点拜访的子节点 ip&port、开始 & 完结工夫戳。

全量调用链外围技术创新点在于两点:(1) 自研 span\_id 推导式生成算法,(2) 联合数据特色定制压缩算法。相比 kepler1.0,在存储开销上实现了 60% 的优化。上面别离介绍这两种技术。

3.2.2.1 span\_id 推导式生成算法

阐明:下图中共有两个 0 和 1 两个 span,每个 span 由 client 端和 server 端两局部形成,每个方框为向 trace 零碎的存储中实在写入的数据。

左图:kepler1.0 随机数算法。为了使得一个 span 的 client 和 server 能拼接起来并且还原出多个 span 之间的父子关系,所有 span 的 server 端必须保留 parent\_span\_id。因而两个 span 理论须要向存储中写入 4 条数据。

右图:kepler2.0 推导式算法,span\_id 自根节点从 0 开始,每调用一次上游就累加该上游实例的 ip 作为其 span\_id 并将其传给上游,上游实例递归在此 span\_id 上持续累加,这样能够保障一个 query 所有调用的 span\_id 是唯一性。实例只须要保留本人的 span\_id 和上游的 ip,即可依据算法还原出一个 span 的 client 端和 server 端。由此可见,只须要写入 2 条数据且数据中不须要保留 parent\_span\_id,因而存储空间失去了节俭,从而实现了全量调用链的采集能力。

右图中 ip1:port1 对 ip2:port 的调用链模仿了对同一个实例 ip2:port2 拜访屡次的场景,该场景在搜寻业务中宽泛存在(例如:一个 query 在交融层服务会申请同一个排序服务实例两次;调度层面上游申请上游异样重试到同一个实例等),推导式算法均能够保障生成 span\_id 在 query 内的唯一性,从而保障了调用链数据的完整性。

3.2.2.2 数据压缩

联合数据特色综合采纳多种压缩算法。

(1) 业务层面:联合业务数据特色进行了定制化压缩,而非采纳通用算法无脑压缩。

(a) timestamp:应用绝对于 base 的差值和 pfordelta 算法。对扇出型服务多子节点工夫戳进行了压缩,只需保留第一个开始工夫戳以及绝对该工夫戳的偏移。以搜寻在线服务常见高扇出、短时延场景为例,存储偏移比间接存储两个工夫戳节俭 70%。

(b) ip:搜寻内网服务 ip 均为 10.0.0.0/24 网段,故只保留 ip 的后 3 字节,省去第 1 字节的 10,每个 ip 节俭 25%。

(2) protobuf 层面:业务层面的数据最终长久化存储时采纳了 protobuf,灵活运用 protobuf 的序列化个性节俭存储。

(a) varint:变长代替原来定长 64 位对所有的整数进行压缩保留,对于 ip、port、工夫戳偏移这种有余 64 位的数据实现了无存储节约。

(b) packed repeated:ip 和 timestamp 均为 repeated 类型,只须要保留一次 field number。packed 默认是不开启的,导致每个 repeated 字段都保留一次 field number,造成了极大节约。以均匀扇出比为 40 的扇出链路为例,开启 packed 可节俭了 25% 的存储空间(40 字节的 field number)。

最终,一个 span 的逻辑格局(上图)和物理格局(下图)如下:

3.2.3 利用场景的受害

3.2.3.1 时光穿梭:历史上任一特定 query 的要害后果在索引库层面未预期召回问题

因为召回层索引库是搜寻最大规模的服务集群,kepler1.0 在索引库服务上只反对 0.1% 抽样率,使得因为索引库的某个库种和分片故障导致的成果问题追究顾此失彼。全量调用链采集较好的解决了这一窘境。

实在案例:PC 搜寻 query= 杭州 未展示百度百科后果,首先通过工具查问到该后果的 url 所在数据库 A 的 9 号分片,进一步通过全量调用链调用链查看该 query 对数据库 A 所有申请中失落了 9 号分片(该分片因重试后仍超时被调度策略抛弃),进一步定位该分片所有正本均无奈提供服务导致失败,修复服务后预期后果失常召回。

3.2.3.2 链式剖析:有状态服务导致“误中副车”型成果问题

有状态服务成果问题剖析复杂性:以最常见的 cache 服务为例。如果没有 cache 只需通过成果异样的 queryID 通过调用链和日志即可定位异样起因。但显然搜寻在线零碎不可能没有 cache,且通常 cache 数据会辅以异步更新机制,此时对于命中了脏 cache 的 query 只是“受害者”,它的调用链和日志无奈用于问题最终定位,须要时序上前一个写 cache 的 query 的调用链和日志进行剖析,咱们称其为“捣鬼者”。

kepler1.0 的局限性:kepler1.0 采样算法是随机比例抽样,“捣鬼者”和“受害者”两个 query 是否命中抽样是独立事件,因为“捣鬼者”在先,当“受害者”在受到成果影响时,已无奈倒流工夫触发前者抽样了,导致两个 query 在“时序”维度够成的 trace 链条中断,追究也随之陷入了窘境。

kepler2.0 的破解之法:在实现“纵向关联”(某一 query 处理过程中全量调用链和日志信息)根底上,借助全量调用链建设了“横向关联”能力,反对了对时序上多个关联 query 的链式追踪需要。写 cache 时将以后 query 的 TraceId 记录到 cache 后果中,读 cache 的 query 就可通过 cache 后果中的 queryID 找到“捣鬼者”。借助全量调用链性能即可对“捣鬼者”写脏 cache 的起因进行剖析定位。另外,用户界面也对时序追踪的易用性进行了非凡设计,例如,对日志中写 cache 的 queryID 进行飘红,点击该字段能够间接跳转到对应 query 的调用链和日志查问页面。

小结

以上,极致的数据建设解决了问题追究的死角,此时问题剖析效率成为主要矛盾,下篇咱们为大家带来百度搜寻如何通过对人工剖析教训进行形象,实现自动化、智能化的故障问题,从而保障百度搜寻稳定性。未完待续,敬请期待……

本期作者 | ZhenZhen;LiDuo;XuZhiMing

招聘信息

关注同名公众号百度 Geek 说,输出内推即可退出搜寻架构部,咱们期待你的退出!

举荐浏览

| 百度对于微前端架构 EMP 的摸索:落地生产可用的微前端架构

| 社群编码辨认黑灰产攻打实际

|PornNet:色情视频内容辨认网络

———- END ———-

百度 Geek 说

百度官网技术公众号上线啦!

技术干货 · 行业资讯 · 线上沙龙 · 行业大会

招聘信息 · 内推信息 · 技术书籍 · 百度周边

欢送各位同学关注

正文完
 0