【HDFS面试】HDFS面试题&答案

35 篇文章 3 订阅
5 篇文章 0 订阅

题目与答案(30题)

✅HDFS文件写入和读取流程

✅HDFS组成架构

✅介绍下HDFS,说下HDFS优缺点,以及使用场景

✅HDFS作用

✅HDFS的容错机制

✅HDFS的存储机制

✅HDFS的副本机制

✅HDFS的常见数据格式,列式存储格式和行存储格式异同点,列式存储优点有哪些?

✅HDFS如何保证数据不丢失?

✅HDFS的文件结构?

✅介绍下HDFS的Block

✅HDFS的默认副本数?为什么是这个数量?如果想修改副本数怎么修改?

✅HDFS的块默认大小,64M和128M是在哪个版本更换的?怎么修改默认块大小?

✅HDFS的block为什么是128M? 增大或减小有什么影响?

✅导入大文件到HDFS时如何自定义分片?

✅HDFS的mapper和reducer的个数如何确定?reducer的个数依据是什么?

✅HDSF通过那个中间组件去存储数据

✅HDFS跨节点怎么进行数据迁移

✅HDFS NameNode高可用如何实现?需要哪些角色?

✅HDFS HA怎么实现?是个什么架构?

✅HDFS的数据一致性靠什么保证?

✅HDFS怎么保证数据安全

✅HDFS中向DataNode写数据失败了怎么办?

✅Hadoop2.xHDFS快照

✅HDFS文件存储的方式?

✅HDFS写数据过程,写的过程中有哪些故障,分别会怎么处理?

✅NameNode存数据吗?

✅使用NameNode的好处

✅HDFS中DataNode怎么存储数据的

✅直接将数据文件上传到HDFS的表目录中,如何在表中查询到该数据?


题目与答案(30题)

✅HDFS文件写入和读取流程

写文件流程

要点:

1、NN会触发副本放置策略返回Client一个DN排序
2、Client写文件会直连DN,流式传输,走一个pipline,变种的并行
3、最值钱是下面这个

  1. 发送创建文件请求:调用分布式文件系统 DistributedFileSystem.create( )方法;
  2. NameNode 创建文件记录:分布式文件系统 DistributedFileSystem 发送 RPC 请求给 NameNode,NameNode 检查权限后创建一条记录,返回输出流 FSDataOutputStream,封装了输出流 DFSOutputDtream;
  3. 客户端写入数据:输出流 DFSOutputDtream 将数据分成一个个的数据包,并写入内部队列。DataStreamer 根据 DataNode 列表来要求 NameNode 分配适合的新块来存储数据备份。 一组 DataNode 构成管线(管线的 DataNode 之间使用 Socket 流式通信);
  4. 使用管线传输数据:DataStreamer 将数据包流式传输到管线第一个DataNode,第一个 DataNode 再传到第二个DataNode,直到完成;
  5. 确认队列:DataNode 收到数据后发送确认,管线的 DataNode 所有的确认组成一个确认队列。所有 DataNode 都确认,管线数据包删除;
  6. 关闭:客户端对数据量调用 close( ) 方法。将剩余所有数据写入DataNode管线,联系NameNode并且发送文件写入完成信息之前等待确认;
  7. NameNode确认:
  8. 故障处理:若过程中发生故障,则先关闭管线,把队列中所有数据包添加回去队列,确保数据包不漏。为另一个正常 DataNode 的当前数据块指定一个新的标识,并将该标识传送给 NameNode,一旦故障 DataNode 在恢复后删除上面的不完整数据块。从管线中删除故障 DataNode 并把余下的数据块写入余下正常的 DataNode。NameNode 发现复本不足时,会在另一个节点创建一个新的复本;

在数据的读取过程中难免碰到网络故障,脏数据,DataNode 失效等问题,这些问题 HDFS 在设计的时候都早已考虑到了。下面来介绍一下数据损坏处理流程:

  • 当 DataNode 读取 block 的时候,它会计算 checksum。
  • 如果计算后的 checksum,与 block 创建时值不一样,说明该 block 已经损坏。
  • Client 读取其它 DataNode上的 block。
  • NameNode 标记该块已经损坏,然后复制 block 达到预期设置的文件备份数 。
  • DataNode 在其文件创建后验证其 checksum。

读文件的流程

如图所示,读文件的流程主要包括以下6个步骤:

  1. 打开分布式文件:调用分布式文件 DistributedFileSystem.open( ) 方法;
  2. 寻址请求:从 NameNode 处得到 DataNode 的地址,DistributedFileSystem使用 RPC 方式调用了NameNode,NameNode 返回存有该副本的DataNode 地址,DistributedFileSystem 返回了一个输入流对象(FSDataInputStream),该对象封装了输入流 DFSInputStream;
  3. 连接到DataNode:调用输入流 FSDataInputStream.read( ) 方法从而让DFSInputStream 连接到 DataNodes;
  4. 从 DataNode 中获取数据:通过循环调用 read( ) 方法,从而将数据从 DataNode 传输到客户端;
  5. 读取另外的 DataNode 直到完成:到达块的末端时候,输入流 DFSInputStream 关闭与 DataNode 连接, 寻找下一个 DataNode;
  6. 完成读取,关闭连接:即调用输入流 FSDataInputStream.close( );

✅HDFS组成架构

角色:NameNode + DataNode + Client
角色即jvm进程
主从与主备的区别:主从都工作互相通信,主备是高可用场景下,备用的不处理任何业务的。HDFS维护一颗文件目录树(path+文件名,全局唯一),不是物理操作系统的目录树。

非高可用集群架构

非高可用HDFS集群工作的时候会启动三个服务,分别是NameNode 和 DataNode以及SecondaryNameNode 。
其中NameNode是HDFS的中心服务,主要维护管理文件系统中的文件的元数据信息,DataNode主要负责存储文件的真实数据块信息,当然在DataNode的数据块信息中也包含一下关于当前数据块的元数据信息,例如:检验值、数据长度、时间戳等。
在非高可用HDFS集群中NameNode和DataNode可以理解为是一对多的关系。二者在集群工作中也要保持通信,通常默认3秒钟会检测一下心跳。最后SecondaryNameNode的工作很单一,就是为了给NameNode的元数据FsImage文件和Edit编辑日志进行合并,并自己也保留一份元数据信息 以防NameNode元数据丢失后有恢复的保障。

