前言
iGraph是搜索事业部工程团队打造的实时在线图存储与查询的系统,提供大规模图数据的存储、查询、更新和计算服务,目前承载了集团多个部门5000+表的数据,双11期间proxy入口峰值流量1106w qps、实时数据峰值更新506w qps,是名副其实的在线数据航空母舰。
在过去2年多的时间里面,iGraph架构经历了3次大的演进,分别是:
(1) 调度之战:实现数据管理及服务运维的自动化,将在线进程推上了hippo,有效降低了业务迅速扩展带来的运维成本,为资源的统一调度及混布打下基础;
(2) 混部之战:升级7U OS,引入block cache替代了原先mmap加载数据方式,精细化内存管理,从而顺利将服务进程推入docker;
(3) 融合之战:将iGraph底层存储统一到indexlib索引内,同时还接入索引构建(build service)和调度(suez)框架。将iGraph在kv/kkv表存储,表管理,渐进切换等方面的积累融合进这个大统一架构的各个组件里,促进搜索在线架构的发展。同时,iGraph每天要完成7500+次索引构建,产出200+TB的索引数据,通过将索引构建统一到build service下,为通过集群混步提高机器资源利用率打下基础。
本文简单回顾了自动化调度之战与混部之战,重点介绍了经过融合之战以后的工程架构。希望通过介绍iGraph工程架构演进的一叶,让大家了解搜索工程团队同学重建引擎流水线,在用户无感知情况下演进、重组数据航母的流程。
调度之战
从2014到2015年间随着业务的迅速发展,表数目量级从百很快破千,在线单机房服务进程数目也要突破2千了。大家在疲于业务运维的同时,还要满足算法同学数据快速迭代的需求,允许表的24小时不间断回流。
在这种情况下,我们升级了iGraph服务框架,引入了admin进程作为二层调度器,在线资源统一从hippo申请,在线服务进程统一托管到“云”上(hippo)。新框架抽象出了role和zone的概念便于进程分组管理和升级,任何组件升级、配置/进程资源变更均可通过灰度的方式热替换、热修改,自动化异常发现/处理流程。还能够支持表的平滑扩缩列以及表级别的渐进切换,满足数据管理及快速回流的需求。
混部之战
在iGraph升级7U OS,进入docker的过程中发现latency经常出现抖动毛刺,原因是数据存储区域是通过mmap非lock方式加载的,依赖于操作系统PageCache的换入/换出机制,进入docker后,经常出现page cache没有及时换出导致申请内存的时候卡在reclaim上造成服务毛刺。
为了解决上述问题,我们引入了block cache层,精细控制内存使用。
其中kkv表的存储如上图所示,将各个数据区域切分成一个个的数据block,开链hash实现的dict区域(pkey区域)也是基于block的。Searcher上增加了一层block cache缓存,内部采用LRU淘汰机制管理,在不命中的情况下,使用direct io的方式读取磁盘,避免对系统page cache的影响。
iGraph根据
LSM tree
的思想进行索引分层存储,数据所在层次越小,数据越新,最上层的mem table会在内存占用到达阈值的情况下dump到磁盘成为sstable。为了减少mem table的内存占用,进而延迟mem table的dump时间, 减少索引的层数,降低访问延迟。我们将mem table区分成dict区域和value区域两部分,dict区域存储pkey的hash到数据地址的映射通常占用的内存比较小,value区域存储了用户数据。其中dict区域加载在内存中,value区域每256M进行一次整理压缩,dump到磁盘并重新用block的方式加载(不占用mem table的内存quota)。对大部分的实时表而言,1天内索引的层次数目(包括mem table)都不会超过2层,大大降低了查询访问层次。
另外,还采取了timestamp字段去重、消除冲突链next指针等优化,大大减少了内存使用。
融合之战
支撑集团搜索和推荐业务在线服务运转的有4大天王级引擎,分别是iGraph、DII、HA3、rank service。由于历史原因,早期四大引擎从存储到集群调度的实现各不相同。
拿存储来说,indexlib原先是HA3的专属倒排索引模块,其他引擎分别实现了自己的kv/kkv/倒排索引数据格式。当DII在倒排索引有更进一步的需求时,就望indexlib而兴叹了;当iGraph优化了kv表的性能时,尴尬的发现DII上类似的优化也要再做一遍。
集群调度方面也有各自的发展轨迹,HA3最早推出了基于admin的调度管理模式,DII和iGraph分别针对多表管理和基于磁盘存储数据的业务特点实现了自己的调度模块,rank service早期复用了DII的框架,再往后又推出了suez表管理框架。由于每个调度系统有自己的操作接口,当运维管控系统对接不同的引擎时,也要分别适配。而且不同的调度模块支持的功能也不一样,这也需要管控系统作出区分。
如果说四大引擎是支撑业务稳定、快速迭代的发动机,那么我们工程同学维护着组装、优化四大引擎的流水线。我们期望用1条流水线既能造出运转飞速的法拉利引擎也能造出功率强劲的航母引擎。具体来说,我们用组件化的方式统一了四大引擎从底层存储到网络通信再到数据管理调度的各个模块。这样底层组件增加了一个功能所有引擎都能享用,优化一个流程各个业务都能受益。这也要求各个基础组件能够允许不同引擎根据自己的业务特点灵活适配。
搜索工程团队在2016年推进完成了倒排索引、Trie树索引结构在DII Suggest业务上的重构落地。这个项目验证了统一底层索引结构、统一存储接口层设计,以及统一索引构建和调度带来的协同效应,在功能迭代、性能优化、运维便利性上面都带来了可观的收益。
基于DII项目的积累,自2016年末搜索工程团队开展了iGraph服务迁移indexlib&build service&suez的项目,进一步优化系统架构的层次关系。
总体框架
统一后,iGraph searcher新的工程框架如下。
数据存储统一到了indexlib里面,除了倒排/trie树以外,iGraph原先支持的kv/kkv表的实现也在其中落地。它即支持索引数据lock进内存的加载模式,也支持数据存放在磁盘同时配置block cache + value cache的访问模式。满足了各个引擎不同场景的数据存储、访问需求。
Kiwi成形于DII重构项目,成熟于iGraph融合indexlib项目。DII和iGraph都通过统一存储层接口(kiwi)进行底层索引数据的查询, kiwi通过提供统一的查询接口对业务方用户屏蔽掉indexlib底层的异构索引类型(kv/kkv/index/trie)。
MatchDoc和Expression分别是查询结果中间对象及表达式计算基础库,在此次迁移项目中,iGraph的在线doc及表达式计算也统一到了这两个基础库中。
Build service负责不同表数据的索引构建及实时文档处理。处理实时数据的时候它会将处理后的文档推送到Swift消息队列上,再由iGraph searcher拖取处理后的数据建立索引。
Suez从iGraph/Dii/早期rtp/ha3 在二层调度上的探索和沉淀抽象而来,设计之初就综合了多方的需求,承担统一在线运维框架的使命。Suez提供了多表的管理能力,例如:表的分发、加载、删除等基础操作;也具有按照app/biz等不同维度管理插件配置的功能。它对外提供了http的运维操作接口便于进程、表及业务插件的管理、更新。在项目中,它和iGraph的运维管理功能进行了有效的融合。
Autoumars沉淀了iGraph多年业务管理的经验,对接了hippo、build service、iGraph各机房集群,封装提供了数据管理、索引构建、在线数据切换、服务创建和监控、服务发布和升级、服务扩缩容等一系列能力。iGraph日常的运维操作以及iGraph用户对表生命周期的管理都在autoumars中进行。
iGraph proxy新的工程框架如下。
iGraph proxy依托于multi call进行和searcher间的通信,multi call封装了cm2 sub,具有服务发现的能力,它可以根据searcher节点的实时服务状况和服务能力,动态的在各个replica间导流,屏蔽异常节点。
iGraph proxy的数据计算中间对象及表达式计算也统一到了MathDoc及Expression lib中。
项目子模块介绍
下面介绍在组装各个子模块的过程中所做的改进和优化。(该部分内容由蒲坚、伯远合写。)
Indexlib && Kiwi
为了支持iGraph业务场景,indexlib底层索引中新加入了基于lsm思想的KV/KKV模型表,以及支持多表共享的结果cache和blockCache的应用模式。并且在iGraph传统的KV/KKV模型中增加了离线批次增量和在线索引实时build共存以及reopen覆盖加载的支持,既引入了传统模式,又保留了优势功能,取得了1+1>2的效果。
另外,在资源控制角度,针对实时索引数据我们支持了在线实时索引落盘和重启加载的功能,并且在索引落盘过程中引入了IO写操作限速的控制策略,进一步完善了在线资源使用的调控能力。
iGraph业务通过统一存储层接口(kiwi)进行底层索引数据的查询。kiwi存在的意义在于,一方面业务方用户可以屏蔽掉indexlib底层的异构索引类型(kv/kkv/index/trie),通过统一的查询接口和查询流程完成不同类型表的数据召回和跨表结果处理逻辑;另一方面,kiwi底层集成了表达式计算、过滤、通用插件集成能力和语法解析等框架通用能力,大大简化了业务开发维护的复杂度。
双十一大促需求中,我们在kiwi层面针对iGraph使用场景进行了session searcher单表维度的优化支持,在表数量较多的iGraph星座场景下取得了明显的优化效果(cpu开销降低10%+);同时在索引层面和Kiwi查询接口层,支持了全量KKV表的内部索引排序功能和结果有序信息透传,以节省业务层进行排序的准备和执行逻辑,在特定业务表也取得了不错的优化效果(latency降低10%+)。
BuildService && Swift
build service是一个离线&在线流式的数据处理系统,负责实时数据处理和索引生成、整理。
iGraph将业务相关的离线处理逻辑封装成了build service processor插件的模式,供建索引的时候使用。
在支持iGraph离线索引构建场景下,build service面临的主要问题是如何应对大规模的数据表构建场景——在离线全量、批次增量构建过程的效率优化、运维管理、资源调控、长尾实例自动解决等方面的问题。
Build service针对上述挑战,一方面从自身性能角度进行优化(内部决策线程拆分、小表merge多阶段合一等),另一方面也引入了swift内存topic优化、swift消息合并、build counter信息记录、慢节点/死节点检测、机器资源黑名单机制、异常信息收集和透传等新功能,最终支撑了iGraph线上业务的需要。
在双11大促当天,Build service协同iGraph autoumars管控平台,针对中美俄5100+张索引表,完成7689次索引构建,产出200+TB的索引数据。
Suez
iGraph在表管理的精细度、运维接口的丰富度、进程/表数据的规模性上都对suez提出了新的挑战。
比如:磁盘quota的控制,在分发索引的时候,如果达到磁盘水位能够及时停止索引数据分发;删除数据的时候也要求更加及时以便腾出磁盘空间。
为了方便表的实时控制需要暂停实时、实时时间戳前置等多维度表管理功能。
为了加快索引切换,期望能够有按列切换的方式(区别于按行切换)。
甚至表的amon metrcs组织方式上也因为表的数量巨大,为了展现上的清晰以及减轻amon的压力,要重新设计考虑。
另外,iGraph区别于其他引擎的一个特点是,不管是索引切换还是服务单元都是表维度的,也就是说一张表切换的时候,其他同进程的表是正常提供在线服务的。而且表切换的时候为了填充cache,要求unload老数据前流量要一点点的下,加载新数据后表的访问流量要一点点上来。同时在进程binary升级场景停进程的时候要求进程上所有表的流量也要渐进式的降下来。作为通用的表管理框架,为了满足不同场景需求,suez重构了表管理流程,从表加载到注册服务到表卸载再到进程退出各个环节上提供了拓展接口,便于不同应用定制。
除了干净利落的解决上述问题,双11前suez提供了老的iGraph框架不具备的动态调整实时表内存阈值大小的能力,便于快速干预大更新表内存满的问题。
同时为了应对iGraph规模大带来的问题,suez还进行了meta信息压缩、meta存储异步化、在线信息压缩读取等一系列的优化。
MultiCall
multi call是一个拥有负载均衡和异常节点屏蔽等流控策略,支持多种网络协议、适用于多种应用场景的服务调用库。multi call通过igraph searcher发布到CM2上的表到进程实例的映射来实现表定位服务。在和iGraph对接的过程中,multi call提供了对tcp网络协议的支持以及相关的异步访问接口。
同时,为了满足iGraph迁移表以及为了抗住流量压力把一张表部署在多个星座的场景,iGraph searcher和multi call间通过扩展服务挂载协议可以支持把同一张表的流量导入到多个星座;为了表切换的时候实现流量的渐进引入,multi call将iGraph searcher返回结果中表示表服务能力的weight字段同导流策略打通。另外,iGraph也是Multi Call根据latency导流新策略的最早使用者。
iGraph此次重构版本的上线刚好赶上搜索在线、离线混步的浪潮,在个别searcher进程因为其他进程影响出现latency抖动的情况下,multi call能够迅速将流量导到其他replica,有效提高了异常场景下的服务质量。
Autoumars
Autoumars项目和iGraph重构项目在同一时间起步,正是因为autoumars带来的运维及表管理上的便捷性,才使得iGraph新版本得以顺利灰度、上线。Autoumars提供了iGraph具体业务相关的功能,比如数据生命周期管理、星座管理、健康分管理、服务更新管理、团队管理、集群自动化巡检、数据部署智能调度等功能。相信Autoumars后续会为搜索的服务产品化带来更强劲的动力。
总结
iGraph在重构各个环节流程并对接上述子模块的同时,也将自己在在线图查询领域多年的积累沉淀到了各个子模块,如: kv/kkv表的实现方式以及cache方式沉淀到了indexlib;在线运维以及在线表管理上的理念沉淀到了suez,并跟新框架做了有效融合;业务及表生命周期管理上的经验沉淀到了autoumars;iGraph在数据规模上的庞大也进一步淬炼了build service和swift。
在重构完iGraph刚做性能调优的时候,我们就深切体会了新架构带来乘法效应:由于iGraph新版本基于match doc及expression lib,优化了matchdoc的反序列化,iGraph和DII都能受益;kkv表指定pkey + skey进cache带来的好处,基于indexlib的多个引擎都能享用;build service原生支持索引按指定字段排序,刚好可以拿来优化iGraph在线查询逻辑;autoumars在管理iGraph业务上积累的经验,在后续统一DII管控的时候将发挥更大的价值…
除了上面提到的工程优化的乘法效应,新架构给iGraph带来了新的能力,比如倒排索引的能力会在后续iGraph对gremlin图查询语言的实现上提供有效的支持;将数据存储沉淀到indexlib层,为将要展开的计算、存储分离工作铺开了道路;build service承担了四大引擎所有的索引构建工作,通过在线离线混步,提升了机器资源利用率。
“
雄关漫道真如铁,而今迈步从头越“。
感谢在indexlib、build service、kiwi、swift、suez、multi call、autoumars、iGraph等各条战线奋斗过的小伙伴们,正式由于大家的众志成城,才能漂亮的完成这样一场场艰苦的战斗。