李小翠- 《网易新一代对象存储引擎》

李小翠 / 网易云高级研发工程师

毕业于成都电子科技大学,目前在网易云负责小文件存储系统(NEFS)的开发和产品化工作。对于分布式一致性协议,分布式存储系统各方面 Golang 的应用有较丰富的一线实践经验。



前言

      我是来自网易云存储团队的李小翠,自入职以来便一直在团队中致力于对照存储的研发。今天演讲的核心内容便是向各位介绍一下我们的网易新一代对象存储引擎NEFS。



对象存储是什么?

       首先让我们先来了解一下对象存储是什么。

       对象存储提供 Key Value 存储,Key 的最大存储范围是1KB,存入数据 ID,而 Value 最大为1TB,包含文件、镜像、图片、视频等等,对象存储并不像文件系统那样需要维持很深的目录树,一般分级是两层或三层,所提供的 RestFul 是数据读写接口。

640?wx_fmt=png

       我们今天将从以下三个方面对存储引擎进行说明。第一,整体概述网易对象存储的基础架构。第二,对对照存储系统中的对象存储引擎进行详细说明。最后,对在开发NEFS系统过程中所做的性能优化和质量保障方面的一些工作进行说明。


对象存储基础架构


       网易对象存储基础架构是:首先对一个用户请求进行运行解析,然后到前端的交换机进行负载均衡,交给 Nginx 之后,再把请求交给 Proxy Cluster,在 Proxy Cluster 存储架构中它是无状态的,经过验证之后会通过请求的信息去 DDB 中找到 K,通过 K 可以读取出存储数据的元信息,比如它的存储位置等,我们根据这个元信息可以读出数据对象,而 NEFS 就是一个对象存储引擎,负责数据的存储。对于用户来说,我们暴露的结果是 PUT、GET和DELETE,这个层面我们不做修改。

640?wx_fmt=png


新一代对象存储引擎  


 设计目标

       在开发 NEFS 之前,网易有一个对象存储系统叫做 DFS,但由于它的设计非常简单,比如:没有一个复制协议的概念,每个对象就是一个文件,而恰恰这样的设计对于小文件是非常不友好的,所以在设计 NEFS 的时候,初衷是在 DFS 基础之上设计一个能覆盖未来五到十年需求的系统。

       当时的设计目标主要有以下几点:

       一、单集群规模达到100 PB,有500台机器左右。

       二、工作负载可以适应大小文件,能减少多余的 IO,避免一些IO放大。

       三、可靠性方面,可以支持传统的多副本的存储,对于3副本可以达到8个9的可靠性,EC 则可以达到11个9。

       四、可用性大于99.95,组建高可用,减小依赖。

       五、可扩展性方面,不影响性能,并且可以支持数据的均衡。在数据均衡过程中,也希望不要进行大规模的数据迁移。

       六、支持多租户,支持不同特性的用户可以有不同特性的介质。

       因此,用一句话概括的话,NEFS 就是基于 LOG 的高可用、高可扩、高可靠的对象存储系统,全称为 Netease File System。

       接下来,我将会从用户,运维和开发三个纬度来向大家介绍一下 NEFS。


 整体介绍


      对于用户

       对于用户来说,NEFS 就是一个 Key-Valu 存储系统,所谓 Key 是指标注的文件逻辑位置,Value 是指任意大小的 blob 数据。对于提供给用户的接口有四个,分别为:PutFile、GetFile、DeleteFile、GetFileInfo

640?wx_fmt=png


      运维角度

       从运维角度来看,我们看到的 NEFS 是如下的拓扑结构。最上面是 User,不同用户要求不同,所以这一模块主要起到的是隔离作用。Pool 是存储池,存储池所有类型都是一致的,Pool 之间是物理隔离的,每个存储机只能属于一个 Pool。Zone 是运维域的概念,为了副本的可靠性,在放置副本的时候会考虑到分散在不同的 Zone 中。Server是存储机,挂载多个磁盘设备。

640?wx_fmt=png

   

  对于开发人员

       对于开发人员而言,我想应该从 NEFS 的组成部件说起。

        Partition是Nefs的最小存储单元,大小一般设置为20G。PS即PartitionServer,用于管理磁盘,一个PS管理一个磁盘,一个磁盘上有很多Partition。MDS是元数据管理服务器,如磁盘空间的管理,副本成员的管理、机器上下线管理等。FSI 是 NEFS 提供的一个客户端,我们可以直接通过该客户端跟NEFS进行读写删的交互。