✅介绍下HDFS,说下HDFS优缺点,以及使用场景

HDFS,是Hadoop Distributed File System的简称,是Hadoop抽象文件系统的一种实现。Hadoop抽象文件系统可以与本地系统、Amazon S3等集成,甚至可以通过Web协议(webhsfs)来操作。HDFS的文件分布在集群机器上,同时提供副本进行容错及可靠性保证。例如客户端写入读取文件的直接操作都是分布在集群各个机器上的,没有单点性能压力。

HDFS优缺点

优点

■ 1.高容错性
  ● 数据自动保存多个副本,通过增加副本的机制,提高容错性。
  ● 某个副本丢失以后,可以实现自动恢复。
■ 2.适合处理大数据
  ● 数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据
  ● 文件规模:能够处理百万规模以上的文件数量。
■ 3.可构建在廉价的机器上,通过副本机制,提高可靠性

缺点

■ 1.不适合低延时数据访问,比如毫秒级的数据存储,是做不到的。
■ 2.无法高效的对大量小文件进行存储
■ 3.不适合并发写入、不适合文件的随机修改

✅HDFS作用

HDFS(Hadoop Distributed File System) , 它是一个分布式的文件系统,用于存储文件,通过多副本机制来进行容错,适用于大数据场景。

✅HDFS的容错机制

HDFS 容错指的是集群部分机器宕机了,集群依然可以正常提供服务的能力。HDFS 是具有很好的容错性的分布式存储系统,它利用复制技术实现数据容错能力,数据会被复制多份并存储在集群的不同节点。这样,集群中的某些机器宕机了,数据还可以从其他正常运行的机器获取。如果有一个机器宕机了,HDFS 会在其他可用的机器创建数据的副本,来保证该数据的副本数与集群的副本因子是一致的。

HDFS 通过复制进程来保证容错机制。在文件写入 HDFS 时,HDFS 会首先把文件分割成块,并把这些数据块存储在集群不同机器上,然后在其他机器创建各个块的副本,默认情况下,HDFS 会在其他机器创建3个文件的副本。所以,HDFS 集群任意机器挂了,我们依然能从其他保存数据副本的机器上读取数据,由于这种独特的分布式存储特性,HDFS 给我们提供了更快的文件读写机制。

✅HDFS的存储机制

1. HDFS开创性地设计出一套文件存储方式。即对文件切割后分别存放;

2. HDFS将要存储的大文件进行切割,切割后存放在既定的存储块(Block)中,并通过预先设定的优化处理,模式对存储的数据进行预处理,从而攻克了大文件储存与计算的需求。


HDFS(Hadoop Distributed File System)是Hadoop分布式计算中的数据存储系统,是基于流数据模式访问和处理超大文件的需求而开发的。
HDFS的基础概念
Block
HDFS中的存储单元是每个数据块block,HDFS默认的最基本的存储单位是128M的数据块。和普通的文件系统相同的是,HDFS中的文件也是被分成128M一块的数据块存储的。不同的是,在HDFS中,如果一个文件大小小于一个数据块的大小,它是不需要占用整个数据块的存储空间的。
NameNode
元数据节点。该节点用来管理文件系统中的命名空间,是master。其将所有的文件和文件夹的元数据保存在一个文件系统树中,这些信息在硬盘上保存为了:命名空间镜像(namespace image)以及修改日志(edit log),后面还会讲到。此外,NameNode还保存了一个文件包括哪些数据块,分布在哪些数据节点上。然而,这些信息不存放在硬盘上,而是在系统启动的时候从数据节点收集而成的。
DataNode
数据节点。是HDFS真正存储数据的地方。客户端(client)和元数据节点(NameNode)可以向数据节点请求写入或者读出数据块。此外,DataNode需要周期性的向元数据节点回报其存储的数据块信息。
Secondary NameNode
从元数据节点。从元数据节点并不是NameNode出现问题时候的备用节点,它的主要功能是周期性的将NameNode中的namespace image和edit log合并,以防log文件过大。此外,合并过后的namespace image文件也会在Secondary NameNode上保存一份,以防NameNode失败的时候,可以恢复。
Edit Log
修改日志。当文件系统客户端client进行------写操作的时候,我们就要把这条记录放在修改日志中。
在记录了修改日志后,NameNode则修改内存中的数据结构。每次写操作成功之前,edit log都会同步到文件系统中。
FsImage
命名空间镜像。它是内存中的元数据在硬盘上的checkpoint。当NameNode失败的时候,最新的checkpoint的元数据信息就会从FsImage加载到内存中,然后注意重新执行修改日志中的操作。而Secondary NameNode就是用来帮助元数据节点将内存中的元数据信息checkpoint到硬盘上的。
Checkpoint的过程如下
1)Secondary NameNode通知NameNode生成新的日志文件,以后的日志都写到新的日志文件中。2)Secondary NameNode用http get从NameNode获得fsimage文件及旧的日志文件。

Secondary NameNode将FsImage文件加载到内存中,并执行日志文件中的操作,然后生成新的FsImage文件。
Secondary NameNode将新的fsimage文件用http post传回NameNode。
NameNode可以将旧的fsimage文件及旧的日志文件,换为新的fsimage文件和新的日志文件(第一步生成的),然后更新fstime文件,写入此次checkpoint的时间。
这样NameNode中的fsimage文件保存了最新的checkpoint的元数据信息,日志文件也重新开始,不会变的很大了。

✅HDFS的副本机制

HDFS上的文件对应的 Block 保存多个副本,且提供容错机制,副本丢失或宕机自动恢复。默认存3份副本。

副本放置策略:

■ 第一副本:放置在上传文件的 DataNode上; 如果是集群外提交,则随机挑选一台磁盘不太慢、CPU不太忙的节点
■ 第二副本:放置在与第一个副本不同的机架的节点上
■ 第三副本:与第二个副本相同机架的不同节点上。
■ 如果还有更多的副本:随机放在节点中。

