内容简介:本文介绍了HDFS的短路读演进、安全的短路读以及小米在安全短路读的优化。上篇文章回顾:了解了HDFS短路读的演进,我们来看下HDFS是如何实现安全短路读的。DataNode将短路读副本的文件描述符传给DFSClient,DFSClient缓存副本文件描述符。由于副本的状态可能随时发生改变,所以需要DFSClient和DataNode实时同步副本状态。同时,DFSClient和DataNode在同一台机器上,共享内存可以通过POSIX提供的 mmap接口实现将文件映射到内存,并且映射数据是实时同步的(如图4)
本文介绍了HDFS的短路读演进、安全的短路读以及小米在安全短路读的优化。
上篇文章回顾: HDFS Decommission问题分析
背景
Hadoop的一个重要思想就是移动计算,而不是移动数据。我们更愿意尽可能将计算移动到数据所在节点。因此,HDFS中经常出现客户端和数据在一个节点上,当客户端读取一个数据块时,就会出现本地读取。例如HBase场景,ResionServer写数据一般在HDFS中都会存储三备份副本并且肯定会往本地节点写一备份,当ResionServer读取该数据时也会优先选择同一节点的数据进行读取。
短路读的演进
1、网络读
最初,HDFS中本地读取的处理方式和远程读取相同,也是通过网络读来实现。客户端通过TCP套接字连接到DataNode,并通过DataTransferProtocol协议传输数据(如下图):
这种方式简单,但有明显的问题:
DataNode必须为每个正在读取数据块的客户端保留一个线程和一个TCP套接字。内核中会有TCP协议的开销,以及DataTransferProtocol协议的开销,因此这里有很大的优化空间。
2、HDFS-2246 不安全短路读
短路读关键思想是因为客户端和数据块在同一节点上,所以DataNode不需要出现在读取数据路径中。而客户端本身可以直接从本地磁盘读取数据。这样会使读取性能得到很大的提高。在HDFS-2246中实现的短路读是DataNode将所有数据块路径的权限开放给客户端,客户端直接通过本地磁盘路径来读取数据,见下图:
但这种方式引入了很多问题:
(1)系统管理员必须更改DataNode数据目录的权限,以允许客户端打开相关文件。将能够使用短路读的用户专门列入白名单,不允许其他用户使用。通常,这些用户也必须放在特殊的Unix组中。
(2)这些权限更改会引入了一个安全漏洞,具有读取DataNode节点上数据块文件权限的用户可以任意读取路径上所有数据块,而不仅仅是他们所需访问的数据块,这好像让用户变成了超级用户,对于少数用户来说可能是可以接受的,例如HBase用户。但总的来说,它会带来很大安全隐患。
3、HDFS-347 安全短路读
HDFS-2246的主要问题是向客户端打开了DataNode的数据目录,而我们真正需要读取的只是一部分数据块文件。Unix有一种机制是可以做到这一点,称为文件描述符传递。HDFS-347使用这种机制来实现安全短路读。DataNode不是将目录传递给客户端,而是打开块文件和元数据文件,并将它们的文件描述符通过domain socket传递给客户端(如图3):
基于以下两方面,安全短路读解决了HDFS-2246存在的安全性问题。
(1)文件描述符是只读的,因此客户端无法修改传递描述符的文件。
(2)客户端无法访问数据块目录本身,所以也无法读取它不应该访问的任何其他数据块文件。
HDFS安全短路读
1、短路读共享内存
了解了HDFS短路读的演进,我们来看下HDFS是如何实现安全短路读的。DataNode将短路读副本的文件描述符传给DFSClient,DFSClient缓存副本文件描述符。由于副本的状态可能随时发生改变,所以需要DFSClient和DataNode实时同步副本状态。同时,DFSClient和DataNode在同一台机器上,共享内存可以通过POSIX提供的 mmap接口实现将文件映射到内存,并且映射数据是实时同步的(如图4),所以共享内存可以维护所有短路读副本的状态,使得DFSClient和DataNode通过共享内存来实时同步副本信息。
共享内存会有很多槽位,每个槽位对应一个短路读副本的信息。共享内存保存了所有槽位的二进制信息。但是映射数据中的二进制槽位信息不便于管理,所以定义了Slot对象操作映射数据中的一个槽位,如下图:
Slot槽位大小是64字节,槽位数据格式,前4字节是Slot标志位,5到8字节是锚计数位,剩余字节保留将来使用,例如统计信息等。
两个标志位:
(1)VALID_FLAG:表示槽位是否有效。
DFSClient在共享内存中分配新的槽位时设置此标志位。当与此槽位关联的副本不再有效时,DataNode将会消除此标志位。DFSClient本身也会消除此槽位,认为DataNode不再使用此槽位进行通信。
(2)ANCHORABLE_FLAG:表示槽位对应的副本是否已经缓存。
DataNode将槽位对应的副本通过POSIX提供的mlock接口缓存时会设置该标志位。当标志位已设置,DFSClient短路读取该副本时不再需要进行校验,因为副本缓存时已经做了检验操作,并且这种副本还支持零拷贝读取。DFSClient对这样的副本进行读取时,需要在对应的槽位锚计数加1,只有当槽位的锚计数为0时,DataNode才可以从缓存中删除此副本。
共享内存段的最大是8192字节,当DFSClient进行大量短路读时, DFSClient和DataNode之间可能会有多段共享内存。HDFS中DFSClient定义了DFSClientShm类抽象了DFSClient端一段共享内存,DFSClientShmManager类管理所有的DFSClientShm,而DataNode端定义了RegisteredShm类抽象DataNode端的一段共享内存,ShortCircuitRegistry类管理所有DataNode端的共享内存,如下图所示:
在安全短路读中,DFSClient和DataNode是通过domain socket来同步共享内存槽位信息的。
-
DFSClient申请一段共享内存保存短路读副本的状态。DataNode会创建共享内存,并将共享内存文件映射到DataNode内存中,并创建RegisteredShm管理这段共享内存,之后会将共享内存文件的文件描述符通过domain socket返回给DFSClient。
-
DFSClient根据文件描述符打开共享内存文件,将该文件映射到DFSClient的内存中,并创建DfsClientShm对象管理这段共享内存。
-
DFSClient通过domain socket向DataNode申请数据块文件以及元数据文件的文件描述符,并且同步共享内存中slot槽位的状态。DFSClient会在DfsClientShm管理的共享内存中为数据块申请一个slot槽位,之后通过domain socket向DataNode同步信息,DataNode会在RegisteredShm管理的共享内存中创建相应的slot槽位,然后获取数据块文件以及元数据文件的文件描述符,并通过domain socket发送给DFSClient,见下图:
2、短路读流程
当客户端执行数据块副本短路读时,DFSClient与DataNode的交互过程如下:
(1)DFSClient通过requestShortCircuitShm()接口向DataNode请求创建共享内存,DataNode创建共享内存文件并将共享内存文件描述符返回给DFSClient。
(2)DFSClient通过allocShmSlot()接口申请共享内存中的槽位,并通过requestShortCircuitFds()接口向DataNode请求要读取的副本文件描述符,DataNode打开副本文件并将数据块文件和元数据文件的文件描述符返回给DFSClient。
(3)DFSClient读取完副本后,异步通过releaseShortCircuitFds()接口向DataNode请求释放文件描述符及相应槽位。
小米对HDFS安全短路读的优化
1、Slot槽位释放缓慢
几次压力场景中,我们发现Hbase ResionServer多个短路读线程经常会阻塞在domain socket的读写上。从DataNode 的dump中发现大量的用于短路读的ShortCircuitShm。于是我们通过YCSB模拟线上的情况,发现短路读请求量较大时,BlockReaderLocal分配的QPS很高,并且BlockReaderLocal的分配依赖于同步读取块的ShortCircuitShm和slot的分配。
通过统计slot分配和释放的QPS,我们发现slot分配的QPS能达到3000+,而释放的QPS只能达到1000+,并且在YCSB测试经过约1小时,DataNode出现FULL GC。由此可看出,DataNode中积累的,来不及释放的slot,是导致GC的主要有原因。
现在的短路读实现中,每次释放slot,都会新建一个domain socket连接。而DataNode对于每个新建立的domain socket 连接,都会重新初始化一个DataXceiver去处理这个请求。通过profile DataNode发现,SlotReleaser线程花了大量的时间在建立和清理这些连接上。
于是,我们对SlotReleaser的domain socket连接进行了复用。通过复用domain socket,在同样的测试集上,slot 释放的QPS能和分配的QPS达到一致。从而消除了过期slot在DataNode中的挤压。同时,由于DataNode Young GC减少,YCSB的GET的QPS也提升了约20%左右。
2、共享内存分配效率低
在profile HBase短路读过程中,我们还发现另外一个问题,就是每隔一段时间,会有一批读会有约200ms左右的延迟而且这些延迟几乎同时出现。开始我们怀疑 Hbase ResionServer的Minor GC导致。但通过比对ResionServer的GC日志,发现时间并不完全匹配。有一部分却和DataNode Minor GC时间吻合。数据块副本的真正读取操作,是完全不通过DataNode的,如果是DataNode的影响,那问题只能出在ResionServer和DataNode建立短路读时的交互上。通过进一步在短路读过程中加trace log,我们发现这些延迟,是由于DataNode Minor GC导致ShortCircuitShm分配请求被阻塞。当分配一个ShortCircuitShm时,会导致很多slot的分配阻塞。slot的分配延迟,又会引起BlockReaderLocal的延迟,从而导致短路读的延迟。这就是之前发现有一批读,总是同时报相近的延迟。 为了解决这个问题,我们对ShortCircuitShm进行了预分配,以减轻DataNode Minor GC对短路度影响,使得延迟更为平滑。
3、短禁止正在构建块的短路读
禁止了正在构建块进行短路读,也就是最后一个块禁止短路读。这个问题由于HBase的读写模式 ,对其影响不是很大,但对基于HDFS流式服务影响很大。我们正在做的优化工作主要是通过保证短路读只发生在flush操作之后,同时在读取过程中检查块的有效性,处理读异常,如果确实失败,转成远程读的方式。
附录
(1)文件描述符(File Descriptor)
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
Unix和Windows系统都允许在进程间传递文件描述符。一个进程打开一个文件,然后把该文件的文件描述符传递给另一个进程,另一个进程可以访问该文件。文件描述符传递对于安全性是非常有用的,因为它消除了对第二个进程需要拥有足够访问权限来打开文件限制,同时文件描述符是只读的,所以该方式还可以防止有问题的程序或者恶意的客户端损坏文件。在Unix系统上,文件描述符传递只能通过Unix domain socket完成。
(2)Unix domain socket
Unix domain socket是用于在同一台主机操作系统上执行的进程间交换数据的通信端点。有效的Unix domain socket类型是SOCK_STREAM(用于面向流的套接字)和SOCK_DGRAM(用于保留消息边界的面向数据报的套接字),与大多数Unix实现一样,Unix domain datagram socket始终可靠且不重新 排序 的数据报。Unix domain socket是POSIX操作系统的标准组件。
Unix domain socket的API类似于网络socket,但是不使用底层网络协议,所有通信都完全在操作系统内核中进行。Unix domain socket使用文件系统作为其地址名称空间。进程引用Unix domain socket作为文件系统inode,因此两个进程可以通过打开相同的socket进行通信。 除了发送数据外,进程还可以使用sendmsg()和recvmsg()系统调用在Unix domain socket连接上发送文件描述符。并且只有发送进程授权给接收进程,接收进程才可以访问文件描述符的权限。
(3)共享内存(Shared Memory)
共享内存是进程间通信的方法,即在同时运行的程序之间交换数据的方法。一个进程将在RAM中创建一个其他进程可以访问的区域。由于两个进程可以像访问自身内存一样访问共享内存区域,因此是一种非常快速的通信方式。但是它的扩展性较差,例如通信必须在同一台机器上运行。而且必须要避免如果共享内存的进程在不同的CPU上运行,并且底层架构不是缓存一致的。
POSIX提供了使用共享内存的POSIX标准化API。使用sys/mman.h中的函数shm_open。POSIX进程间通信包含共享函数shmat,shmctl,shmdt和shmget。shm_open创建的共享内存是持久化的。它一直保留在系统中,直到被进程明确删除。这有一个缺点,如果进程崩溃并且无法清理共享内存,它将一直保持到系统关闭。POSIX还提供了用于将文件映射到内存的mmap API,可以共享映射,允许将文件的内容用作共享内存。
引用
1. https://en.wikipedia.org/wiki/File_descriptor
2. https://en.wikipedia.org/wiki/Unix_domain_socket
3. https://en.wikipedia.org/wiki/Shared_memory
4.https://www.tutorialspoint.com/inter_process_communication/inter_process_communication_shared_memory.htm
5. https://blog.cloudera.com/blog/2013/08/how-improved-short-circuit-local-reads-bring-better-performance-and-security-to-hadoop/
本文首发于“小米云技术”,点击查看原文。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 动态规划之最短路径和
- 单源最短路径:Dijkstra算法(堆优化)
- 漫画:如何求图的最短路径? | 技术头条
- 漫画:数据结构之最短路径 Dijkstra 算法的优化 | 技术头条
- 【你该懂一点Javascript算法系列】之单源最短路径 - Dijkstra算法
- ArangoDB 3.5:流事务 API、蒙面数据、搜索性能大幅提升、最短路径功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Data Structures and Algorithms
Alfred V. Aho、Jeffrey D. Ullman、John E. Hopcroft / Addison Wesley / 1983-1-11 / USD 74.20
The authors' treatment of data structures in Data Structures and Algorithms is unified by an informal notion of "abstract data types," allowing readers to compare different implementations of the same......一起来看看 《Data Structures and Algorithms》 这本书的介绍吧!