HDFS基于内存存储-集中式缓存管理
概览
HDFS 中的集中式缓存管理 是一种明确的缓存机制,允许用户指定由 HDFS 缓存的路径。NameNode 将与磁盘上具有所需block的 datanode 通信,并指示它们将block缓存到off-heap缓存中。
HDFS 中的集中式缓存管理有许多显著的优点。
- 显式指定可以防止经常使用的数据从内存中删除。当工作集的大小超过主内存的大小时,这一点尤其重要,这对于许多 HDFS 工作负载是很常见的。
- 因为 DataNode 缓存是由 NameNode 管理的,所以应用程序在做任务布局决策时可以查询缓存的块位置集。将任务与缓存的块副本共存可以提高读取性能。
- 当块被 DataNode 缓存时,客户端可以使用一个新的、更高效的、零拷贝的读 API。由于 DataNode 只完成一次缓存数据的校验和验证,因此在使用这个新 API 时,客户端基本上不会产生任何开销。
- 集中式缓存可以提高总体集群内存利用率。当依赖于每个 DataNode 上的 OS 缓冲区缓存时,对一个块的重复读取将导致该块的所有 n 个副本被拉入缓冲区缓存。使用集中式缓存管理,用户只能显式地固定 n 个副本中的 m 个副本,从而节省 n-m 内存。
用例
集中式缓存管理对于重复访问的文件非常有用。例如,Hive 中经常用于连接的小事实表可以作为缓存的候选对象。另一方面,缓存查询一年报告的结果可能不太有用,因为历史数据可能只读取一次。
集中式缓存管理对于具有性能 SLAs 的混合工作负载也很有用。缓存高优先级工作负载的工作集可以确保它不会与低优先级工作负载争用磁盘 i/o。
架构
在这个体系结构中,NameNode 负责协调集群中的所有 DataNode off-heap 缓存。NameNode 定期从每个 DataNode 接收一个缓存报告,该报告描述缓存在给定 DN 上的所有块。NameNode 通过 piggybackback cache 和 DataNode heartbeat 上的 uncache 命令来管理 DataNode 缓存。
NameNode查询它的缓存指令集,以确定应该缓存哪些路径。缓存指令持久地存储在 fsimage 和edits日志中,可以通过 Java 和命令行 api 添加、删除和修改。NameNode 还存储一组缓存池,这些缓存池是管理实体,用于将缓存指令组合在一起,以进行资源管理和执行权限。
NameNode 定期重新扫描名称空间和活动缓存指令,以确定哪些块需要缓存或缓存,并将缓存工作分配给 datanode。重新扫描也可以由用户操作触发,如添加或删除缓存指令或删除缓存池。
当前不缓存正在构造、损坏或其他不完整的块。如果缓存指令覆盖符号链接,则不缓存符号链接目标。缓存目前是在文件或目录级别进行的,块和子块缓存是未来工作的一项。
概念
缓存指令
存指令定义了一个应该被缓存的路径。路径可以是目录或文件。目录被非递归地缓存,这意味着只缓存目录的一级列表中的文件。
指令还指定其他参数,如缓存复制因子和过期时间。复制因子指定要缓存的块副本的数量。如果多个缓存指令指向同一个文件,则应用最大缓存复制因子。
过期时间在命令行上指定为生存时间(time-to-live, TTL),未来的相对过期时间。缓存指令过期后,NameNode在做缓存决策时不再考虑它。
缓存池
缓存池是用来管理缓存指令组的管理实体。缓存池具有类unix的权限,这限制了哪些用户和组可以访问缓存池。写权限允许用户添加和删除缓存指令到池中。读权限允许用户在池中列出缓存指令,以及附加的元数据。执行权限未使用。
缓存池也用于资源管理。池可以执行最大限制,限制池中的指令可以缓存到聚合中的字节数。通常,池限制的总和大约等于集群上为HDFS缓存预留的聚合内存。缓存池还跟踪大量统计信息,以帮助集群用户确定缓存什么和应该缓存什么。
池还可以强制执行最长生存时间,这限制了向池中添加指令的最长过期时间。
命令
管理员和用户可以通过 hdfs cacheadmin 子命令与缓存池和指令交互。
缓存指令由唯一的、不重复的64位整数 ID 标识。即使稍后删除了缓存指令,也不会重用 id。缓存池由唯一的字符串名称标识。
缓存指令
addDirective
用法: hdfs cacheadmin-addDirective-path < path >-pool < pool-name > [-force ][-replication < replication > ][-ttl < time-to-live > ]
添加新的缓存指令。
A path to cache. The path can be a directory or a file. | |
The pool to which the directive will be added. You must have write permission on the cache pool in order to add new directives. | |
-force | Skips checking of cache pool resource limits. |
The cache replication factor to use. Defaults to 1. | |
How long the directive is valid. Can be specified in minutes, hours, and days, e.g. 30m, 4h, 2d. Valid units are [smhd]. “never” indicates a directive that never expires. If unspecified, the directive never expires. |
removeDirective
用法: hdfs caheadmin-removeDirective < id >
删除缓存指令。
The id of the cache directive to remove. You must have write permission on the pool of the directive in order to remove it. To see a list of cachedirective IDs, use the -listDirectives command. |
removeDirectives
用法: hdfs caheadmin-removeDirectives < path >
使用指定的路径删除每个缓存指令。
The path of the cache directives to remove. You must have write permission on the pool of the directive in order to remove it. To see a list of cache directives, use the -listDirectives command | |
---|---|
列表指令
用法: hdfs caheadmin-listDirectives [-stats ][-path < path > ][-pool < pool > ]
列出缓存指令。
List only cache directives with this path. Note that if there is a cache directive for path in a cache pool that we don’t have read access for, it will not be listed. | |
---|---|
List only path cache directives in that pool. | |
-stats | List path-based cache directive statistics. |
缓存池命令
addPool
用法: hdfs caheadmin-addPool < name > [-owner < owner > ][-group < group > ][-mode < mode > ][-limit < limit > ][-maxTtl < maxTtl > ]
添加新的缓存池。
Name of the new pool. | |
---|---|
Username of the owner of the pool. Defaults to the current user. | |
Group of the pool. Defaults to the primary group name of the current user. | |
UNIX-style permissions for the pool. Permissions are specified in octal, e.g. 0755. By default, this is set to 0755. | |
The maximum number of bytes that can be cached by directives in this pool, in aggregate. By default, no limit is set. | |
The maximum allowed time-to-live for directives being added to the pool. This can be specified in seconds, minutes, hours, and days, e.g. 120s, 30m, 4h, 2d. Valid units are [smhd]. By default, no maximum is set. A value of "never " specifies that there is no limit. |
modifyPool
用法: hdfs caheadmin-modifyPool < name > [-owner < owner > ][-group < group > ][-mode < mode > ][-limit < limit > ][-maxTtl < maxTtl > ]
修改现有缓存池的元数据。
Name of the pool to modify. | |
Username of the owner of the pool. | |
Groupname of the group of the pool. | |
Unix-style permissions of the pool in octal. | |
Maximum number of bytes that can be cached by this pool. | |
The maximum allowed time-to-live for directives being added to the pool. |
removePool
用法: hdfs caheadmin-removePool < name >
删除缓存池。这也会缓存与缓存池关联的路径。
Name of the cache pool to remove. | |
---|---|
listPools
用法: hdfs cacheadmin-listPools [-stats ][ < name > ]
显示有关一个或多个缓存池的信息,例如名称、所有者、组、权限等。
The command for which to get detailed help. If no command is specified, print detailed help for all commands. | |
---|---|
配置
本地库
为了将文件锁定到内存中,DataNode 依赖于libhadoop.so。如果您正在使用 HDFS 集中缓存管理,请确保启用 JNI。
配置属性
必需项
请确保配置以下内容:
-
dfs.datanode.max.locked.memory
这决定了 DataNode 用于缓存的最大内存量。在类 unix 系统上,还需要增加 DataNode 用户的“ locked-in-memory size”ulimit (ulimit-l)以匹配这个参数(参见下面关于 OS Limits 的部分)。在设置这个值时,请记住您还需要在内存中为其他东西留出空间,比如 DataNode 和应用程序 JVM 堆以及操作系统页面缓存。
此设置与 Lazy Persist Writes 特性共享。Data Node 将确保 Lazy Persist Writes 和 Centralized Cache Management 使用的组合内存不超过 dfs.datanode.max.locked.memory 中配置的数量。
可选项
下列属性不是必需的,但可指定用于调优:
-
dfs.namenode.path.based.cache.refresh.interval.ms
NameNode 将以此作为后续路径缓存重新扫描之间的毫秒数。这将计算要缓存的块和每个 DataNode,其中包含应该缓存它的块的副本。
默认情况下,这个参数设置为300000,也就是5分钟。
-
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% 。
操作系统限制
如果出现错误“无法启动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。