可回答:1)HDFS的副本放置策略;2)写数据时如何选择DataNode节点呢?这么选择节点的依据是什么?
在HDFS中,一个文件会被拆分为一个或多个数据块。默认情况下,每个数据块都会有3个副本。每个副 本都会被存放在不同的机器上,而且每一个副本都有自己唯一的编号。
NameNode节点选择一个DataNode节点去存储block副本的过程就叫做副本存放,这个过程的策略其实 就是在可靠性和读写带宽间的权衡。

Hadoop3.x副本节点选择:
由上图可知,第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。 第二个副本在另一个机架的随机一个节点。
第三个副本在第二个副本所在机架的随机节点。

✅HDFS的常见数据格式,列式存储格式和行存储格式异同点,列式存储优点有哪些?

列式存储(column-based)是相对于传统关系型数据库的行式存储(Row-basedstorage)来说的。简单来说两者的区别就是如何组织表:

在数据写入上的对比:
1)行存储的写入是一次完成。如果这种写入建立在操作系统的文件系统上,可以保证写入过程的成功或者失败,数据的完整性因此可以确定。

2)列存储由于需要把一行记录拆分成单列保存,写入次数明显比行存储多(意味着磁头调度次数多,而磁头调度是需要时间的,一般在1ms~10ms),再加上磁头需要在盘片上移动和定位花费的时间,实际时间消耗会更大。所以,行存储在写入上占有很大的优势。

3)还有数据修改,这实际也是一次写入过程。不同的是,数据修改是对磁盘上的记录做删除标记。行存储是在指定位置写入一次,列存储是将磁盘定位到多个列上分别写入,这个过程仍是行存储的列数倍。所以,数据修改也是以行存储占优。

在数据读取上的对比:
1)数据读取时,行存储通常将一行数据完全读出,如果只需要其中几列数据的情况,就会存在冗余列,出于缩短处理时间的考量,消除冗余列的过程通常是在内存中进行的。

2)列存储每次读取的数据是集合的一段或者全部,不存在冗余性问题。

3) 两种存储的数据分布。由于列存储的每一列数据类型是同质的,不存在二义性问题。比如说某列数据类型为整型(int),那么它的数据集合一定是整型数据。这种情况使数据解析变得十分容易。
相比之下,行存储则要复杂得多,因为在一行记录中保存了多种类型的数据,数据解析需要在多种数据类型之间频繁转换,这个操作很消耗CPU,增加了解析的时间。所以,列存储的解析过程更有利于分析大数据。

4)从数据的压缩以及更性能的读取来对比

优缺点
显而易见,两种存储格式都有各自的优缺点:

1)行存储的写入是一次性完成,消耗的时间比列存储少,并且能够保证数据的完整性,缺点是数据读取过程中会产生冗余数据,如果只有少量数据,此影响可以忽略;数量大可能会影响到数据的处理效率。

2)列存储在写入效率、保证数据完整性上都不如行存储,它的优势是在读取过程,不会产生冗余数据,这对数据完整性要求不高的大数据处理领域,比如互联网,犹为重要。

传统行式数据库的特性如下:

①数据是按行存储的。

②没有索引的查询使用大量I/O。比如一般的数据库表都会建立索引,通过索引加快查询效率。

③建立索引和物化视图需要花费大量的时间和资源。

④面对查询需求,数据库必须被大量膨胀才能满足需求。

列式数据库的特性如下:

①数据按列存储,即每一列单独存放。

②数据即索引。

③只访问查询涉及的列,可以大量降低系统I/O。

④每一列由一个线程来处理,即查询的并发处理性能高。

⑤数据类型一致,数据特征相似,可以高效压缩。比如有增量压缩、前缀压缩算法都是基于列存储的类型定制的,所以可以大幅度提高压缩比,有利于存储和网络输出数据带宽的消耗。


1、Hadoop中的文件格式大致上分为面向行和面向列两类行式存储
一条数据保存为一行,读取一行中的任何值都需要把整行数据都读取出来(如:Sequence Files,Map File,Avro Data Files),这种方式在磁盘读取的开销比较大,这无法避免。
列式存储
整个文件被切割为若干列数据,每一列中数据保存在一起(如:Parquet,RC Files,ORC Files,Carbon Data,IndexR)。这种方式会占用更多的内存空间,需要将行数据缓存起来。
2、列存储和行存储异同点从以下几个方面说明
写入:
行存储的写入是一次完成,数据的完整性因此可以确定;
列存储需要把一行记录拆分成单列保存,写入次数明显比行存储多; 行存储在写入上占有很大的优势。
数据修改:
行存储是在指定位置写入一次,列存储是将磁盘定位到多个列上分别写入; 行存储在数据修改也是占优的。
数据读取:
行存储通常将一行数据完全读出,如果只需要其中几列数据,就会存在冗余列; 列存储每次读取的数据是集合中的一段或者全部;
由于列储存的数据是同质的,这种情况使数据解析变得容易。行存储则复杂的多,因为在一行记录 中保存了多种类型的数据,数据解析需要在多种数据类型之间频繁转换,这个操作很消耗cpu;
所以列存储的解析过程中更有利于分析大数据。
3、列存储和行存储优缺点行存储
优点:行存储的写入是一次性完成的,写入效率高,消耗的时间比列存储少,并且能够保证数据的完整 性
缺点:数据读取过程中会产生冗余数据,如果只看少量数据,此影响可以忽略;数量大可能会影响数据 的处理效率
列存储
优点:在读取过程中,不会产生冗余数据,这对数据完整性要求不高的大数据处理领域尤为重要
缺点:写入效率,保证数据完整性上都不如行存储

✅HDFS如何保证数据不丢失?

一、文件的完整
1、在文件刚刚开始建立的时候,文件中的每个数据块都会产生一个校验和(checksum),这个校验和会保存在.meta文件内部。
2、客户端获取数据的时候会和这个校验和进行对比,检查是否相同,从而发现数据块是否损坏。
2、如果说正在读取的数据块损坏,则可以读取其他副本。接着NameNode会标记该block块已经损坏,然后复制该block达到预期设置的文件备份数。
4、DataNode在其文件创建后周期性验证其校验和(checksum)

补充:
  校验和–checksum是对一组数据(通常是一个文件)进行算法-加密哈希函数运算得到的结果。通过比较你手头文件和原始文件的校验和,能够确保你对原始文件的拷贝是真的并且不存在错误。
  校验和通常也被称之为哈希值、哈希和、哈希码,或简称为哈希–hash。