640?wx_fmt=png

       接下来介绍一下 NEFS 的读写流程。

写入:FSI从 MDS 获得可写Partition的信息,MDS 把Partition副本的信息返回给 FSI,FSI选定PS请求写入,PS将写入的数据通过复制协议复制到其他副本后,返回给用户一个 FID。用户可以通过FID读取存储的数据。

640?wx_fmt=png

       读取:FSI对FID进行解析,获取数据所在Partition,接着从MDS获取Partition的副本信息,采用backupRequest和roundRoubin的策略读取数据。

640?wx_fmt=png

       以上是从不同角度对 NEFS 整体的介绍,下面我将要针对 NEFS 的每个模块进行介绍。


  模块分析


      Partition

       Partition 是 NEFS 最小的存储单元,那么 Partition 有些什么特点呢?第一,用户写入的数据就是日志,数据只追加,不修改。删除数据直接追加一条删除记录,实际物理空间的回收由后续的垃圾回收进行。Partiton 由 datafile 和 hintfile 组成的。datafile 有只读和可以进行追加的active 状态。 hintfile 是一个二级索引,和index一样,用于垃圾回收时判断删除标记是否要删除的一个依据。LogEntry 包含用户数据的crc校验,一致性协议相关信息,以及用户数据。之所以要包含一致性协议相关信息,是因为Nefs通过一致性协议保障各副本的一致性,在日志回放、副本不一致的情况下需要用到这些信息。

640?wx_fmt=png

   

   一致性协议

       NEFS 使用基于 PacifcA 的主从复制协议。过程比较简单:用户提交请求给 leader,leader 在日志里面进行追加,并且把请求发送给其他副本,副本写本地日志完成之后,回复leader,leader收到副本的应答之后对当前数据进行commit, 并把写入结果返回给客户端。下一次写入时,leader通知follower进行上条数据的commit.640?wx_fmt=png

       副本的关系不是一直不变的。比如有磁盘坏了,需要进行恢复,那么该磁盘上涉及到的partition的副本的关系必然会变化;其次,机器负载不够均衡,有些机器上的leader较多,写入较多,就需要进行rebalance;再者,磁盘空间使用不均衡等,这些情况均涉及到成员的变更,由于NEFS是有中心的分布存储系统,因此成员变更都是通过 MDS 进行的。

640?wx_fmt=png

      那我们当时选用类PacificA协议,也源于一部分原因是Raft其实在当时也没有非常靠谱的工业化的实现,PacificA毕竟相对相对简单。

如果现在进行系统设计、应该选择什么样子的一致性协议?这里就功能特性、非功能特性方面对两个协议(pacificA、raft)做了一个简要的对比。

从功能上来说:两者的实现难度基本一样。从实现角度可以发现,pacificA跟raft的差别基本只在最后返回客户端的条件判断上,所以说一致性协议在针对不同的应用场景其实在一些点上可以有很大的变化,来满足业务不同的需求,比如我们其实也很容易转化为类似于Raft 的 F/2 +1。成员变更上,PacificA依赖中心节点更加简单。

从性能上来说:类pacificA的协议是全写才返回,而raft是2/F +1 节点写完就可以返回。比如三副本的话,raft响应时间是3各种最快的2个,而pacificA全写是三个中最慢的一个。响应时间的分布基本呈现zifp分布,跟磁盘写入的时间呈现基本同样的分布趋势,但是根据统计每个节点在写磁盘上耗费的均值都只是60ms,但是写三副本的耗时是大于60ms的。

        从可靠性上看:pacificA在挂一个节点情况下没法写入,raft是多数派存储,pacificA 略胜一筹,随时随地都是全副本的可靠性,raft是2/f +1;还有一点就是pacificA 只要有一个副本数据还在就不需要人工介入,但是,raft比如在3个节点情况下,挂2个,就需要人工介入。

要是总体回头看,pacificA也还算是一个相对不错的方案。当然就现在来说选择Raft也不错,反正都是可以现实的需求对协议进行一定调整。

640?wx_fmt=png

接下来说一下NEFS非功能特性。


  非功能特性


      性能

       NEFS 的性能:

       一、只有一次写入,IO 放大系数是1。采用bitCast 小文件合成大文件模型,一次写入只需要一次Append;另外本身多副本一致性协议 不需要额外的实时持久化的信息。

       二、大文件在上层拆解为1MB的小文件进行写入,实现并发写入,可以并发读取。

       三、最后根据现实情况,我们针对HDD性能在设计层面做了一些调优:

a.当然只能限制写入,每个磁盘上能够并发进行开写的Paritition开支在8个左右

b.合并更多的小写

c.因为删除请求发往具体的某个Partition,如果持久化会带来非常严重的随机       IO,所以对Delete不刷盘

640?wx_fmt=png

   

   可靠性

      可靠性:听起来很虚,其实业界各个存储的产品线一般都会有 明确的Durability的SLA,比如我们看AWS S3 对象存储, Standard、Standard-IA、Glacier 对外保障的可靠性都是11个9。11个9翻译一下就是100亿个文件一年只可能丢失1个文件,能不能达到不说,没达到,按照赔偿条款进行赔偿。

640?wx_fmt=png

       可靠性影响因素有几个:

       一、磁盘年故障率;显然导致数据丢失,除了说程序Bug、运维 事故认为因素之外,主要就是磁盘故障,故障率越高可靠性越差。

       二、复制因子,系统使用的副本数越多,显然存储可靠性越高

       三、坏盘恢复时间,对于多副本系统中,坏盘请求导致数据副本的丢失,如果能够越快恢复,可靠性越高。

四、系统中磁盘数量。

       五、系统复制组数量。考虑如下两个极端设计。

       第一种设计,是在包含999块磁盘的3备份存储系统中,同时坏三块盘情况下的数据丢失概率,它的可靠性还是比较高的。

640?wx_fmt=png

       而第二种设计,是我们把数据随机打散掉999块磁盘中,所有数据都可能在其他所有盘中,这样数据丢失的概率就是1,也就是说数据一定会丢失,复制组太多或者太少,其实都有一定的弊端,太多可靠性就会下降。而接下来是系统中磁盘的数量,磁盘数量越多可靠性越低,这是为什么呢?因为从全世界范围内来看,肯定每天都有数据的丢失。640?wx_fmt=png

       对于 NEFS 而言,在系统的硬件规划中,2个交换机堆叠,下联3个Rack,每个Rack放置9台存储机器,一个Zone 27台机器;可用性考虑,2个副本放置在一个Zone,另外一个放置在另外一个Zone。为了加快恢复时间,设计考虑就是利用所有机器、磁盘的IO、带宽进行恢复;一个磁盘上的Partition的副本打散在所有其他机器磁盘上(比如一个Zone的27台机器上,每台机器36块盘)。从我们系统来说,一个磁盘上Partition的数量就是能够打散在所有这些盘上即可。不要太小,太小会导致复制组 变大,所以一个Partition大概也就20GB左右。普通情况下,为了保障对外服务能力。但磁盘、机器 能够参与恢复可能控制在10%差不多了。随意最终结果看一块8TB的盘,在1~2个小时恢复基本可以做到的。

640?wx_fmt=png640?wx_fmt=png

      成本

       从直观层面来说,多几个副本是提高可靠性的唯一方式。但是从根源上来说,我们需要提高的是复制因子,也就是坏多少块和这份数据直接相关盘的情况下会导致数据丢失。所以从这个层面来拓展,分布式存储系统为了提高可靠性,基本可以通过两种方式来达到目的:

1: 合适的多副本

多副本是比较显而易见的,是通过客户端或者服务端进行数据的多备份写入。

2: EC 纠删码冗余技术。

原理就是通过不同的几份数据,通过矩阵换算得到冗余数据块,在出现坏块情况下,通过其他几份数据计算还原得到丢失的数据。 EC纠删码技术这里不多作展开。

640?wx_fmt=png

       在NEFS系统中我们EC的实现原理:

EC基本单位:以Parititon下的data file 为EC的基本单位,一般来说一个data file 为1GB。

EC策略:比如10+4 的策略的话:将1GB 文件拆分为10份数据块,并计算出4份校验块。

1.容忍任意4个Block的丢失(看上去数据拆分更多更佳容易丢失数据,但是要坏容忍坏4副本,随意一般来说EC的可靠性会比三副本可靠性高几个数量级)

2.存储开销: 1.4x = 14/10

3.任意一个块损坏,需要通过网络读取10个Block进行恢复

当然也可以根据实际的情况,做更多的EC测测的选择,更多的暂时不多做介绍,因为EC还在设计实现中,不太好说过多。

640?wx_fmt=png

       以上是对 NEFS 整个对象存储系统在一些细节以及架构上的介绍,下面主要说明一下在开发过程中我们对性能优化和质量保障做的一些工作。


性能优化和质量保障


    性能优化

       问题分析&解决

