在《HBase查询优化》一文中,介绍了基于HBase层面的读取优化。因为HBase的实际数据是以HFile的形式,存储在HDFS上。那么,HDFS层面也有它本身的优化点,即:Short-Circuit Local Reads。本篇博客笔者将从HDFS层面来进行优化,从而间接的提高HBase的查询性能。
html
Hadoop系统在设计之初,遵循一个原则,那就是移动计算的代价比移动数据要小。故Hadoop在作计算的时候,一般是在本地节点上的数据中进行计算。即计算和数据本地化。流程以下图所示:linux
在最开始的时候,短回路本地化读取和跨节点的读取的处理方式是同样的,流程都是先从DataNode读取数据,而后经过RPC服务把数据传输给DFSClient,这样处理虽然流程比较简单,可是读取性能会受到影响,由于跨节点读取数据,须要通过网络将一个DataNode的数据传输到另一个DataNode节点(通常来讲,HDFS有3个副本,因此,本地取不到数据,会到其余DataNode节点去取数据)。缓存
短回路本地化读取的核心思想是,因为客户端和数据在同一个节点上,因此DataNode不须要在数据路径中。相反,客户端自己能够简单地读取来自本地磁盘的数据。这种性能优化集成在CDH的Hadoop相关项目中,实现以下图所示:安全
这种短回路本地化读取的思路虽然很好,可是配置问题比较麻烦。系统管理员必须更改DataNode数据目录的权限,以便客户端有权限可以打开相关文件。这样就不得不专门为那些可以使用短回路本地化读取的用户提供白名单,不容许其余用户使用。一般,这些用也必须被放置在一个特殊的UNIX组中。性能优化
另外,这种本地化短回路读取的思路还存在另一个安全问题,客户端在读取DataNode数据目录时打开了一些权限,这样意味着,拥有这个目录的权限,那么其目录下的子目录中的数据也能够被访问,好比HBase用户。因为存在这种安全风险,因此这个实现思路已经不建议使用了。网络
为了解决上述问题,在实际读取中须要很是当心的选择文件。在UNIX中有这样一种机制,叫作“文件描述符传递”。使用这种机制来实现安全的短回路本地读取,而不是经过目录名称的客户端,DataNode打开Block文件和元数据文件,将它们直接给客户端。由于文件描述符是只读的,用户不能修改文件。因为它没有进入Block目录自己,它没法读取任何不该该访问的目录。app
举个例子:dom
现有两个用户hbase1和hbase2,hbase1拥有访问HDFS目录上/appdata/hbase1文件的权限,而hbase2用户没有改权限,可是hbase2用户又须要访问这个文件,那么能够借助这种“文件描述符传递”的机制,可让hbase1用户打开文件获得一个文件描述符,而后把文件描述符传递给hbase2用户,那么hbase2用户就能够读取文件里面的内容了,即便hbase2用户没有权限。这种关系映射到HDFS中,能够把DataNode看做hbase1用户,客户端DFSClient看做hbase2用户,须要读取的文件就是DataNode目录中的/appdata/hbase1文件。实现以下图所示:socket
HDFS客户端可能会有常常读取相同Block文件的场景,为了提高这种读取性能,旧的短回路本地读取实现具备Block路径的高速缓存。该缓存容许客户端从新打开其最近已读取的Block文件,而不须要再去访问DataNode路径读取。oop
新的短回路本地读取实现不是一个路径缓存,而是一个名为FileInputStreamCache的文件描述符缓存。这样比路径缓存要更好一些,由于它不须要客户端从新打开文件来从新读取Block,这种读取方式比就的短回路本地读取方式在读性能上有更好的表现。
缓存的大小能够经过dfs.client.read.shortcircuit.streams.cache.size属性来进行调整,默认是256,缓存超时能够经过dfs.client.read.shortcircuit.streams.cache.expiry.ms属性来进行控制,默认是300000,也能够将其设置为0来将其进行关闭,这两个属性均在hdfs-site.xml文件中能够配置。
为了配置短回路本地化读取,须要启用libhadoop.so,通常来讲所使用Hadoop一般都是包含这些包的,能够经过如下命令来检测是否有安装:
$ hadoop checknative -a Native library checking: hadoop: true /home/ozawa/hadoop/lib/native/libhadoop.so.1.0.0 zlib: true /lib/x86_64-linux-gnu/libz.so.1 snappy: true /usr/lib/libsnappy.so.1 lz4: true revision:99 bzip2: false
短回路本地化读取利用UNIX的域套接字(UNIX domain socket),它在文件系统中有一个特定的路径,容许客户端和DataNode进行通讯。在使用的时候须要设置这个路径到Socket中,同时DataNode须要可以建立这个路径。另外,这个路径应该不可能被除了hdfs用户或root用户以外的任何用户建立。所以,在实际建立时,一般会使用/var/run或者/var/lib路径。
短回路本地化读取在DataNode和客户端都须要配置,配置以下:
<configuration>
<property>
<name>dfs.client.read.shortcircuit</name>
<value>true</value>
</property>
<property>
<name>dfs.domain.socket.path</name>
<value>/var/lib/hadoop-hdfs/dn_socket</value>
</property>
</configuration>
其中,配置dfs.client.read.shortcircuit属性是打开这个功能的开关,dfs.domain.socket.path属性是DataNode和客户端之间进行通讯的Socket路径地址,核心指标配置参数以下:
属性 | 描述 |
dfs.client.read.shortcircuit | 打开短回路本地化读取,默认false |
dfs.client.read.shortcircuit.skip.checksum | 若是配置这个参数,短回路本地化读取将会跳过checksum,默认false |
dfs.client.read.shortcircuit.streams.cache.size | 客户端维护一个最近打开文件的描述符缓存,默认256 |
dfs.domain.socket.path | DataNode和客户端DFSClient通讯的Socket地址 |
dfs.client.read.shortcircuit.streams.cache.expiry.ms | 设置超时时间,用来设置文件描述符能够被放进FileInputStreamCache的最小时间 |
dfs.client.domain.socket.data.traffic | 经过UNIX域套接字传输正常的数据流量,默认false |
短回路本地化读取可以从HDFS层面来提高读取性能,若是HBase场景中,有涉及到读多写少的场景,在除了从HBase服务端和客户端层面优化外,还能够尝试从HDFS层面来进行优化。
这篇博客就和你们分享到这里,若是你们在研究学习的过程中有什么问题,能够加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!
另外,博主出书了《Hadoop大数据挖掘从入门到进阶实战》,喜欢的朋友或同窗, 能够在公告栏那里点击购买连接购买博主的书进行学习,在此感谢你们的支持。