1)数据在写入之后进行校验和的计算,DataNode周期性的进行校验和计算,将计算结果与第一次进行对比,若相同表示无数据丢失,若不相同表示有数据丢失,丢失后将进行数据修复;

2)数据读取之前对数据进行校验,与第一次的结果进行对比,若相同表示数据没有丢失,可以读取, 若不相同表示数据有所丢失,到其他副本读取数据。
保证DataNode节点保证数据完整性的方法

3)当 DataNode 读取 Block 的时候,它会计算CheckSum,如果计算后的 CheckSum,与Block 创建时值不一样,说明 Block 已经损坏,Client 读 取 其 他 DataNode 上 的 Block;

4)常见的校验算法 crc(32),md5(128),sha1(160);

5)DataNode 在其文件创建后周期验证CheckSum。
案例分析
第一步:写入数据
第二步:进行首次校验和,得到数值70
第三步:读取数据时,仍然会校验和,得到数据70
第四步:对比两次较校验和的值
如两次读取的值都是70,则开始读取数据
如两次读取的值不一致,说明此节点数据丢失,则去其他节点读取数据

✅HDFS的文件结构?

NameNode文件结构

VERSION文件
VERSION是java属性文件

edits_*文件
        edits文件中存放的是客户端执行的所有更新命名空间的操作。
        这里我们首先了解一下transactionId的概念。transactionId与客户端每次发起的RPC操作相关,当客户端发起一次RPC请求对Namenode的命名空间修改后,Namenode就会在editlog中发起一个新的transaction用于记录这次操作,每个transaction会用一个唯一的transactionId标识。
        edits_*分为两类文件,一类是edits_startTransactionId-endTransactionId,另一类是edits_inprogress_startTransactionId。对于第一类文件,每个edits文件都包含了文件名中从startTransactionId开始到endTransactionId之间的所有事务。
        第二类文件表示正在进行处理的editlog,所有从startTransactionId开始的新的修改操作都会记录在这个文件中,直到HDFS重置这个日志文件,重置操作会将inprogress文件关闭,并将inprogress文件改名为正常的editlog文件,即第一类文件,同时还会打开一个新的inprogress文件,记录正在进行的事务。

fsimage_*文件
        fsimage文件其实是hadoop文件系统元数据的一个永久性的检查点,其中包含hadoop文件系统中的所有目录和文件inode的序列化信息。
        fsimage_*的具体文件命名规则是这样的:fsimage_endTransactionId,它包含hadoop文件系统中endTransactionId前的完整的HDFS命名空间元数据镜像。

fsimage_*.md5文件
md5校验文件,用于确保fsimage文件的正确性,可以作用于磁盘异常导致文件损坏的情况。

seen_txid文件
这个文件中保存了一个事务id,这个事务id值只会在两种情况下更新:
上一个检查点(即合并edits和fsimage文件)时的最新事务id
编辑日志重置(即生成一个新的inprogress文件)时的最新事务id

in_use.lock文件
这是一个被Namenode线程持有的锁文件,用于防止多个Namenode线程启动并且并发修改这个存储目录

DataNode文件结构

blk_*文件
blk_*是数据块文件,其中*代表的数据是数据块id。
blk_*_*.meta是数据块校验文件,其中第一个*是数据块id,第二个*代表数据块的版本号。
blk的meta校验文件(保存blk的checksum信息)大小大概是blk文件大小的1/128,因为每512字节做一次校验生成4字节校验,在机器上验证了一下大小,接近1/128。

参考:HDFS文件目录结构详解_hdfs目录结构-CSDN博客

✅介绍下HDFS的Block

✅HDFS的默认副本数?为什么是这个数量?如果想修改副本数怎么修改?

1、默认副本数:3个。
2、设置为3个的原因:既保证了数据的相对高的可靠性又避免了数据的大量冗余。

HDFS采用一种称为机架感知的策略来改进数据的可靠性、可用性和网络带宽的利用率。
在大多数情况下,HDFS的副本系数是3,HDFS的存放策略是一个副本存放在本地机架节点上,另一个副本存放在不同一机架的节点上,第三个副本存放在在与第二个节点同一机架的不同节点上。这种策略减少了机架间的数据传输,提高了写操作的效率。机架错误的概率远比节点错误的概率小,所以这种策略不会对数据的可靠性和可用性造成影响。与此同时,因为数据只存在两个机架上,这种策略减少了读数据时需要的网络传输带宽。
在这种策略下,副本并不是均匀地分布在机架上。这种策略在不损坏可靠性和读取性能的情况下,改善了写的性能。

3、修改步骤:
第一步:将HDFS上已有文件副本数修改为2;
hdfs dfs -setrep 2 -R -w /
第二步:修改dfs.replication值为2(页面上操作),然后重启HDFS
hdfs-site.xml文件:

<property>   
<name>dfs.replication</name>   
<value>2</value>
​​​​​​​</property>

第三步:修改副本数之后进行核对 hdfs fsck / -files -blocks

第四步、手动创建一个文件,上传至HDFS,观察其副本数

✅HDFS的块默认大小,64M和128M是在哪个版本更换的?怎么修改默认块大小?

Hadoop 2.7.2版本及之前默认64MB,Hadoop 2.7.3版本及之后默认128M
通过修改hdfs-site.xml文件中的dfs.blocksize对应的值修改block大小

注意:在修改HDFS的数据块大小时,首先停掉集群hadoop的运行进程,修改完毕后重新启动。

✅HDFS的block为什么是128M? 增大或减小有什么影响?

HDFS中的文件在物理上是分块存储(Block),块的大小可以通过配置参数(dfs.blocksize)来规定,默认的大小是128M。

HDFS块大小设置与磁盘的传输速度紧密相关。寻址时间为传输时间的百分之一时,为最佳的状态。一般的寻址时间约为10ms,则传输时间约为1s,而目前磁盘的传输速度普遍为100MB/s,所以将Block的大小设置为128M最为合理,当然,随着磁盘的传输速度的提升,可以适当调整Block的大小。

