一、通用优化
1.1、NameNode 的元数据备份使用 SSD
1.2、定时备份 NameNode 上的元数据
建议每小时或者每天备份,如果数据极其重要,可以5~10分钟备份一次。备份可以通过定时任务复制元数据目录即可。
1.3、为 NameNode 指定多个元数据目录
使用 dfs.name.dir 或者 dfs.namenode.name.dir 指定。一个指定本地磁盘,一个指定网络磁盘。这样可以提供元数据的冗余和健壮性,以免发生故障。
1.4、设置 dfs.namenode.name.dir.restore 为 true
即允许尝试恢复之前失败的 dfs.namenode.name.dir 目录,在创建 checkpoint 时做此尝试,如果设置了多个磁盘,建议允许。
1.5、NameNode 节点必须配置为 Raid1(镜像盘)结构
1.6、补充:什么是Raid0、Raid0+1、Raid1、Raid5
Standalone:最普遍的单磁盘储存方式。
Cluster:集群储存是通过将数据分布到集群中各节点的存储方式,提供单一的使用接口与界面,使用户可以方便地对所有数据进行统一使用与管理。
Hot swap:用户可以再不关闭系统,不切断电源的情况下取出和更换硬盘,提高系统的恢复能力、拓展性和灵活性。
Raid0:是所有 raid 中存储性能最强的阵列形式
。其工作原理就是在多个磁盘上分散存取连续的数据,这样,当需要存取数据是多个磁盘可以并排执行,每个磁盘执行属于它自己的那部分数据请求,显著提高磁盘整体存取性能。但是不具备容错能力,适用于低成本、低可靠性的台式系统。
Raid1:又称镜像盘,把一个磁盘的数据镜像到另一个磁盘上,采用镜像容错来提高可靠性,具有 raid 中最高的数据冗余能力
。存数据时会将数据同时写入镜像盘内,读取数据则只从工作盘读出。发生故障时,系统将从镜像盘读取数据,然后再恢复工作盘正确数据。这种阵列方式可靠性极高,但是其容量会减去一半。广泛用于数据要求极严的应用场合,如商业金融、档案管理等领域。只允许一个硬盘出故障。
Raid0+1:将 Raid0 和 Raid1 技术结合在一起,兼顾两者的优势。在数据得到保障的同时,还能提供较强的存储性能。不过至少要求4个或以上的硬盘,但也只允许一个磁盘出错。是一种三高技术
。
Raid5:Raid5 可以看成是 Raid0+1 的低成本方案
。采用循环偶校验独立存取的阵列方式。将数据和相对应的奇偶校验信息分布存储到组成 Raid5 的各个磁盘上。当其中一个磁盘数据发生损坏后,利用剩下的磁盘和相应的奇偶校验信息重新恢复/生成丢失的数据而不影响数据的可用性。至少需要3个或以上的硬盘。适用于大数据量的操作。成本稍高、储存新强、可靠性强的阵列方式。
Raid还有其他方式,请自行查阅。
1.7、保持 NameNode 日志目录有足够的空间,这些日志有助于帮助你发现问题
1.8、因为 Hadoop 是 IO 密集型框架,所以尽量提升存储的速度和吞吐量(类似位宽)
二、Linux 优化
2.1、开启文件系统的预读缓存可以提高读取速度
$ sudo blockdev --setra 32768 /dev/sda
(尖叫提示:ra 是 readahead 的缩写)
2.2、关闭进程睡眠池
$ sudo sysctl -w vm.swappiness=0
2.3 修改系统能够打开的文件句柄数
即调整 ulimit 上限,默认值为比较小的数字(CentOS 6.8 默认是 1024)。做高并发服务器或者像聊天这种长连接服务时,需要修改系统能够打开的文件句柄数。否则会出现 too many open files 的错误。socket 句柄和文件句柄是相同的,像聊天这种长链接服务,此时 too many open files 指的就是 socket 句柄数超出了系统的限制。
$ ulimit -n 查看允许最大进程数
$ ulimit -u 查看进程允许打开最大文件数
单进程文件句柄限制
句柄数限制又分为系统总限制
和单进程限制
。使用命令 ulimit -n 可以看到系统对于单个进程的限制,即 open files。执行命令 ulimit -a 如下:
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7334
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
如果 open files 1024 表示我当前登录的用户(atguigu),每个进程可以打开 1024 个句柄,当然总和不能超过 file-max 限制。
如果 open files 65535 表示我当前登录的用户(root),每个进程可以打开 65535 个句柄,当然总和不能超过 file-max 限制。
修改 open files 的值,有两种方法,临时的和永久的。
临时修改 open files
ulimit -HSn 1000 将 open-files 修改为 1000,退出当前 shell 后即失效。H 和 S 选项表示硬限制和软限制,下面有解释,省略的话表示同时修改。
永久修改 open files
若希望永久生效的话就得修改配置文件,/etc/security/limits.conf,修改后需要重启系统,该文件内容为:
$ sudo vim /etc/security/limits.conf 修改单个进程打开文件数限制
在末尾添加以下内容:
* soft nofile 1024000
* hard nofile 1024000
hive - nofile 1024000
hive - nproc 1024000
第一列:表示对哪些用户进程进行限制,* 表示所有用户。
第二列:
soft:表示软限制,当进程打开的句柄数超过该限制后,只是进行警告。
hard:表示硬限制,进程最多打开这么多句柄。
第三列:nofile 表示进程能够打开的最大文件数,nproc 表示允许最大进程数(其他配置值可以参考文件中的注释,与 ulimit -a 列出的值一一对应)。
第四列:是具体的值,这个值也是有上限的,这个上限的值设置在 /proc/sys/fs/nr_open,默认值为 1048576,完全够用了。
修改后需要重启系统生效。
nr_open 表示一个进程最多能分配的文件句柄数
cat /proc/sys/fs/nr_open
1048576
系统总打开文件句柄限制
上面的 open files 是对单个进程的限制,属于线程级别的。系统级的限制在这个文件中 /proc/sys/fs/file-max
cat /proc/sys/fs/file-max
185890
file-max 指定了系统范围内所有进程可以打开的文件句柄限制。
同样,修改上面那个文件也是临时生效的,重启后会失效。如果要永久生效,则要修改这个文件, /etc/sysctl.conf
fs.file-max = 185890
如果没有这一项,则新加这一项就行。运行 sysctl -p 或重启后才能生效。
lsof -p 进程pid 查看单个进程打开的文件句柄
lsof -p 4512
/proc/sys/fs/file-nr 记录当前系统打开的句柄数
cat /proc/sys/fs/file-nr
2240 0 185890
第一列:表示已打开的句柄数
第二列:表示已分配但是未使用的句柄数
第三列:表示系统总的句柄数,即 file-max
总结
所有进程能够打开的文件句柄总数不能超过 file-max
单个进程打开的句柄数不能超过nofile soft limit
nofile soft limit 的设置不能超过nofile hard limit
nofile hard limit 的设置不能超过 no_open
no_open 的设置不能超过 file-max
修改用户打开进程数限制
$ sudo vim /etc/security/limits.d/90-nproc.conf 修改用户打开进程数限制
修改为:
* soft nproc 40960
root soft nproc unlimited
修改后需要重启系统生效。
2.4、开启集群的时间同步 NTP,请参看一下链接
https://www.cnblogs.com/chenmingjun/p/10545638.html
2.5、更新系统补丁(尖叫提示:更新补丁前,请先测试新版本补丁对集群节点的兼容性)
三、HDFS 优化(hdfs-site.xml)
3.1、保证 RPC 调用会有较多的线程数
属性:dfs.namenode.handler.count**
解释:该属性是 NameNode 服务默认线程数,的默认值是 10,根据机器的可用内存可以调整为 50~100 。
属性:dfs.datanode.handler.count
解释:该属性默认值为 10,是 DataNode 的处理线程数,如果 HDFS 客户端程序读写请求比较多,可以调高到 15~20,设置的值越大,内存消耗越多,不要调整的过高,一般业务中,5~10 即可。
3.2、副本数的调整
属性:dfs.replication
解释:如果数据量巨大,且不是非常之重要,可以调整为 2~3,如果数据非常之重要,可以调整为 3~5。
3.3.、文件块大小的调整
属性:dfs.blocksize
解释:块大小定义,该属性应该根据存储的大量的单个文件大小来设置,如果大量的单个文件都小于 100M,建议设置成 64M 块大小,对于大于 100M 或者达到 GB 的这种情况,建议设置成 256M,一般设置范围波动在 64M~256M 之间。
四、MapReduce 优化(mapred-site.xml)
4.1、Job 任务服务线程数调整
属性:mapreduce.jobtracker.handler.count
解释:该属性是 Job 任务线程数,默认值是 10,根据机器的可用内存可以调整为 50~100。
4.2、Http 服务器工作线程数
属性:mapreduce.tasktracker.http.threads
解释:定义 HTTP 服务器工作线程数,默认值为 40,对于大集群可以调整到 80~100。
4.3、文件排序合并优化
属性:mapreduce.task.io.sort.factor
解释:文件排序时同时合并的数据流的数量,这也定义了同时打开文件的个数,默认值为 10,如果调高该参数,可以明显减少磁盘 IO,即减少文件读取的次数。
4.5、设置任务并发
属性:mapreduce.map.speculative
解释:该属性可以设置任务是否可以并发执行,如果任务多而小,该属性设置为 true 可以明显加快任务执行效率,但是对于延迟非常高的任务,建议改为 false,这就类似于迅雷下载。
4.6、MR 输出数据的压缩
属性:mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress
解释:对于大集群而言,建议设置 Map-Reduce 的输出为压缩的数据,而对于小集群,则不需要。
4.7、优化 Mapper 和 Reducer 的个数
属性:
mapreduce.tasktracker.map.tasks.maximum
mapreduce.tasktracker.reduce.tasks.maximum
解释:
以上两个属性分别为一个单独的 Job 任务可以同时运行的 Map 和 Reduce 的数量。
设置上面两个参数时,需要考虑 CPU 核数、磁盘和内存容量。假设一个 8 核的 CPU,业务内容非常消耗 CPU,那么可以设置 map 数量为 4,如果该业务不是特别消耗 CPU 类型的,那么可以设置 map 数量为 40,reduce 数量为 20。这些参数的值修改完成之后,一定要观察是否有较长等待的任务,如果有的话,可以减少数量以加快任务执行,如果设置一个很大的值,会引起大量的上下文切换,以及内存与磁盘之间的数据交换,这里没有标准的配置数值,需要根据业务和硬件配置以及经验来做出选择。
在同一时刻,不要同时运行太多的 MapReduce,这样会消耗过多的内存,任务会执行的非常缓慢,我们需要根据 CPU 核数,内存容量设置一个 MR 任务并发的最大值,使固定数据量的任务完全加载到内存中,避免频繁的内存和磁盘数据交换,从而降低磁盘 IO,提高性能。
大概配比:
CPU CORE | MEM(GB) | Map | Reduce |
---|---|---|---|
1 | 1 | 1 | 1 |
1 | 5 | 1 | 1 |
4 | 5 | 1~4 | 2 |
16 | 32 | 16 | 8 |
16 | 64 | 16 | 8 |
24 | 64 | 24 | 12 |
24 | 128 | 24 | 12 |
大概估算公式:
map = 2 + ?cpu_core
reduce = 2 + ?cpu_core
五、HBase 优化
5.1、在 HDFS 的文件中追加内容
不是不允许追加内容么?没错,请看背景故事:
属性:dfs.support.append
文件:hdfs-site.xml、hbase-site.xml
解释:开启 HDFS 追加同步,可以优秀的配合 HBase 的数据同步和持久化。默认值为 true。
5.2、优化 DataNode 允许的最大文件打开数
属性:dfs.datanode.max.transfer.threads
文件:hdfs-site.xml
解释:HBase 一般都会同一时间操作大量的文件,根据集群的数量和规模以及数据动作,设置为 4096 或者更高。默认值:4096。
5.3、优化延迟高的数据操作的等待时间
属性:dfs.image.transfer.timeout
文件:hdfs-site.xml
解释:如果对于某一次数据操作来讲,延迟非常高,socket 需要等待更长的时间,建议把该值设置为更大的值(默认 60000 毫秒),以确保 socket 不会被 timeout 掉。
5.4、优化数据的写入效率
属性:
mapreduce.map.output.compress
mapreduce.map.output.compress.codec
文件:mapred-site.xml
解释:开启这两个数据可以大大提高文件的写入效率,减少写入时间。第一个属性值修改为 true,第二个属性值修改为:org.apache.hadoop.io.compress.GzipCodec。
5.5、优化 DataNode 存储
属性:dfs.datanode.failed.volumes.tolerated
文件:hdfs-site.xml
解释:默认为 0,意思是当 DataNode 中有一个磁盘出现故障,则会认为该 DataNode shutdown 了。如果修改为 1,则一个磁盘出现故障时,数据会被复制到其他正常的 DataNode 上,当前的 DataNode 继续工作。
5.6、设置 RPC 监听数量
属性:hbase.regionserver.handler.count
文件:hbase-site.xml
解释:默认值为 30,用于指定 RPC 监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。
5.7、优化 HStore 文件大小
属性:hbase.hregion.max.filesize
文件:hbase-site.xml
解释:默认值 10737418240(10GB),如果需要运行 HBase 的 MR 任务,可以减小此值,因为一个 region 对应一个 map 任务,如果单个 region 过大,会导致 map 任务执行时间过长。该值的意思就是,如果 HFile 的大小达到这个数值,则这个 region 会被切分为两个 Hfile。
5.8、优化 hbase 客户端缓存
属性:hbase.client.write.buffer
文件:hbase-site.xml
解释:用于指定 HBase 客户端缓存,增大该值可以减少 RPC 调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少 RPC 次数的目的。
5.9、指定 scan.next 扫描 HBase 所获取的行数
属性:hbase.client.scanner.caching
文件:hbase-site.xml
解释:用于指定 scan.next 方法获取的默认行数,值越大,消耗内存越大。
六、内存优化
HBase 操作过程中需要大量的内存开销,毕竟 Table 是可以缓存在内存中的,一般会分配整个可用内存的 70% 给 HBase 的 Java 堆。但是不建议分配非常大的堆内存,因为 GC 过程持续太久会导致 RegionServer 处于长期不可用状态,一般 16~48G 内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。
七、JVM 优化
涉及文件:hbase-env.sh
7.1、并行 GC
参数:-XX:+UseParallelGC
解释:开启并行GC。
7.2、同时处理垃圾回收的线程数
参数:-XX:ParallelGCThreads=cpu_core – 1
解释:该属性设置了同时处理垃圾回收的线程数。
7.3、禁用手动 GC
参数:-XX:DisableExplicitGC
解释:防止开发人员手动调用 GC。
八、Zookeeper 优化
8.1、优化 Zookeeper 会话超时时间
参数:zookeeper.session.timeout
文件:hbase-site.xml
解释:In hbase-site.xml, set zookeeper.session.timeout to 30 seconds or less to bound failure detection (20-30 seconds is a good start) 该值会直接关系到 master 发现服务器宕机的最大周期,默认值为 30 秒,如果该值过小,会在 HBase 在写入大量数据发生而 GC 时,导致 RegionServer 短暂的不可用,从而没有向 ZK 发送心跳包,最终导致认为从节点 shutdown。一般 20 台左右的集群需要配置 5 台 zookeeper。
注意:不同的 HBase 版本,它的 zookeeper 会话超时时间默认是不一样的!