因为 NEFS 一直在线上使用,去年系统切进百分之百流量时,发生了机器负载过高,夜高峰内存使用量达到80%的情况,随后采取临时措施内存得以降低,后面是优化后的结果。

采取的临时措施是降低GOGC的值,原来默认是100,我们调整为50后,预计golang GC的 频率变高,释放内存速度变快。虽然有一定的效果,但也带来了一定问题:由于要清理的内存量较大GC 颠簸严重,停顿上百秒,并且线上有很多失败请求,线上每个进程有两百多个线程创建出来。所以这个调整是治标不治本。

后来我们对问题进行分析,发现在请求数量上升过程中,磁盘 IO 变高,处于系统调用状态的协程变多了,线程数上升,内存占用升高,这是现状。请求数量上升,内存占用更多,GOGC 更为频繁,CPU 繁忙,导致GOGC 很慢,导致请求堆积,进一步加剧内内存占用。所以要解决的有两点问题:内存占用问题以及整个过程中不能让线程数量过多。640?wx_fmt=png640?wx_fmt=png

       pprof 是golang自带的性能调优工具,该工具收集程序运行时信息,并给出内存占用较大的函数调用。通过该工具我们发现关键路径上有很多内存拷贝。

优化一是减少程序读请求路径上内存拷贝,改了之后发现对内存的使用并没有很好的效果,只能降低10%左右。

再分析发现 thrift 中的数据内存拷贝在请求较多的情况下占用内存较多。TFramedTransport 先把数据序列化后再拷贝,这里做一个优化,序列化之后,不需要把内容拷贝到buffer中在flush。在我们的场景是可以兼容的。改动完之后内存降低50%。

       第二个问题线程数量会随着读请求的增多而增多。因此我们对 IO 做了两层并发控制,一个是外层请求数量会控制并发,第二是磁盘IO并发控制,用来降低线程数量,减少机器的负载。640?wx_fmt=png

       对IO并发控制则设置为60,单个最大并发度不超过200,并发度不能过小,因为过小会造成等待时间过长,很多请求会超时,当时也是做了测试,比如并发度为1的时候,我们发现排队时间会达到5秒甚至超过10秒,当并发度为120的时候 wait tiem 会很小,60的时候也会很小。

640?wx_fmt=png

       

场景&分析

       场景主要是线上发现读请求较多情况下,用户请求延迟非常大,是 IO 延迟的数倍,并且并发越高延迟明显增加。线下做了压测发现,如果在 写入并发达到20时,延迟高达几百毫秒,但实际上刷盘时候就只有100毫秒。我们使用golang的trace工具跟踪一个请求的生命周期是非常合适的。图一显示了一分钟内,请求响应时间的统计,并且给出了分位点。图二显示了响应时间大于0.1秒的请求。所以通过trace工具,可以很快的看到当前系统是否正常。回到之前我们说的问题,trace可以在请求的生命周期内埋点,打印出每一步的延迟。这样可以排查出耗时的步骤从而进行优化。

640?wx_fmt=png

    质量保障


       NEFS 测试

       单元测试和系统测试,应该是在系统开发过程中大家都会做的,构建思路也比较清晰。go提供的test测试框架可以方便的进行常规测试,耦合问题可以通过mockgen工具来解决。系统测试人可以从外部进行压测、功能验证; 也可以通过gofail 来控制键路径上的异常返回,集成测试做的事情,系统测试都可以做。但是如果每次系统测试,都需要构造相同的异常场景,把所有的点都测试一遍,费时费力。轻量级的系统测试是需要的,因此NEFS还做了集成测试。

       集成测试在构建过程中需要考虑些什么?

1.采用何种方式启动ps,mds?我们可以用协程的方式,也可以用进程的方式运行,最初写的时候还是以协程的方式做的,但是协程方式有一个问题,比如我想把这个 ps 挂掉,让它下线,协程就很不方便了。因此这样的方式后来没有采用,最后我们采用进程的方式来启动 ps 和 mds。

2.各组件之间网络通信方式?采用net namespace的范式

3.如何解耦ps,mds与zk,db的依赖?使用memzk,以及嵌入式的sqlite

4.程序处理异常如何模拟?使用gofail

5.客户端fsi如何使用?FSI 是 NEFS 提供客户端的 jar 包,而NEFS 的 Server 端使用 GO 编写,因此我们最终做了一个解决方案:在 FSI 起一个 http Server,供测试所用。

640?wx_fmt=png

     

  NEFS质量保障

go的技术栈 提供了非常多便捷的工具,可以用来提高我们的代码质量