为什么块的大小不能设置太小,也不能设置太大?

  1. HDFS的块设置太小,寻址时间的比例增加,程序花费大量的时间用来进行寻址操作
  2. HDFS的块设置太大,磁盘传输数据的时间明显大于定位时间(传输的块中的数据不一定全部都需要),导致程序在处理这块数据时,会变得非常慢。
  3. 总结:HDFS块的大小设置主要取决于磁盘的传输速率。

1、首先先来了解几个概念
寻址时间:HDFS中找到目标文件block块所花费的时间。
在这里插入图片描述

原理:文件块越大,寻址时间越短,但磁盘传输时间越长;文件块越小,寻址时间越长,但磁盘传输时 间越短。

如果块设置过大,一方面从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时,变得非常慢;另一方面,MapReduce中的map任务通常一次只处理一个块中的数据,如果块过大运行速度也会很慢。
如果设置过小,一方面存放大量小文件会占用NameNode中大量内存来存储元数据,而NameNode的内存是有限的,不可取;另一方面块过小,寻址时间增长,导致程序一直在找block的开始位置。因此,块适当设置大一些,减少寻址时间,传输一个有多个块组成的文件的时间主要取决于磁盘的传输速度。

✅导入大文件到HDFS时如何自定义分片?

a. 参数调整Split

hadoop 的作业在提交过程中,需要把具体的输入进行分片。具体的分片细节由InputSplitFormat 指定。分片的规则为 FileInputFormat.class 中的getSplits()方法指定:
long splitSize = computeSplitSize(goalSi***Size, blockSize);
computeSplitSize: Math.max(minSize, Math.min(goalSize, blockSize));
其中 goalSize 为 “InputFile 大小” / “我们在配置文件中定义的 mapred.map.tasks” 值,minsize 为 mapred.min.split.size,blockSize 为 128MB,所以,这个算式为取分片大小不大于 blockSize,并且不小于在 mapred.min.split.size 配置中定义的最小 Size
当某个分块分成均等的若干分片时,会有最后一个分片大小小于定义的分片大小,则该分片独立成为一个分片。

b. 自定义InputFormat

关于InputFormat接口的两个方法:

1.getSplit是从HDFS上读取文件,并形成逻辑的分片,在本文中,这个分片会包含多个小文件。
2.createRecordReader会创建一个RecordReader对象,用来读取getSplit产生的分片,mapper中的键值对就是这个RecordReader输出的。

自定义MyInputFormat类实现分片策略,但是分片之后如何读取分片内的数据是createRecordReader方法创建的RecordReader对象决定的。

所以自定义分片策略的关键在于两点:

1.MyInputFormat类自定义分片策略
2.MyRecordReader类自定义读取分片内的数据

参考:自定义分片策略解决大量小文件问题_public boolean nextkeyvalue() 如何重写?-CSDN博客

✅HDFS的mapper和reducer的个数如何确定?reducer的个数依据是什么?

Mapper数量

数据切片split只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是 MapReduce 程序计算输入数据的单位,一个切片会对应启动一个MapTask,所以只能通过设定分片的大小间接调整mapper的个数。

影响map个数(split个数)的主要因素有:

  • 文件的大小:当块(dfs.block.size)为128MB时,如果输入文件为128MB,会被划分为1个split;当块为256m,会被划分为2个split。
  • 文件的个数:FileInputFormat按照文件分割split,并且只会分割大文件,即那些大小超过HDFS块的大小的文件。如果HDFS中dfs.block.size设置为128MB,而输入的目录中文件有100个,则划分后的split个数至少为100个。
  • splitSize的大小:分片是按照splitszie的大小进行分割的,一个split的大小在没有设置的情况下,默认等 于hdfs block的大小。
    1、splitSize=max{minSize,min{maxSize,blockSize}}
    2、map数量由处理的数据分成的block数量决定default_num = total_size / split_size

Reducer数量

可以由用户独立设置的,在默认情况下只有一个Reducer。 它的个数既可以使用命令行参数设置(mapreduce.job.reduces=number),也可以在程序中制定(job.setNumReduceTasks(number))。

需要特别注意的是:

■ 如果ReduceTask的数量 > getPartition的结果数
  ● 则会多产生几个空的输出文件
■ 如果ReduceTask的数量 < getPartition的结果数
  ● 会有一部分分区数据无处安放,会报错Exception
■ 如果ReduceTask的数量 = 1 
  ● 不管MapTask端输出多少个分区文件,最终结果都交给这一个ReduceTask,最终也只会产生一个结果文件
■ 分区号是从0开始,逐一累加。

✅HDSF通过那个中间组件去存储数据

DataNode。

流程:Client客户端,负责文件的切分,将文件切分为大小相同的物理块(Block)分布式存储在DataNode中。与NameNode进行交互,获取文件的位置信息,与DataNode进行交互,读取或者写入文件。
 

✅HDFS跨节点怎么进行数据迁移

1)scp 实现两个远程主机之间的文件复制

■ scp -r hello.txt root@hadoop103:/user/atguigu/hello.txt        // 推 push
■ scp -r root@hadoop103:/user/atguigu/hello.txt hello.txt        // 拉 pull
■ scp -r root@hadoop103:/user/atguigu/hello.txt root@hadoop104:/user/atguigu //是通过本地主机中转实现两个远程主机的文件复制;

2)采用 distcp 命令实现两个 Hadoop 集群之间的递归数据复制

■ [atguigu@hadoop102 hadoop-3.1.3]$ bin/hadoop distcp hdfs://hadoop102:9820/user/atguigu/hello.txt hdfs://hadoop105:9820/user/atguigu/hello.txt

参考:一篇文章彻底搞定跨集群跨版本distcp的使用 - 知乎

✅HDFS NameNode高可用如何实现?需要哪些角色?

