HDFS中的集中式缓存管理是一种显式的缓存机制,允许用户指定HDFS缓存的路径。NameNode将与在磁盘上具有所需块的DataNode通信,并指示它们将这些块缓存在堆外缓存中。
HDFS中的集中式缓存管理具有许多重要的优势。
显式固定可防止将常用数据从内存中逐出。当工作集的大小超过主内存的大小时,这尤其重要,这对于许多HDFS工作负载来说是很常见的。
由于DataNode缓存由NameNode管理,因此应用程序可以在制定任务放置决策时查询缓存块位置的集合。将任务与缓存的块副本共置一处可提高读取性能。
当块已由DataNode缓存后,客户端可以使用新的,效率更高的零拷贝读取API。由于缓存数据的校验和验证是由DataNode一次完成的,因此使用此新API时,客户端实际上可以产生零开销。
集中式缓存可以提高总体群集内存利用率。当依赖每个DataNode上的OS缓冲区高速缓存时,重复读取一个块将导致该块的所有n个副本都被拉到缓冲区高速缓存中。借助集中式高速缓存管理,用户可以明确地引脚只米的的ñ副本,节省了纳米内存。
集中式缓存管理对于重复访问的文件很有用。例如,Hive中的一个小的事实表(通常用于联接)是缓存的不错选择。另一方面,缓存一年报告查询的输入可能不太有用,因为历史数据可能只读取一次。
集中式缓存管理对于具有性能SLA的混合工作负载也很有用。缓存高优先级工作负载的工作集可确保它不与低优先级工作负载的磁盘I / O竞争。
在这种体系结构中,NameNode负责协调群集中的所有DataNode堆外缓存。NameNode定期从每个DataNode 接收缓存报告,该报告描述了在给定DN上缓存的所有块。NameNode通过piggy带DataNode心跳上的缓存和取消缓存命令来管理DataNode缓存。
NameNode查询其缓存指令集,以确定应该缓存哪些路径。缓存指令永久存储在fsimage和编辑日志中,并且可以通过Java和命令行API进行添加,删除和修改。NameNode还存储一组缓存池,这是用于将缓存指令组合在一起以进行资源管理和强制执行权限的管理实体。
NameNode定期重新扫描名称空间和活动缓存指令,以确定哪些块需要缓存或取消缓存,并将缓存工作分配给DataNodes。还可以通过用户操作(例如添加或删除缓存指令或删除缓存池)来触发重新扫描。
我们目前不缓存正在构造,损坏或不完整的块。如果缓存指令涵盖了符号链接,则不会缓存符号链接目标。
缓存当前在文件或目录级别完成。块和子块缓存是将来的工作。
在命令行上,管理员和用户可以通过hdfs cacheadmin子命令与缓存池和指令进行交互。
高速缓存指令由唯一的,非重复的64位整数ID标识。即使以后删除了缓存指令,ID也不会被重用。
高速缓存池由唯一的字符串名称标识。
用法:hdfs cacheadmin -addDirective -path <路径> -pool <池名称> [-force] [-复制<复制>] [-ttl <生存时间>]
添加一个新的缓存指令。
<路径> | 缓存路径。路径可以是目录或文件。 |
<池名称> | 指令将添加到的池。您必须对高速缓存池具有写权限才能添加新指令。 |
-力 | 跳过对缓存池资源限制的检查。 |
<复制> | 要使用的缓存复制因子。默认为1。 |
<生存时间> | 指令有效的时间。可以以分钟,小时和天为单位指定,例如30m,4h,2d。有效单位为[smhd]。“从不”表示永不过期的指令。如果未指定,则该指令永不过期。 |
用法:hdfs cacheadmin -removeDirective <id>
删除缓存指令。
<id> | 要删除的缓存指令的ID。您必须对指令池具有写许可权才能将其删除。要查看高速缓存指令ID的列表,请使用-listDirectives命令。 |
用法:hdfs cacheadmin -addPool <名称> [-所有者<所有者>] [-组<组>] [-模式<模式>] [-限制<限制>] [-maxTtl <maxTtl>]
添加一个新的缓存池。
<名称> | 新池的名称。 |
<所有者> | 池所有者的用户名。默认为当前用户。 |
<组> | 池组。默认为当前用户的主要组名。 |
<模式> | 池的UNIX样式权限。权限以八进制指定,例如0755。默认情况下,此设置为0755。 |
<限制> | 总的来说,此池中的指令可以缓存的最大字节数。默认情况下,未设置限制。 |
<maxTtl> | 将指令添加到池中的最大允许生存时间。可以以秒,分钟,小时和天为单位指定,例如120s,30m,4h,2d。有效单位为[smhd]。默认情况下,未设置最大值。值“从不”表示没有限制。 |
用法:hdfs cacheadmin -modifyPool <名称> [-所有者<所有者>] [-组<组>] [-模式<模式>] [-限制<限制>] [-maxTtl <maxTtl>]
修改现有缓存池的元数据。
<名称> | 要修改的池的名称。 |
<所有者> | 池所有者的用户名。 |
<组> | 池组的组名。 |
<模式> | 八进制的池的Unix样式权限。 |
<限制> | 此池可以缓存的最大字节数。 |
<maxTtl> | 将指令添加到池中的最大允许生存时间。 |
为了将阻止文件锁定到内存中,DataNode依赖Windows上libhadoop.so或hadoop.dll中的本机JNI代码。如果您正在使用HDFS集中式缓存管理,请确保启用JNI。
确保配置以下内容:
这确定了DataNode将用于缓存的最大内存量。在类似Unix的系统上,还需要增加DataNode用户的“内存大小” ulimit(ulimit -l)以匹配此参数(请参阅下面的“ OS Limits”部分)。设置此值时,请记住,您还将需要内存中的空间来做其他事情,例如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(即启用集中式缓存)。
如果收到错误“由于已配置的最大锁定内存大小而导致无法启动数据节点…大于数据节点的可用RLIMIT_MEMLOCK ulimit”,则表示操作系统对您可以锁定的内存量施加的限制比您所限制的要低。已经配置。要解决此问题,您必须调整与DataNode一起运行的ulimit -l值。通常,此值在/etc/security/limits.conf中配置。但是,它会根据所使用的操作系统和发行版而有所不同。
您可以从外壳程序运行ulimit -l并获得比使用dfs.datanode.max.locked.memory或字符串“ unlimited” 配置的更高的值时,已经正确配置了该值。表示没有限制。请注意,通常ulimit -l以KB为单位输出内存锁定限制,但是dfs.datanode.max.locked.memory必须以字节为单位指定。
此信息不适用于Windows上的部署。Windows没有ulimit -l的直接等效项。