以下内容均翻译自官方文档.
概述
HDFS的集中缓存管理是一种显式的缓存机制,允许用户指定HDFS缓存的路径。NameNode将与磁盘上具有所需块的DataNode通信,并指示它们将块缓存在堆外缓存中。
在HDFS中集中管理缓存具有许多显著的优点:
显式固定可防止经常使用的数据从内存中被淘汰。当工作集的大小超过主内存的大小时,这一点
尤为重要,而主内存对于许多HDFS工作负载来说都是常见的。由于DataNode缓存由NameNode管理,因此应用程序可以在做出任务放置决策时查询缓存块位
置集。将任务与缓存的块副本联合定位可以提高读取性能。当块已经被DataNode缓存时,客户端可以使用一个新的、更高效的零拷贝读取API。由于缓存
数据的校验和验证由DataNode完成一次,因此客户端在使用这个新API时基本上可以产生零开
销。集中式缓存可以提高集群整体内存利用率。当依赖每个DataNode上的OS缓冲区缓存时,重复读
取块将导致该块的所有n个副本被拉入缓冲区缓存。通过集中式缓存管理,用户可以显式地锁定
n个副本中的m个副本,从而节省n-m个内存。
使用案例
集中缓存管理对于重复访问的文件很有用。例如, Hive中经常用于连接的小事实表是缓存的好选择。另一方面,缓存一年报告查询的输入可能不太有用,因为历史数据可能只读取一次。
集中式缓存管理对于具有性能SLA的混合工作负载也很有用。缓存高优先级工作负载的工作集可确保它不会与低优先级工作负载竞争磁盘I/O。
架构
在此架构中,NameNode负责协调集群中所有的DataNode堆外缓存。NameNode周期性地从每个DataNode接收缓存报告,报告描述了在给定DN上缓存的所有块。NameNode通过在DataNode心跳上附带cache和uncache命令来管理DataNode缓存。
NameNode查询其缓存指令集,以确定应该缓存哪些路径。缓存指令持久存储在fsimage和edits log中,可以通过Java和命令行API添加、删除和修改缓存指令。NameNode还存储一组缓存池,这些缓存池是管理实体,用于将缓存指令分组在一起,以便进行资源管理和强制执行权限。
NameNode定期重新扫描命名空间和活动缓存指令,以确定哪些块需要缓存或去缓存,并将缓存工作分配给DataNode。重新扫描也可以由用户操作触发,如添加或删除缓存指令或删除缓存池。
我们目前不缓存正在构建、损坏或不完整的块。如果缓存指令覆盖了symlink,则不缓存symlink目标。
缓存当前是在文件或目录级别完成的。块和子块缓存是未来工作的一项内容。
概念
缓存指令
缓存指令定义了应该缓存的路径。路径可以是目录也可以是文件。目录将以非递归方式缓存,仅表示目录的第一级列表中的文件。
指令还指定附加参数,如高速缓存复制因子和过期时间。复制因子指定要缓存的块副本数。如果多个缓存指令引用同一个文件,则应用最大缓存复制因子。
到期时间在命令行上指定为生存时间(TTL),即将来的相对到期时间。当缓存指令过期后,NameNode在作出缓存决策时将不再考虑该指令。
缓存池
缓存池是用于管理缓存指令组的管理实体。缓存池具有类似于UNIX的权限,这些权限限制了哪些用户和组可以访问该池。写入权限允许用户向池添加和删除缓存指令。读取权限允许用户列出池中的缓存指令以及其它元数据。执行权限未使用。
缓存池也用于资源管理。池可以强制执行最大限制,该限制限制通过池中的指令可以聚合缓存的字节数。通常,池限制的总和大约等于集群上为HDFS缓存保留的聚合内存量。缓存池还跟踪一些统计信息,以帮助集群用户确定哪些缓存,哪些缓存应该缓存。
池还可以强制执行最长生存时间。这限制了添加到池的指令的最大过期时间。
cacheadmin 命令行接口
在命令行上,管理员和用户可以通过hdfs cacheadmin子命令与缓存池和指令交互。
高速缓存指令由唯一、不重复的64位整数ID标识。即使随后删除了缓存指令, ID也不会被重用。
缓存池由唯一的字符串名称标识。
缓存指令命令
addDirective
用法: hdfs cacheadmin -addDirective -path -pool [-force] [-replication ] [-ttl ]
添加新的缓存指令。
参数 | 说明 |
---|---|
path | 缓存路径。路径可以是目录或文件 |
pool-name | 指令将被添加到的池。为了添加新指令,必须对缓存池具有write 权限 |
-force | 跳过对缓存池资源限制的检查 |
replication | 要使用的缓存复制因子。默认为1 |
time-to-live | 指令的有效时间。可指定分钟、小时和天,例如30m、4h、2d。有效单位为[smhd]。“never”表示一个指令永远不会过期。如果未指定,指令永远不会过期 |
removeDirective
用法:hdfs cacheadmin -removeDirective
删除缓存指令。
参数 | 说明 |
---|---|
id | 要删除的缓存指令的id。你必须对指令池有写权限才能删除它。要查看cachedirective id列表,请使用-listDirectives命令。 |
removeDirectives
用法:hdfs cacheadmin -removeDirectives
删除具有指定路径的每个缓存指令。
参数 | 说明 |
---|---|
path | 要删除的缓存指令的路径。你必须对指令池有写权限才能删除它。要查看缓存指令列表,请使用-listDirectives命令。 |
listDirectives
用法:hdfs cacheadmin -listDirectives [-stats] [-path ] [-pool ]
列出缓存指令。
参数 | 说明 |
---|---|
path | 仅列出具有此路径的缓存指令。注意,如果缓存池中有一个路径的缓存指令我们没有读访问权限,那么它将不会被列出。 |
pool | 仅列出该池中的路径缓存指令。 |
-stats | 列出基于路径的缓存指令统计信息。 |
缓存池命令
addPool
用法:hdfs cacheadmin -addPool [-owner ] [-group ] [-mode ] [-limit ] [-maxTtl ]
添加一个新的缓存池。
参数 | 说明 |
---|---|
name | 新池的名称。 |
owner | 池所有者的用户名。默认为当前用户。 |
group | 池的组。默认为当前用户的主组名。 |
mode | 池的unix样式的权限。权限以八进制方式指定,例如0755。默认情况下,它被设置为0755。 |
limit | 此池中指令可以缓存的最大字节数。默认情况下,没有设置限制。 |
maxTtl | 添加到池中的指令允许的最大生存时间。这可以用秒、分、小时和天来指定,例如120秒、30米、4h、2d。有效单位为[smhd]。默认情况下,没有设置最大值。值“never”表示没有限制。 |
modifyPool
用法:hdfs cacheadmin -modifyPool [-owner ] [-group ] [-mode ] [-limit ] [-maxTtl ]
修改缓存池的元数据。
参数 | 说明 |
---|---|
name | 要修改的池的名称。 |
owner | 池所有者的用户名。 |
group | 池的组的Groupname。 |
mode | 池的unix样式的八进制权限。 |
limit | 此池可缓存的最大字节数。 |
maxTtl | 添加到池中的指令允许的最大生存时间。 |
removePool
用法:hdfs cacheadmin -removePool
删除一个缓存池,这也解除与池关联的路径的缓存。
参数 | 说明 |
---|---|
name | 要删除的缓存池的名称。 |
listPools
用法:hdfs cacheadmin -listPools [-stats] []
显示有关一个或多个缓存池的信息,例如名称、所有者、组、权限等。
参数 | 说明 |
---|---|
-stats | 显示额外的缓存池统计信息。 |
name | 如果指定,则仅列出命名的缓存池。 |
help
用法:hdfs cacheadmin -help
显示命令的详情。
参数 | 说明 |
---|---|
command-name | 要获得详细帮助的命令。如果没有指定任何命令,则打印所有命令的详细帮助。 |
配置
本地库
为了将块文件锁定到内存中, DataNode依赖于libhadoop.so或Windows上的hadoop.dll中的本机JNI代码。使用HDFS集中缓存管理时,请务必启用JNI。
配置属性
必选
请务必配置以下内容:
dfs.datanode.max.locked.memory
这决定了DataNode用于缓存的最大内存量。在类Unix系统上,DataNode用户的ulimit (ulimit -l)
也需要增大以匹配此参数(见下面关于操作系统限制的部分)。设置该值时,请记住,在内存中
还需要空间用于其它事情,例如DataNode和应用程序JVM堆以及操作系统页面缓存。此设置与“延迟持久性写入”功能共享。数据节点将确保延迟持久写入和集中缓存管理所使用的组
合内存不超过dfs.datanode.max.locked.memory中配置的内存量。
可选
以下属性不是必需的,但可以指定用于调优:
dfs.namenode.path.based.cache.refresh.interval.ms
NameNode将使用这个值作为后续路径缓存重新扫描之间的毫秒数。这将计算要缓存的块和每个
DataNode包含应该缓存的块的副本。默认值为30000,即30秒。
dfs.datanode.fsdatasetcache.max.threads.per.volume
DataNode将使用这个值作为每个卷用于缓存新数据的最大线程数。
值为4。
dfs.cachereport.intervalMsec
DataNode将使用这个值作为向NameNode发送其缓存状态的完整报告之间的毫秒数。默认值为10000,即10秒。
dfs.namenode.path.based.cache.block.map.allocation.percent
我们将分配给缓存块映射的Java堆的百分比。缓存的块映射是使用链散列的散列映射。如果缓存
的块数较多,访问较小的映射可能会较慢;较大的映射将消耗更多的内存。默认值为0.25%。dfs.namenode.caching.enabled
此参数用于设置NameNode中集中缓存的开关。当集中缓存被禁用时,NameNode将不会处理
缓存报告,也不会在集群上存储有关块缓存位置的信息。请注意, NameNode将继续在文件系统
元数据中存储基于路径的缓存位置,即使它不会在缓存启用之前处理这些信息。此参数的默认值
是true (即。已启用集中缓存)。
操作系统限制
如果出现“无法启动datanode”错误,因为配置的最大锁定内存大小…大于datanode的可用RLIMIT_MEMLOCK ulimit。这意味着操作系统对可锁定的内存量施加了低于您所配置的内存量的限制。要解决此问题,必须调整DataNode运行的ulimit -l值。一般在/etc/security/limits.conf中配置。但是,它会因您使用的操作系统和发行版而异。
当您能够从shell中运行ulimit -l并返回一个比您使用dfs.datanode.max.locked.memory配置的值更高的值或字符串unlimited时,您就会知道您正确配置了这个值。?表示没有限制。请注意, ulimit -l通常以KB为单位输出内存锁限制,但是dfs.datanode.max.locked.memory必须以字节为单位指定。
此信息不适用于Windows上的部署。Windows没有直接等价于ulimit -l。