1、系统各个指标的收集 是监控 系统健康状况的重要手段,也是发现问题和排查问题所需的必不可少的信息。比如qps的收集、垃圾回收数据量的收集等

2、之前系统中存在不少问题,引用megacheck进行更高质量的代码检查。代码优化中会有这样的提示:复制slice的时候,使用何种操作更为简便

mds/topology/pickup.go:143:3: should replace loop with nodes = append(nodes, dataNodes...)

3、go中还有辅助检测race的工具,可以在集成测试中开启

640?wx_fmt=png640?wx_fmt=png

       以上就是对 NEFS 的介绍,以及我们在真实开发过程中遇到的一些问题,欢迎有意向、有兴趣加入网易存储团队的小伙伴们,一起参与设计研发的工作!


Q&A   

提问:李老师你好,我有一个问题,刚才说到删除不刷盘,到底是什么意思?

   

李小翠:这是我们做删除操作的时候,一般对象存储系统不会真的把那个物理文件删除,直接写入一条删除标记,对于删除记录我们不做sync操作。

   

提问:还有一个问题,中间有说近来并发比较大,优化到thrift协议里面去的优化,我看你们最后还是做了一个缓存,那个缓存跟TCP缓存有什么区别吗?

   

李小翠:其实原来它是为了把所有数据都收集下来,完全写成一帧。

   

提问:thrift的算法,不管你写多少都会缓存成一个……

   

李小翠:对,比如我在这里进行一个缓存,这个数据很大,比如原来这个是128,现在数据有256,是不是要重新搞一个256的内存,然后把128的释放?会有这种内存的开销。当我们读特别多的时候,这个开销就会放大。所以我们这里做buffer,就是为了几个字节的时候都拼起来,做成一帧再写入。再大的时候就没有必要了,是为了防止buffer变长的一个开销。

       

提问:你好,用户那一侧怎么找到那个小红键呢?链路怎么走?

   

李小翠:因为我们在写入完成后会返回一个FID给用户,由partitionID和InternalID组成,internalID在partition中是递增的。通过FID可以确定数据所在的位置

   

提问:刚才还涉及到机房问题,用户数据过来的时候其实都是隐藏在后面的,用户进来可能是最外一层,怎么知道是哪个Pool?

   

李小翠:Pool的相关信息存储在mds中,Pool是一个逻辑上的概念,是面向nefs服务器的,mds会记录user和pool的对应关系。用户角度是没有pool的概念的。

   

提问:我有一个问题,你提到做磁盘的(英文),还包括删除以后只是写一条追加记录,等后面有GC的过程,可能这个GC是跟(英文)一起做的,这方面的介绍您介绍的比较少,我想知道如果要做这个(英文)怎么管理?这是一个自动的过程还是运维的过程?

   

李小翠:这是一个自动的过程,我们会有一个预值,比如多久开始对它做数据恢复。因为PS上面很多(英文),假设一个副本是放在1、2、3PS上,这个3挂掉了,MDS会针对当前负载信息选择一个PS,让它作为第三个副本,让它去恢复,整个过程都是MDS控制的。还有leader的变更也是由MDS做的,因为复制协议会有这样一种情况,就是写都是从leader过的,假设你的PS上有几百个leader,都是申请到这个PS上,这个负载就会非常大。所以我们会让leader尽可能分布在不同的PS上面,因为原来没有这种设计,会出现不均衡,也是根据当前每个PS上leader的数量,对他进行均衡以及leader的切换,这都是在后台每隔5分钟做一个检查,是以这种模式来的。

   

提问:数据删除你们什么时候做清理?

   

李小翠:数据删除先有一个记录,在晚上的时候做清理,因为需要读取数据,所以白天做可能对用户请求有影响。我们做GC怎么做的呢?比如我们扫描这个数据,看这里面数据的比例,比如如果达到0.8我们就进行回收,垃圾回收是走复制协议的,因为所有的复制协议是基于字节一致的基础之上。对于这里面非垃圾数据会根据数据协议进行重写,写完了就会把这个数据删掉。

   

提问:删除数据你们是晚上批量去做?

   

李小翠:是的。

   

提问:我有一个问题,我觉得你这个架构跟(英文)特别像,但是有一点不同,你中间有原数据重组,有MDS,如何实现高可用呢?

   

李小翠:实际上我们会部署三个,然后进行选组,我们会做备份,另外两台是处于备状态,其他挂掉了会由它做备份。

   

提问:为什么不做成分布式集群?只有一个在运行,有一个挂了以后再把另外一个接进来?

   

李小翠:对,是这样的。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值