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

全文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说

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

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

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

欢送各位同学关注