导读:过去四年,快手数据量增长了几十倍,集群规模也增长了几十倍,集群主要面临两个问题,一个是扩展性的问题,另一个是存储成本的问题。今天将为大家介绍快手HDFS的技术演进和思考,以及未来规划。
01
技术演进和思考
1. 快手HDFS简介
快手的数据存储在过去四年有数十倍的增量。现在总存储在EB级别,数万个节点,日写入百PB级。
快手HDFS支持了丰富的业务形态,包括传统的数据仓库,也包括音视频、图片的存储以及AI训练、机器学习的存储,覆盖了离线、近线、实时全场景。下图中的BlobStore是基于HDFS的对象存储,支撑了数万亿个对象。
一个大型的分布式存储系统,技术能力可以分为四个维度:可用性、可靠性、成本和性能,我们在过去几年的能力演进也是围绕这四个维度展开的。
在扩展性方面,除了跨region集群外,我们还建设了分级保障、降级和限流的机制。
在成本方面,我们输出了降低存储成本的能力,提供存储用量的数据洞察、治理建议以及配额机制。
在性能方面,我们针对社区namenode和datanode都完成了细粒度锁的拆分,大幅提升了元数据和数据通路的性能;对于超大规模集群下不可避免出现的慢节点、慢盘问题提供了一套规避和熔断的机制,降低长尾读写对于业务的影响。在可靠性方面,除了已有的传输时端到端校验和静默错误处理机制,我们还加强了datanode索引以解决单机文件系统的可靠性问题并引入了多种离线校验,可以发现各类数据持久性和正确性隐患。
2. 扩展性优化
① 元数据通路扩展性
存储规模在快速上涨,IO path可以通过添加datanode进行容量和性能的扩展,但namenode由于实现中存在一把大的读写锁,极易成为瓶颈。
首先看原生架构和问题,首先namenode有一把大的读写锁,在写的QPM到20w~30w时,就会到达瓶颈,且namespace内只有一个active节点可提供服务,导致单个namespace的服务容量较低;第二点,由于所有的元数据都放在内存中,单namespace的元数据承载量较低;第三点,客户端维护目录子树到namespace的路由,修改成本极高。
为了解决这些问题,分为两个阶段做优化:第一是在服务容量无法快速提升的情况下,先提供分级保障能力,保障核心作业的响应时间;第二是解决元数据通路的扩展性问题。
首先是分级保障。分级保障的目标是,在离线集群复杂的负载环境下,保障高优作业的rpc响应时间,为基线作业的按时产出提供保障。首先客户端需要将离线生产链路的优先级定义带到namenode,决定将rpc请求放到哪个队列,rpc的fetcher会根据权重向高优作业的请求做倾斜处理,来保障高优作业的rpc响应时间
第二个是引入并深入改造了Router Based Federation架构,使集群真正具备灵活的横向扩展能力。RBF架构是在客户端和namenode之间再增加一个接入层,将之前维护在客户端的路由信息收敛到Router维护,路由表的变更只需要在router操作,大大提高了可运维性。
RBF的实现也存在一些问题。Router采用同步调用模型,需要防止少量慢namespace耗尽router的线程资源。为此增加了thread permit机制,某个namespace需要获取thread permit后方可转发rpc;此外还设置了旁路队列,使得发向暂时慢或者满载namespace的请求,不至于由于快速达到重试次数而失败。我们也在设计和开发异步调用模型,去彻底解决这个问题。
第三是支持StandbyRead。通过引入RBF架构,可以比较好地解决突发的写流量将namespace打满的问题, 但很多热点数据,例如数仓的热点表,其瓶颈在读请求, 所以希望在namespace支持读写分离,并通过StandbyRead支持读服务容量的横向扩展。
不同于社区版是放在客户端里实现的,我们是把StandbyRead这套机制放在Router里面实现,主要是放在客户端实现,涉及到多语言客户端,维护的成本较高,推广的进度也不太可控,而放在Router内便于做控制面的变更;此外,读请求在单个namespace内也可做流量调度;最后,我们也让Standby节点能够提供读服务,尽可能充分利用主节点的处理资源。
下图中,在开启standbyRead后,热点数据所在namespace读取QPM达到近1000万/min,并可以持续横向扩展。
有了RBF架构和Standby读后,我们有了一定的横向扩展能力,但由于离线负载本身抖动会比较大,横向扩展都是后置的,对业务已经有损,此外频繁由于临时性的需求扩展主节点,也可能导致主节点的利用率较低,因此必须提升单个namenode的处理能力,为此我们将大的读写锁拆分为细粒度的读写锁。
Namenode有三大功能——名字空间管理、文件layout和块的管理、集群状态管理和副本一致性保障。Namenode主要的三条操作流:来自客户端的请求,操作对象为文件或目录;来自datanode请求,操作对象为datanode及datanode上的block;namenode内部的状态维护线程,操作对象为block。
我们拆细锁在实际落地的过程中,分为了三个步骤。第一步是把namenode这把大锁拆成namespace大锁和block manager大锁;第二步是把namespace大锁拆为inode细琐;第三步将block manager大锁拆成了datanode的细琐,相比第二步,很多逻辑不是那么直线,因此稍微难了一点。
拆锁的收益比较大。在读写量非常大的 yarn shuffle service 所在namespace, 写吞吐可以达到之前的5.7倍,读吞吐达到之前的16.6倍。
除此之外,我们还在进行一些优化,例如优化standby节点的回放速度,以减少回放editlog时的lag;改进standbyRead机制,大部分读取请求只需要看到局部最新的更新txid,而不需要看到全局的,可减少不必要的msync,降低active负载和standbyRead的延迟。此外,namenode的元数据还是都保存在内存中,这影响了元数据的承载量,我们使用了rocksdb来将目录树元信息offload到disk上,这也是我们正在优化的方面。
② 跨Region集群构建
跨Region集群构建背景有两个,第一就是某个地域能提供的物理资源是有上限的,架构层需要去突破地域的限制以完成资源交付;第二就是为更低成本的机房选址准备好基础能力。这里的主要挑战是在跨地域的网络延迟和带宽约束下,保持服务的可用性和较好的访问性能,以及尽量减少业务的感知。
这里首先要确定的是,业务或是租户放到哪个Region,也就是租户的编排问题。之所以将编排的粒度放到租户,是因为一般来说租户对应某个业务部门或者项目,是一堆计算资源和存储资源的组合。租户内的数据天然有较强的内聚性,可以减少跨Region的访问,如果租户之间的数据依赖比较多可以将多个租户形成租户组合进行编排。
在存储层的优化包括三个方面。
第一是跨Region数据访问带宽可管控。集群上离线负载种类多、波动大,对于重要作业产出有严格SLA要求,此外链路上也可能有在线流量,需防止离线流量将链路打满影响在线服务。在网络层尚不具备精细化管控能力的背景下,我们认为有在软件层面实现精细化和全局的带宽管控方案的必要。
第二个是提供一些机制,前置地减少跨Region的数据访问,优化访问性能。具体方案有,读写流量均提供位置亲和性,从源头减低跨Region访问的出现;此外提供Region内的数据缓存能力,并提供多种触发机制供业务调用。
第三个方面是提供对业务无感的数据搬迁能力。
架构上采用的是大集群的方式,所有的datanode会向所有Region的namenode做汇报,这样的好处是可以利用副本机制来做缓存,而不是依赖外部的缓存系统,数据的一致性会有强保障,另外对业务完全透明,非常友好。第二个方面就是前面说的全局带宽管控服务,我们叫Zone Service,这个服务本身是一个无状态的服务,可快速伸缩。
一个精细化和全局的带宽管控方案,具体的做法是:由于客户端和datanode均有位置信息,当客户端发起跨Region的流量前需要申请zone lease,lease内包含一系列我们预定义的限流维度,主要用于描述这个流量的发起方,便于按照单一维度或者维度组合去做限流,具体维度包括租户、平台、流量类型、优先级、DAG ID等。
管控的大原则是当资源不足时,向高优的带宽需求倾斜;当资源充足时,满足各个优先级的业务需求。具体做法是在业务进程中嵌入Zone service SDK,完成流量信息收集和执行限速两个功能。Zone service SDK将这个流量发起方的跨Region流量情况定期上报到Zone Service,Zone Service收集到所有流量发起方的上报信息并按照多种索引维度维护起来,当达到调度周期后会依据各个链路的限速值、实际带宽的使用情况、设定的维度限速值和各个发起方的优先级、带宽使用率,重新计算各流量发起方的可用流量并返回给SDK。
第二个方面是未接入流量的识别和运营,由于我们集群的依赖方极多,一定会有来不及升级的业务。首先是可以通过客户端版本和ip判断出哪些客户端还未接入,另外是可以利用已有机制做一定程度的限制和阻断,保障整个链路上的带宽不超限制。
最后是提供Zone Service的服务降级能力,当Zone Service服务不可用或过载时,可以把带宽限速的决策降级到本地做,等Zone Service的快速扩容后再恢复到Zone service做全局管控。
元数据请求和数据IO均遵循就近原则。写入时优先使用Client同Region的 router, namespace(Try Best) 和Datanode;读取时优先Client同Region的router和Datanode,当然这里有一个前提是需要做好容量规划,各个地域内存储的IO容量和计算的需求要匹配,可以依据线上的单CU IO吞吐做决策。
另外一个方面就是Region内的本地缓存,出于数据一致性考虑,我们使用了缓存副本的形式而不是独立的缓存系统。在ETL作业等场景,跨Region的数据依赖可根据数据血缘提前判定, 因此提供写触发机制来提前缓存数据到下游计算所在Region;在ADHOC查询等场景无法预判,为应对突发的跨Region数据流量,对于高频跨Region访问的数据可以在Zone service感知并发起读缓存。此外,读写缓存有TTL和容量限制,不计入用户quota用量。
3. 成本优化体系建设
离线存储和对象存储数据量的快速上涨,带来了存储成本的压力。
我们的解决方案是提供不同的存储类型,以应对不同数据对于服务可用性、数据持久性、成本和性能四者的权衡倾向。对象存储有标准存储(本地冗余、同城冗余)、低频存储(本地冗余、同城冗余)四种类型;离线HDFS同样也分为标准存储和低频存储,但只支持本地冗余。
其中标准存储和低频存储分别用于存储访问频率相对较高和较低的数据。低频存储副本数更低,存储机型密度更高,可用性低于标准存储。同城冗余会将数据分散在一个Region的多个AZ内存储,本地冗余只会在一个AZ内存储,数据可靠性和服务可用性相对多AZ形式较低。
低频存储都是基于EC构建的,并支持多种EC算法,包括RS, XOR和LRC,同时支持灵活的K+M配比,便于存储不同热度的数据,同时支持一个数据条带跨AZ放置,可容忍单个AZ的故障。在落地过程中,我们先支持了异步转存EC,再考虑支持直接写EC,主要是由于对象存储和文件系统都可用异步方式转存,但是对象存储不太适合直接写EC。
对于低频存储类型的数据, 我们做了一系列机制来保持数据的持久性和正确性。例如,在EC流程计算出parity后会立刻使用parity重构src并对比,确保parity的正确性;写入src和parity有checksum可保障端到端的数据正确性;对于src和parity均存入一份crc校验到其余分布式kv系统,用于修复数据后的对比,及时发现数据正确性问题、防止错误扩散;所有涉及数据放置位置的操作,均需保证条带数据打散到不同故障域内,防止单个故障域出问题导致数据丢失;引入了离线的数据副本放置位置及数据正确性性的检查机制,从黑盒视角发现问题。
--
02
未来规划
未来我们依然会围绕可用性、可靠性、成本和性能四个方面来优化。
首先存算分离是一个大的趋势,HDFS目前大部分节点还是和计算节点混部的,后面会和计算节点拆开并使用更高密度的存储机型;第二个,HDFS会通过架构升级,做到可与其余存储产品形态(对象存储、NAS、并行文件系统和块存储等)共享资源池,以提高存储领域全局的资源利用率和资源交付效率;第三个,在冷存储方面,我们会探索更多的存储方案来降低冷存储的成本;最后就是继续提升EC数据覆盖率并支持直接写入EC提升转换效率。
我们希望能够进一步完善多租户机制,在后续存储资源池化的大背景下,还可提供不同的可用性SLA保障。
我们会优化Copyset的放置策略、缩短恢复时间,进一步的提高数据持久性。
对于元数据通路,我们会对router和namenode的性能做进一步的优化;对于IO path,我们会进一步优化单机存储引擎并提升IO性能,例如基于现有的XFS,或是基于SSD和HDD的混合存储构建更高性能的单机存储引擎;最后,存算分离场景下,缓存层也是需要优化的一个方面。
--
03
问答环节
Q:慢节点如何处理?
A:有两种方式,一个是规避,一个是熔断。规避是会采集一些datanode的物理指标和服务端指标,将这些指标上报给namenode,namenode会判断这些物理指标和服务端指标是否异常,如果异常,在读取和写入时都会规避这个datanode,当然也会有个兜底,如果这样的节点占比达到一个阈值策略就失效了。除了datanode之外,客户端这边我们会探测实际读写的速度,当发现速度低于某个阈值时,客户端会上报相关的疑似慢节点信息,然后namenode汇总这些节点信息来做决策。之所以是疑似慢节点,主要是对于写时遇到的慢节点,本身pipeline涉及3个节点,因此会同时上报3个节点的信息,namenode会依据汇总信息对这三个节点进行投票,来发现真正慢的节点是哪个。
Q:除了存储量,现在hdfs的成本考虑,有几个维度?
Q:主要是3个维度:集群的数据量、单GB存储成本、平均副本数。平台侧希望在保证数据可用性、持久性和正确性,以及整体性能的前提下,使用更低成本的存储介质来降低单GB成本;通过EC等方式来降低平均副本数;数据量方面平台主要是提供一些数据洞察能力给用户,比如某个目录的数据已经长时间没有访问了,提示是不是可以删除,或者设置生命周期交给生命周期系统定期删除了。
Q:hdfs的EC是不是还有一些bug导致丢数据, 快手怎么做监测和处理?EC对带宽和磁盘io会有多大程度的影响?EC主要用于冷数据方面吗?
A:我们现在EC数据占比很高,整体还是比较稳定的,EC丢数据出现在如果一个条带丢了超过m块的时候,我们会强监控这些高危的条带,提前处理。另外就是会执行离线校验来确定是否有丢数据的风险;EC主要用于低频访问的数据,对于高频访问的数据,还是保持多副本存储,整体来看对带宽和磁盘IO的影响还是比较可控的状态。
今天的分享就到这里,谢谢大家。
阅读更多技术干货文章、下载讲师PPT,请关注微信公众号“DataFunTalk”。
分享嘉宾:丁定华 快手
编辑整理:Brandon OPPO
出品平台:DataFunTalk
分享嘉宾:
活动推荐:
关于我们:
DataFun:专注于大数据、人工智能技术应用的分享与交流。发起于2017年,在北京、上海、深圳、杭州等城市举办超过100+线下和100+线上沙龙、论坛及峰会,已邀请超过2000位专家和学者参与分享。其公众号 DataFunTalk 累计生产原创文章700+,百万+阅读,14万+精准粉丝。
欢迎转载分享评论,转载请私信。
| 留言与评论(共有 0 条评论) “” |