HDFS的高可用指的是HDFS持续对各类客户端提供读、写服务的能力,因为客户端对HDFS的读、写操作之前都要访问NameNode服务器,客户端只有从NameNode获取元数据之后才能继续进行读、写。所以HDFS的高可用的关键在于NameNode上的元数据持续可用。我们知道2NN的功能是把NameNode的fsimage和edit log做定期融合,融合后传给NameNode,以确保备份到的元数据是最新的,这一点类似于做了一个元数据的快照。Hadoop官方提供了一种quorum journal manager来实现高可用,那么就没必要配置2NN了。在高可用配置下,edit log不再存放在NN,而是存放在一个共享存储的地方,这个共享存储由奇数个Journal Node组成,一般是3个节点(JN小集群), 每个Journal Node专门用于存放来自NameNode的编辑日志,编辑日志由活跃状态的NameNode写入Journal Node小集群。那么要有两个NameNode,而且二者之中只能有一个NameNode处于活跃状态(Active),另一个是待命 状态(Standby),只有Active的NameNode节点才能对外提供读写HDFS服务,也只有Active态的NameNode才能向Journal Node写入编辑日志;Standby状态的NameNode负责从Journal Node小集群中拷贝数据到本地。另外,各个DataNode也要向Active状态的NameNode报告状态(心跳信息、块信息等)。

2个NameNode与3个Journal Node构成的组保持通信,active的NN负责往Journal Node集群写入编辑日志,standby的NN负责观察Journal Node集群中的编辑日志,并且把日志拉取到standby节点,再加上两个NameNode各自的fsimage镜像文件,这样一来就能确保两个NameNode的元数据保持同步。一旦Active NameNode不可用,提前配置的Zookeeper会把Standby节点自动变Active状态,继续对外提供读写服务。

手动实现高可用的大概流程:

准备3台服务器分别用于运行JournalNode进程(也可部署在DateNode上),准备2台NameNode用于运行NameNode进程,Data Node数量不限
分别启动3台JN服务器上的JournalNode进程,分别在date node服务器启动DataNode进程
需要同步2台NameNode之间的元数据。具体做法:从第一台NameNode拷贝元数据到另一台NameNode,然后启动第一台的NameNode进程,再到另一台名称节点上做standby引导启动
把第一台名节点的edit log初始化到JournalNode节点,以供standby状态的NameNode到JournalNode拉取数据,启动standby状态的NN,这样就能同步fsimage文件
模拟故障,检验是否成功实现:手动把active状态的NameNode弄挂掉,正常的话会自动把standby状态的NameNode转变成active。

自动实现通过分布式协调服务者zookeeper来实现。

✅HDFS HA怎么实现?是个什么架构?

在Hadoop2.X之前,NameNode是集群中可能发生单点故障的节点,每个HDFS集群只有一个
NameNode,一旦这个节点不可用,则整个HDFS集群将处于不可用状态。
HDFS高可用(HA)方案就是为了解决上述问题而产生的,在HA HDFS集群中会同时运行两个NameNode,一个作为活动的NameNode(Active),一个作为备份的NameNode(Standby)。备份的NameNode的命名空间与活动的NameNode是实时同步的,所以当活动的NameNode发生故障而停止服务时,备份NameNode可以立即切换为活动状态,而不影响HDFS集群服务。

在一个HA集群中,会配置两个独立的Namenode。在任意时刻,只有一个节点作为活动的节点,另一个节点则处于备份状态。活动的Namenode负责执行所有修改命名空间以及删除备份数据块的操作,而备份的Namenode则执行同步操作,以保持与活动节点命名空间的一致性。
为了使备份节点与活动节点的状态能够同步一致,两个节点都需要同一组独立运行的节点
(JournalNodes,JNS)通信。当Active Namenode执行了修改命名空间的操作时,它会定期将执行的操作记录在editlog中,并写入JNS的多数节点中。而Standby Namenode会一直监听JNS上editlog的变化,如果发现editlog有改动,Standby Namenode就会读取editlog并与当前的命名空间合并。当发生了错误切换时,Standby节点会保证已经从JNS上读取了所有editlog并与命名空间合并,然后才会从Standby状态切换为Active状态。通过这种机制,保证了Active Namenode与Standby Namenode之间命名空间状态的一致性,也就是第一关系链的一致性
为了使错误切换能够很快的执行完毕,就要保证Standby节点也保存了实时的数据快的存储信息,也就是第二关系链。这样发生错误切换时,Standby节点就不需要等待所有的数据节点进行全量数据块汇报,而直接可以切换到Active状态。为了实现这个机制,Datanode会同时向这两个Namenode发送心跳以及块汇报信息。这样就实现了Active Namenode 和Standby Namenode 的元数据就完全一致,一旦发生故障,就可以马上切换,也就是热备。
这里需要注意的是 Standby Namenode只会更新数据块的存储信息,并不会向namenode 发送复制或者删除数据块的指令,这些指令只能由Active namenode发送。
在HA架构中有一个非常重非要的问题,就是需要保证同一时刻只有一个处于Active状态的Namenode, 否则机会出现两个Namenode同时修改命名空间的问,也就是脑裂(Split-brain)。脑裂的HDFS集群很可能造成数据块的丢失,以及向Datanode下发错误的指令等异常情况。
为了预防脑裂的情况,HDFS提供了三个级别的隔离机制(fencing):
1、共享存储隔离:同一时间只允许一个Namenode向JournalNodes写入editlog数据。
2、客户端隔离:同一时间只允许一个Namenode响应客户端的请求。
3、Datanode隔离:同一时间只允许一个Namenode向Datanode下发名字节点指令,例如删除、复制数据块指令等等。
在HA实现中还有一个非常重要的部分就是Active Namenode和Standby Namenode之间如何共享editlog日志文件。Active Namenode会将日志文件写到共享存储上。Standby Namenode会实时的从共享存储读取editlog文件,然后合并到Standby Namenode的命名空间中。这样一旦Active Namenode发生错误, Standby Namenode可以立即切换到Active状态。在Hadoop2.6中,提供了QJM(Quorum Journal Manager)方案来解决HA共享存储问题。

所有的HA实现方案都依赖于一个保存editlog的共享存储,这个存储必须是高可用的,并且能够被集群中所有的Namenode同时访问。Quorum Journal是一个基于Paxos算法的HA设计方案。

Quorum Journal方案中有两个重要的组件。
JournalNode(JN):运行在N台独立的物理机器上,它将editlog文件保存在JournalNode的本地磁盘上,同时JournalNode还对外提供RPC接口QJournalProtocol以执行远程读写editlog文件的功能。
QuorumJournalManager(QJM) :运行在NameNode上,(目前HA集群只有两个Namenode),通过调用RPC接口QJournalProtocol中的方法向JournalNode发送写入、排斥、同步editlog。
Quorum Journal方案依赖于这样一个概念:HDFS集群中有2N+1个JN存储editlog文件,这些editlog 文件是保存在JN的本地磁盘上的。每个JN对QJM暴露QJM接口QJournalProtocol,允许Namenode读写editlog文件。当Namenode向共享存储写入editlog文件时,它会通过QJM向集群中所有的JN发送写editlog 文件请求,当有一半以上的JN返回写操作成功时,即认为写成功。这个原理是基于Paxos算法的。
使用Quorum Journal实现的HA方案有一下优点:
JN进程可以运行在普通的PC上,而无需配置专业的共享存储硬件。
不需要单独实现fencing机制,Quorum Journal模式中内置了fencing功能。
Quorum Journal不存在单点故障,集群中有2N+1个Journal,可以允许有N个Journal Node死亡。JN不会因为其中一个机器的延迟而影响整体的延迟,而且也不会因为JN数量的增多而影响性能
(因为Namenode向JournalNode发送日志是并行的)

✅HDFS的数据一致性靠什么保证?

1、HDFS的NameNode机制
HDFS只有一个NameNode,一旦NameNode出现问题,数据块信息无法寻找。NameNode中的元数据信息在工作时,会将元数据信息缓存在内存中。NameNode将这些内存中的数据备份到磁盘中fsimage,每当有数据进行存入时,元数据信息会被追加到editLog文件中,内存中实际是这两部分的集合。那么什么时候将editlog与fsimage进行合并呢?HDFS引入了SeconderyNameNode这个节点,该节点主要功能就是定期或者等到editlog达到一定数量(可在hdfs_site.xml设置)之后,就会copy NameNode中的editlog和fsimage文件到SeconderyNameNode中,在SeconderyNameNode合并之后再更新NameNode上的fsimage文件。如果一旦NameNode中的数据丢失,SeconderyNameNode中的数据将作为备份,确保元数据信息不会丢失。
2、心跳机制
NameNode与DataNode之间通过心跳(每3秒一次可以在配置文件中设置)信息来确认并更新DataNode的元数据信息。若DataNode发生故障,NameNode就会将取消对该DataNode节点的信任(之后的读写都不 会在该DataNode节点上),同时,NameNode将该节点上的数据进行备份处理。NameNode的备份节点的选择主要依据的是拓扑距离和具体node负载情况。
3、安全模式
HDFS在初始化阶段会进入安全模式,在安全模式下NameNode不允许操作。NameNode会与连接的DataNode进行安全检查,只有当安全的数据块比值达到设定的阈值才会退出安全模式。
4、回滚机制
在HDFS升级或者执行数据写入时,相关的数据将会被保留备份,如果成功,则更新备份,失败,则使用备份信息。
5、安全校验
为了避免网络传输造成的数据错误问题,HDFS采用了校验和机制。各个node之间数据备份和数据读取,校验通过数据备份成功,否则备份失败,重新备份。
6、回收站
当数据文件从HDFS中删除时,文件并没有消失,而是转存/trash目录下。如果误删除的话,可以在该目录下找回文件,文件的存储时间可以配置fs.trash.interval。超过该时间NameNode将该文件的元数据删除,同时DataNode上的文件将会被删除。

✅HDFS怎么保证数据安全

1、存储在HDFS系统上的文件,会分割成128M大小的block存储在不同的节点上,block的副本数默认3份,也可配置成更多份;
2、第一个副本一般放置在与client(客户端)所在的同一节点上(若客户端无datanode,则找一个空闲的、不太满的放), 第二个副本放置到与第一个副本同一机架的不同节点,第三个副本放到不同机架的datanode节点,当取用时遵循就近原则;
3、namonode会把其上面的数据备份到其他一个datanode节点上,保证数据的副本数量;
4、datanode会默认每小时把自己节点上的所有块状态信息报告给namenode;
5、采用safemode模式:datanode会周期性的报告block信息。Namenode会计算block的损坏率,当阀值<0.999f时系统会进入安全模式,HDFS只读不写。
6、HDFS元数据采用secondaryname备份或者HA备份

✅HDFS中向DataNode写数据失败了怎么办?

1、此时,Pipeline数据流管道会被关闭,ACK queue中的packets会被添加到data queue的前面以确保不会发生packets数据包的丢失;
2、在正常的DataNode节点上的以保存好的block的ID版本会升级——这样发生故障的DataNode节点上的block数据会在节点恢复正常后被删除,失效节点也会被从Pipeline中删除;
3、剩下的数据会被写入到Pipeline数据流管道中的其他两个节点中。

✅Hadoop2.xHDFS快照

1、快照介绍和作用
  HDFS snapshot是HDFS整个文件系统,或者某个目录在某个时刻的镜像。该镜像并不会随着源目录的改变而进行动态的更新。可以将快照理解为拍照片时的那一瞬间的投影,过了那个时间之后,又会有新的一个投影。
  HDFS 快照的核心功能包括:数据恢复、数据备份、数据测试。

1.1 数据恢复
  可以通过滚动的方式来对重要的目录进行创建 snapshot 的操作,这样在系统中就存在针对某个目录的多个快照版本。当用户误删除掉某个文件时,可以通过最新的 snapshot 来进行相关的恢复操作。

1.2 数据备份
  可以使用 snapshot 来进行整个集群,或者某些目录、文件的备份。管理员以某个时刻的 snapshot 作为备份的起始结点,然后通过比较不同备份之间差异性,来进行增量备份。

1.3 数据测试
  在某些重要数据上进行测试或者实验,可能会直接将原始的数据破坏掉。可以临时的为用户针对要操作的数据来创建一个 snapshot,然后让用户在对应的 snapshot 上进行相关的实验和测试,从而避免对原始数据的破坏。

2、HDFS快照的实现
  在了解 HDFS 快照功能如何实现之前,首先有一个根本的原则需要记住:快照不是数据的简单拷贝,快照只做差异的记录。这一原则在其他很多系统的快照概念中都是适用的,比如磁盘快照,也是不保存真实数据的。因为不保存实际的数据,所以快照的生成往往非常迅速。
  在 HDFS 中,如果在其中一个目录比如/A下创建一个快照,则快照文件中将会存在与/A目录下完全一致的子目录文件结构以及相应的属性信息,通过命令也能看到快照里面具体的文件内容。但是这并不意味着快照已经对此数据进行完全的拷贝 。这里遵循一个原则:对于大多不变的数据,你所看到的数据其实是当前物理路径所指的内容,而发生变更的inode数据才会被快照额外拷贝,也就是所说的差异拷贝。
  inode 译成中文就是索引节点,它用来存放文件及目录的基本信息,包含时间、名称、拥有者、所在组等信息。
  HDFS 快照不会复制 datanode 中的块,只记录了块列表和文件大小。
  HDFS 快照不会对常规 HDFS 操作产生不利影响,修改记录按逆时针顺序进行,因此可以直接访问当前数据。通过从当前数据中减去修改来计算快照数据。

参考:Hadoop 生态圈(十六)- HDFS Snapshot 快照详解 | 程序园

✅HDFS文件存储的方式?

以Block数据块的方式存储数据。默认一个数据块128M,该数值可以修改。
注意:这里的128仅仅是切分数据的阈值。
一个大的数据被切分成多个小的128M的数据块,分别存储在集群多个节点的不同位置。

过程

  1. 客户端向namenode请求上传文件,namenode检查环境和权限等(目标路径的环境是否已存在)。
  2. namenode返回上传应答。
  3. block上传到哪几个DN节点。
  4. namenode返回3个节点,分别为dn1、dn2、dn3。
  5. 客户端请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
  6. dn1、dn2、dn3逐级应答
  7. 客户端开始往dn1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,dn1收到一个packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待。当一个block传输完成之后,客户端再次请求namenode上传第二个block的服务器。

✅HDFS写数据过程,写的过程中有哪些故障,分别会怎么处理?

写数据过程中,可能的异常模式如下所列:

  • Client 在写入过程中,自己挂了
  • Client 在写入过程中,有DataNode 挂了
  • Client 在写入过程中,NameNode 挂了

对于以上所列的异常模式,都有分别对应的恢复模式。
1、Client 在写入过程中,自己挂了
当 Client 在写入过程中,自己挂了。由于 Client 在写文件之前需要向 NameNode 申请该文件的租约(lease),只有持有租约才允许写入,而且租约需要定期续约。所以当 Client 挂了后租约会超时, HDFS 在超时后会释放该文件的租约并关闭该文件,避免文件一直被这个挂掉的 Client 独占导致其他人不能写入。这个过程称为 lease recovery。
在发起 lease recovery 时,若多个文件 block 副本在多个 DataNodes 上处于不一致的状态,首先需要将其恢复到一致长度的状态。这个过程称为 block recovery。 这个过程只能在 lease recovery 过程中发起。
2、Client 在写入过程中,有 DataNode 挂了
当 Client 在写入过程中,有 DataNode 挂了。写入过程不会立刻终止(如果立刻终止,易用性和可用性都太不友好),取而代之 HDFS 尝试从流水线中摘除挂了的 DataNode 并恢复写入,这个过程称为pipeline recovery。

客户端上传文件时与 DataNode 建立 pipeline 管道,管道正向是客户端向 DataNode 发送的数据包,管道反向是 DataNode 向客户端发送 ack 确认,也就是正确接收到数据包之后发送一个已确认接收到的应答,当 DataNode 突然挂掉了,客户端接收不到这个 DataNode 发送的 ack 确认,客户端会通知NameNode,NameNode 检查该块的副本与规定的不符,NameNode 会通知 DataNode 去复制副本,并将挂掉的 DataNode 作下线处理,不再让它参与文件上传与下载。

3、Client 在写入过程中,NameNode 挂了
当 Client 在写入过程中,NameNode 挂了。这里的前提是已经开始写入了,所以 NameNode 已经完成了对 DataNode 的分配,若一开始 NameNode 就挂了,整个 HDFS 是不可用的所以也无法开始写入。流水线写入过程中,当一个 block 写完后需向 NameNode 报告其状态,这时 NameNode 挂了,状态报告失败,但不影响 DataNode 的流线工作,数据先被保存下来,但最后一步 Client 写完向 NameNode 请求关闭文件时会出错,NN非高可用模式下,挂掉的NN可以通过SNN来提供服务,高可用模式下通过StandbyNN来提供服务。

✅NameNode存数据吗?

1、维护目录树,维护命名空间。
2、负责确定指定的文件块到具体的Datanode结点的映射关系。(在客户端与Datanode之间共享数据)
3、管理Datanode结点的状态报告

✅使用NameNode的好处

NameNode:就是 master,它是一个主管、管理者。
1、管理 HDFS 的名称空间。
2、管理数据块(Block)映射信息
3、副本放置策略
4、处理客户端读写请求。

好处如:数据复制
每个DataNode需要定期向NameNode汇报他的“心跳”,也就是说NameNode需要定期接收这些“心跳”信息,一旦某个DataNode没有向NameNode证明它还“活着”,NameNode就会标记为“死亡”,不再向它请求IO, 这个DataNode上的数据就不能被HDFS使用,那么,NameNode就会启动数据复制,将这个死掉的节点上的数据复制到一个新的节点上。

✅HDFS中DataNode怎么存储数据的

HDFS分布式文件系统也是一个主从架构,主节点是我们的NameNode,负责管理整个集群以及维护集群的元数据信息。
从节点DataNode,主要负责文件数据存储。
1、DataNode工作机制
        一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。DataNode启动后向NameNode注册,通过后,周期性(6小时)的向NameNode上报所有的块信息心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器, 或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用,集群运行中可以安全加入和退出一些机器。
2、数据完整性
1)当DataNode读取block的时候,它会计算checksum
2)如果计算后的checksum,与block创建时值不一样,说明block已经损坏
3)client读取其他DataNode上的block
4)datanode在其文件创建后周期验证checksum

✅直接将数据文件上传到HDFS的表目录中,如何在表中查询到该数据?

在Hive中创建表并将HDFS中的数据导入Hive,然后再进行查询就行得到结果。

参考:大数据(HDFS)面试题及答案_牛客网

hadoop生态圈面试精华之HDFS部分_blockpoolid-CSDN博客

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

话数Science

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值