hbase、hadoop、sparkStreaming、sparkCore,sparkShuffle的调优

Hbase的优化

服务端优化:
    hbase.regionserver.handler.count:rpc请求的线程数量,默认值是10,生产环境建议使用100,特别大的时候scan/put几M的数据,会占用过多的内存,有可能导致频繁的GC,甚至oom。
    hbase.regionserver.hlog.splitlog.writer.threads:默认值是3,建议设为10,日志切割所用的线程数
    hbase.regionserver.global.memstore.upperLimit:默认值0.4,regionserver所有memstore占用内存在总内存中的upper比例,当达到该值,则会从整个regionserver中找出最需要flush的region进行flush
    hbase.regionserver.global.memstore.lowerLimit:默认值0.35
    hbase.regionserver.thread.compaction.small:默认值为1,regionserver做Minor Compaction时线程池里线程数目,可以设置为5。
    hbase.regionserver.thread.compaction.large:默认值为1,regionserver做Major Compaction时线程池里线程数目,可以设置为8。
    hbase.regionserver.lease.period:默认值60000(60s),客户端连接regionserver的超时时间,这个最好根据实际业务情况进行调整
    hbase.hregion.max.filesize:默认10G,StoreFile,建议手动定时分裂,可以设置为60G
    hbase.hregion.majorcompaction:hbase的region主合并的间隔时间,默认为1天,建议设置为0,禁止自动的major主合并,major合并会把一个store下storefile合并为storefile文件,在合并过程中删除标识的数据,主合并可能持续数小时,减少业务影响,业务低峰期手动、脚本、api定期进行major合并。
    hbase.hregion.memstore.flush.size:默认值128M,一旦有memstore超过该值flush,如果regionserver的jvm内存比较充足(16G),可以调整为256M。
    hbase.hregion.memstore.block.multiplier:默认值2,如果一个memstore的内存大小已经超过  .flush.size *  .block.multiplier,则会阻塞该memstore的写操作,为避免阻塞,建议为5,太大会有OOM的风险。在regionserver日志中出现"Blocking updates for '<threadName>' on region <regionName> : memstore size <多少M> is >= than blocking <多少M> size"的信息时,说明这个值该调整了。
    hbase.hstore.compaction.min:默认值为3,任何一个store里的storefile总数超过该值,会触发默认的合并操作,可以设置5~8,在手动的定期major compact中进行storefile文件的合并,减少合并的次数,延长合并的时间。
    hbase.hstore.compaction.max:默认值为10,一次最多合并storefile个数,避免OOM。
    hbase.hstore.blockingStoreFiles:默认为7,任何一个store的storefile的文件数大于该值,则在flush memstore前先进行split/compact,同时把该region添加到flushQueue,延时刷新,这期间会阻塞写操作直到compact完成/超过hbase.hstore.blockingWaitTime(90s)配置的时间,可以设置为30,避免memstore不及时flush。当regionserver运行日志中出现大量的“Region <regionName> has too many store files; delaying flush up to 90000ms"时,说明这个值需要调整了
    hbase.master.distributed.log.splitting:默认值为true,建议设为false。关闭hbase的分布式日志切割,在log需要replay时,由master来负责重放
    hbase.snapshot.enabled:快照功能,默认是false(不开启),建议设为true,特别是对某些关键的表,定时用快照做备份是一个不错的选择。
    hfile.block.cache.size:默认值0.25,regionserver的block cache的内存大小限制,在偏向读的业务中,可以适当调大该值,需要注意的是hbase.regionserver.global.memstore.upperLimit的值和hfile.block.cache.size的值之和必须小于0.8。
    dfs.socket.timeout:默认值60000(60s),建议根据实际regionserver的日志监控发现了异常进行合理的设置,比如我们设为900000,这个参数的修改需要同时更改hdfs-site.xml
    dfs.datanode.socket.write.timeout:默认480000(480s),有时regionserver做合并时,可能会出现datanode写超时的情况,480000 millis timeout while waiting for channel to be ready for write,这个参数的修改需要同时更改hdfs-site.xml


Client端优化:
    1.hbase.client.write.buffer:默认为2M,写缓存大小,推荐设置为5M,单位是字节,当然越大占用的内存越多,此外测试过设为10M下的入库性能,反而没有5M好
    2.hbase.client.pause:默认是1000(1s),如果你希望低延时的读或者写,建议设为200,这个值通常用于失败重试,region寻找等
    3.hbase.client.retries.number:默认值是10,客户端最多重试次数,可以设为11,结合上面的参数,共重试时间71s
    4.hbase.ipc.client.tcpnodelay:默认是false,建议设为true,关闭消息缓冲
    5.hbase.client.scanner.caching:scan缓存,默认为1,避免占用过多的client和rs的内存,一般1000以内合理,如果一条数据太大,则应该设置一个较小的值,
        通常是设置业务需求的一次查询的数据条数 如果是扫描数据对下次查询没有帮助,则可以设置scan的setCacheBlocks为false,避免使用缓存;
    6.table用完需关闭,关闭scanner
    7.限定扫描范围:指定列簇或者指定要查询的列,指定startRow和endRow
    8.使用Filter可大量减少网络消耗
    9.通过Java多线程入库和查询,并控制超时时间。后面会共享下我的hbase单机多线程入库的代码
    10.建表注意事项:
        开启压缩
        合理的设计rowkey
        进行预分区
        开启bloomfilter

jvm和垃圾收集参数:
export HBASE_REGIONSERVER_OPTS="-Xms36g -Xmx36g -Xmn1g -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=15 -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/data/logs/gc-$(hostname)-hbase.log"

由于我们服务器内存较大(96G),我们给一部分regionserver的jvm内存开到64G,到现在为止,还没有发生过一次full gc,hbase在内存使用控制方面确实下了不少功夫,比如各种blockcache的实现,细心的同学可以看源码。

ZooKeeper调优:
zookeeper.session.timeout:默认值3分钟,不可配置太短,避免session超时,hbase停止服务,线上生产环境由于配置为1分钟,如果太长,当regionserver挂掉,zk还得等待这个超时时间(已有patch修复),从而导致master不能及时对region进行迁移。
查看zk节点状态。重新启动zk节点前后,一定要查看状态
echo ruok | nc host port

echo stat | nc host port

一、调优的目的
    充分的利用机器的性能,更快的完成mr程序的计算任务。甚至是在有限的机器条件下,能够支持运行足够多的mr程序。

二、调优的总体概述
    从mr程序的内部运行机制,我们可以了解到一个mr程序由mapper和reducer两个阶段组成,其中mapper阶段包括数据的读取、map处理以及写出操作(排序和合并/sort&merge),
    而reducer阶段包含mapper输出数据的获取、数据合并(sort&merge)、reduce处理以及写出操作。
    那么在这七个子阶段中,能够进行较大力度的进行调优的就是map输出、reducer数据合并以及reducer个数这三个方面的调优操作。
    也就是说虽然性能调优包括cpu、内存、磁盘io以及网络这四个大方面,但是从mr程序的执行流程中,我们可以知道主要有调优的是内存、磁盘io以及网络。
    在mr程序中调优,主要考虑的就是减少网络传输和减少磁盘IO操作,故本次课程的mr调优主要包括服务器调优、代码调优、mapper调优、reducer调优以及runner调优这五个方面。

三、服务器调优
    服务器调优主要包括服务器参数调优和jvm调优。在本次项目中,由于我们使用hbase作为我们分析数据的原始数据存储表,所以对于hbase我们也需要进行一些调优操作。
    除了参数调优之外,和其他一般的java程序一样,还需要进行一些jvm调优。

    hdfs调优
    1. dfs.datanode.failed.volumes.tolerated: 允许发生磁盘错误的磁盘数量,默认为0,表示不允许datanode发生磁盘异常。当挂载多个磁盘的时候,可以修改该值。
    2. dfs.replication: 复制因子,默认3
    3. dfs.namenode.handler.count: namenode节点并发线程量,默认10
    4. dfs.datanode.handler.count:datanode之间的并发线程量,默认10。
    5. dfs.datanode.max.transfer.threads:datanode提供的数据流操作的并发线程量,默认4096。
        一般将其设置为linux系统的文件句柄数的85%~90%之间,查看文件句柄数语句ulimit -a,修改vim /etc/security/limits.conf, 不能设置太大
        文件末尾,添加
            * soft nofile 65535
            * hard nofile 65535
            注意:句柄数不能够太大,可以设置为1000000以下的所有数值,一般不设置为-1。
            异常处理:当设置句柄数较大的时候,重新登录可能出现unable load session的提示信息,这个时候采用单用户模式进行修改操作即可。
                单用户模式:
                    启动的时候按'a'键,进入选择界面,然后按'e'键进入kernel修改界面,然后选择第二行'kernel...',按'e'键进行修改,在最后添加空格+single即可,
                    按回车键回到修改界面,最后按'b'键进行单用户模式启动,当启动成功后,还原文件后保存,最后退出(exit)重启系统即可。
    6. io.file.buffer.size: 读取/写出数据的buffer大小,默认4096,一般不用设置,推荐设置为4096的整数倍(物理页面的整数倍大小)。

    mapreduce调优
    1. mapreduce.task.io.sort.factor: mr程序进行合并排序的时候,打开的文件数量,默认为10个.
    2. mapreduce.task.io.sort.mb: mr程序进行合并排序操作的时候或者mapper写数据的时候,内存大小,默认100M
    3. mapreduce.map.sort.spill.percent: mr程序进行flush操作的阀值,默认0.80。
    4. mapreduce.reduce.shuffle.parallelcopies:mr程序reducer copy数据的线程数,默认5。
    5. mapreduce.reduce.shuffle.input.buffer.percent: reduce复制map数据的时候指定的内存堆大小百分比,默认为0.70,
    适当的增加该值可以减少map数据的磁盘溢出,能够提高系统性能。
    6. mapreduce.reduce.shuffle.merge.percent:reduce进行shuffle的时候,用于启动合并输出和磁盘溢写的过程的阀值,默认为0.66。
    如果允许,适当增大其比例能够减少磁盘溢写次数,提高系统性能。同mapreduce.reduce.shuffle.input.buffer.percent一起使用。
    7. mapreduce.task.timeout:mr程序的task执行情况汇报过期时间,默认600000(10分钟),设置为0表示不进行该值的判断。

四、代码调优
    代码调优,主要是mapper和reducer中,针对多次创建的对象,进行代码提出操作。这个和一般的java程序的代码调优一样。

五、mapper调优
    mapper调优主要就是就一个目标:减少输出量。我们可以通过增加combine阶段以及对输出进行压缩设置进行mapper调优。
    combine介绍:
        实现自定义combine要求继承reducer类,特点:
        以map的输出key/value键值对作为输入输出键值对,作用是减少网络输出,在map节点上就合并一部分数据。
        比较适合,map的输出是数值型的,方便进行统计。
    压缩设置:
        在提交job的时候分别设置启动压缩和指定压缩方式。

六、reducer调优
    reducer调优主要是通过参数调优和设置reducer的个数来完成。
    reducer个数调优:
        要求:一个reducer和多个reducer的执行结果一致,不能因为多个reducer导致执行结果异常。
        规则:一般要求在hadoop集群中的执行mr程序,map执行完成100%后,尽量早的看到reducer执行到33%,可以通过命令hadoop job -status job_id或者web页面来查看。
            原因: map的执行process数是通过inputformat返回recordread来定义的;而reducer是有三部分构成的,分别为读取mapper输出数据、合并所有输出数据以及reduce处理,
            其中第一步要依赖map的执行,所以在数据量比较大的情况下,一个reducer无法满足性能要求的情况下,我们可以通过调高reducer的个数来解决该问题。
        优点:充分利用集群的优势。
        缺点:有些mr程序没法利用多reducer的优点,比如获取top n的mr程序。

七、runner调优
    runner调优其实就是在提交job的时候设置job参数,一般都可以通过代码和xml文件两种方式进行设置。
    1~8详见ActiveUserRunner(before和configure方法),9详解TransformerBaseRunner(initScans方法)
    1. mapred.child.java.opts: 修改childyard进程执行的jvm参数,针对map和reducer均有效,默认:-Xmx200m 
    2. mapreduce.map.java.opts: 需改map阶段的childyard进程执行jvm参数,默认为空,当为空的时候,使用mapred.child.java.opts。
    3. mapreduce.reduce.java.opts:修改reducer阶段的childyard进程执行jvm参数,默认为空,当为空的时候,使用mapred.child.java.opts。
    4. mapreduce.job.reduces: 修改reducer的个数,默认为1。可以通过job.setNumReduceTasks方法来进行更改。
    5. mapreduce.map.speculative:是否启动map阶段的推测执行,默认为true。其实一般情况设置为false比较好。可通过方法job.setMapSpeculativeExecution来设置。
    6. mapreduce.reduce.speculative:是否需要启动reduce阶段的推测执行,默认为true,其实一般情况设置为fase比较好。可通过方法job.setReduceSpeculativeExecution来设置。
    7. mapreduce.map.output.compress:设置是否启动map输出的压缩机制,默认为false。在需要减少网络传输的时候,可以设置为true。
    8. mapreduce.map.output.compress.codec:设置map输出压缩机制,默认为org.apache.hadoop.io.compress.DefaultCodec,
    推荐使用SnappyCodec(在之前版本中需要进行安装操作,现在版本不太清楚,安装参数:http://www.cnblogs.com/chengxin1982/p/3862309.html)
    9. hbase参数设置
        由于hbase默认是一条一条数据拿取的,在mapper节点上执行的时候是每处理一条数据后就从hbase中获取下一条数据,通过设置cache值可以一次获取多条数据,减少网络数据传输。

    
Spark Streaming作业的执行流程

    具体流程:
        客户端提交作业后启动Driver,Driver是park作业的Master。
        每个作业包含多个Executor,每个Executor以线程的方式运行task,Spark Streaming至少包含一个receiver task。
        Receiver接收数据后生成Block,并把BlockId汇报给Driver,然后备份到另外一个Executor上。
        ReceiverTracker维护Reciver汇报的BlockId。
        Driver定时启动JobGenerator,根据Dstream的关系生成逻辑RDD,然后创建Jobset,交给JobScheduler。
        JobScheduler负责调度Jobset,交给DAGScheduler,DAGScheduler根据逻辑RDD,生成相应的Stages,每个stage包含一到多个task。
        TaskScheduler负责把task调度到Executor上,并维护task的运行状态。
        当tasks,stages,jobset完成后,单个batch才算完成。

    调优:
        receiver的数量:
            1、如果是kafka direct 不需要考虑
            2、其他ssc.socketTextStream("",4440)
        数据块的数量:
            1、receiver
                batchinterval/blockInterval 2000/200
            2、如果数据块少,小宇executor的core,则降低blockInterval的数值
            3、但是BlockInterval值不要太少,低于50ms就会产生太多的block,这样launch task就会花不少的时间
        receiverjieshousulv:
            1、qps sparkstreaming默认无限制
            2、spark.streaming.receiver.maxRate
        task的并行度:
            1、BlockRDD 的分区数 
                a)Receiver接收的数据特点决定 
                b)reparation (跟RDD一样) 
            2、ShuffleRDD的分区数 
                a)如果没有shuffle,那么一直block该是多少就是多少 
                b)如果有shuffle,那么我们可以设置 
                如果不设置,默认就是任务的core数(spark.default.parallelism)
        序列化:
            StorageLevel.MEMORY_AND_DISK_SER_2 
            StorageLevel.MEMORY_ONLY_SER 
            使用kyro序列化机制
        内存调优:
            1:需要内存大小 和transform类型有关系(updateStateBykey reduceBykeyAndWindow mapWithState) 
                数据存储的级别(持久化) 
                https://github.com/apache/spark/blob/master/examples/src/main/java/org/apache/spark/examples /streaming/JavaStatefulNetworkWordCount.java 
            2:GC Driver和Executor都使用CMS垃圾回收器 
                --dirver-java-options 
                --spark.executor.extraJavaOptions 
                -XX:+UseConcMarkSweepGC
        压力反馈:
            写入过慢会反向限制数据读取效率
            spark.streaming.backpressure.enabled = true
        动态资源匹配:
            1、spark batch application动态的决定这个application需要多少个execytors
                a、当一个executor空闲的时候,将这个executor杀掉
                b、当task过多的时候动态的启动executors
            streaming分配executor的原则上是比对process time /batchinterval 的比率
            2、spark.streaming.dynamicAllocation.enabled = true
        
        kafka和flume的集成
        push模式 推
        pull模式 拉 不会丢失数据,符合at least once,消费完数据以后会回执,push模式不会,会丢失数据。
        
        spark消费kafka数据模式对比
        receiver模式
        kafkarReceiver                会丢数据            al most once
        reliableKafkaReceiver        no                    at most once
        设置spark.streaming.receiver.writeAheadLog.enable true
        
        direct模式
        SimplifiedParallelism            不需要union多个inputStreaming
        effciency                        不需要WAL就可以达到不丢失数据
        exactly-onc semanticy            sparkStreaming自己跟踪消费者的offset,消除了与zk的不一致
        

sparkCore优化:
    1、考虑方向
        io流压缩、序列化机制、压缩、内存模型、storage模型、shuffle模型
    2、io流的加解密
        spark.io.entryption.enable=true 开启
        spark.io.entrytion.keySizeBits=128 加密长度
        spark.io.entryption.keygen.algorithm=HmacSHAI 加密算法
    3、序列化
        Kryo Serializer
    4、io的解压缩
        目前支持lz4 lzf snappy
        snappy 提高压缩速度和合理的压缩率
        可压缩的地方:
            1、spark日志 spark.eventLog.compress
            2、broadcast变量 spark.broadcast.compress
            3、shuffle map 上游输出文件 spark.shuffle.compress
            4、对序列化后的rdd进行压缩
    5、内存管理
                        运行时数据                            
        方法区            虚拟机栈                本地方法栈
        堆                        程序计数器                
        
        执行引擎                本地接口        本地方法库
        
        虚拟机栈、方法区和堆所有线程共享,其余线程隔离
        1、内存管理分为静态内存模型和统一内存模型
    
    
    申请删除Executor的规则:spark.dynamicAllocation.executorIdleTimeout=60
    spark.dynamicAllocation.cachedExecutorIdleTimeout=Max_value 这个空闲的Executor中含有缓存数据
    spark.dynamicAllocation.schedulerBacklogTimeout=1 task pending超过一定的时间M后会触发申请增加Executor
    spark.dynamicAllocation.sustainedSchedulerBacklogTimeout = 1 申请的Executor数量以指数形式增长 2
    将spark.dynamicAllocation.enabled设置为true
    将spark.shuffle.service.enabled设置为true
    
    
    
动态资源分配机制
    standalone:
        将spark.shuffle.service.enabled设置为true
        在每一个worker节点中$SPARK_HOME/conf/spark-env.sh中加上:
        export SPARK_WORKER_OPTS=Dspark.shuffle.service.enabled=true
    Mesos:
        在每一个slave节点上运行$SPARK_HOME/sbin/start-mesos-shuffle-service.sh
        且将spark.shuffle.service.enabled设置为true
    YARN:
        1)spark-<version>-yarn-shuffle.jar放到nodeManager的classpath中
        2)配置yarn-site.xml
        <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
        <value>spark_shuffle</value>
        </property>
        <property>
        <name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
        <value>org.apache.spark.network.yarn.YarnShuffleService</value>
        </property>

 

spark shuffle优化参数:****************************************

spark.shuffle.file.buffer
    默认值:32k
    参数说明:该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘。
    调优建议:减少shuffle write过程中溢写磁盘文件的次数,减少磁盘IO次数,进而提升性能。
spark.reducer.maxSizeInFlight
    默认值:48m 
    参数说明:该参数用于设置shuffle read task的buffer缓冲大小,而这个buffer缓冲决定了每次能够拉取多少数据。
    调优建议:如果作业可用的内存资源较为充足的话,适当增加大小(比如96m),减少拉取数据次数,减少网络传输次数
spark.shuffle.io.maxRetries
    默认值:3
    参数说明:shuffle read task从shuffle write task所在节点拉取属于自己的数据时,最大重试次数
    调优建议:对于那些包含了特别耗时的shuffle操作的作业,建议增加重试最大次数(比如60次),
    以避免由于JVM的full gc或者网络不稳定等因素导致的数据拉取失败。调节该参数可以大幅度提升稳定性。
spark.shuffle.io.retryWait
    默认值:5s
    参数说明:具体解释同上,该参数代表了每次重试拉取数据的等待间隔,默认是5s。
    调优建议:建议加大间隔时长(比如60s),以增加shuffle操作的稳定性。
spark.shuffle.memoryFraction
    默认值:0.2
    参数说明:该参数代表了Executor内存中,分配给shuffle read task进行聚合操作的内存比例,默认是20%。
    调优建议:很少持久化操作,建议调高这个比例,给shuffle read的聚合操作更多内存,避免内存不足导致频繁读写磁盘
    在实践中发现,合理调节该参数可以将性能提升10%左右。
spark.shuffle.manager
    默认值:sort|hash
    参数说明:该参数用于设置ShuffleManager的类型。Spark 1.5以后,有三个可选项:hash、sort和tungsten-sort。
    HashShuffleManager是Spark 1.2以前的默认选项,但是Spark 1.2以及之后的版本默认都是SortShuffleManager了。
    tungsten-sort与sort类似,但是使用了tungsten计划中的堆外内存管理机制,内存使用效率更高。
    调优建议:由于SortShuffleManager默认会对数据进行排序,
    因此如果你的业务逻辑中需要该排序机制的话,则使用默认的SortShuffleManager就可以;
    1、业务逻辑不需要排序,通过bypass机制或优化的HashShuffleManager来避免排序操作,同时提供较好的磁盘读写性能。
    2、这里要注意的是,tungsten-sort要慎用,因为之前发现了一些相应的bug。
spark.shuffle.sort.bypassMergeThreshold----针对SortShuffle
    默认值:200
    参数说明:SortShuffleManager,如果shuffle read task的数量小于这个阈值(默认是200),则shuffle write过程中不会进行排序操作,
    而是直接按照未经优化的 HashShuffleManager 的方式去写数据,但是最后会将每个task产生的所有临时磁盘文件都合并成一个文件,并会创建单独的索引文件。
    调优建议:当你使用SortShuffleManager时,如果的确不需要排序操作,那么建议将这个参数调大一些,大于shuffle read task的数量。那么此时就会自动启用bypass机制,
    map-side就不会进行排序了,减少了排序的性能开销。但是这种方式下,依然会产生大量的磁盘文件,因此shuffle write性能有待提高。
spark.shuffle.consolidateFiles----针对HashShuffle
    默认值:false
    参数说明:如果使用 HashShuffleManager ,该参数有效。如果设置为true,那么就会开启consolidate机制,会大幅度合并shuffle write的输出文件,
    对于shuffle read task数量特别多的情况下,这种方法可以极大地减少磁盘IO开销,提升性能。
    调优建议:如果的确不需要SortShuffleManager的排序机制,那么除了使用bypass机制,还可以尝试将spark.shffle.manager参数手动指定为hash,使用HashShuffleManager,
    同时开启consolidate机制。在实践中尝试过,发现其性能比开启了bypass机制的SortShuffleManager要高出10%~30%。

 

 

 

通常我们对一个系统进行性能优化无怪乎两个步骤——性能监控和参数调整

性能监控工具

【Spark监控工具】

Spark提供了一些基本的Web监控页面,对于日常监控十分有用。

1. Application Web UI

http://master:4040(默认端口是4040,可以通过spark.ui.port修改)可获得这些信息:

(1)stages和tasks调度情况;

  1. RDD大小及内存使用;
  2. 系统环境信息;
  3. 正在执行的executor信息。

2. history server

当Spark应用退出后,仍可以获得历史Spark应用的stages和tasks执行信息,便于分析程序不明原因挂掉的情况。配置方法如下:

(1)$SPARK_HOME/conf/spark-env.sh

export SPARK_HISTORY_OPTS="-Dspark.history.retainedApplications=50

Dspark.history.fs.logDirectory=hdfs://hadoop000:8020/directory"

说明:spark.history.retainedApplica-tions仅显示最近50个应用spark.history.fs.logDirectory:Spark History Server页面只展示该路径下的信息。

(2)$SPARK_HOME/conf/spark-defaults.conf

spark.eventLog.enabled true

spark.eventLog.dir hdfs://hadoop000:8020/directory #应用在运行过程中所有的信息均记录在该属性指定的路径下

3. spark.eventLog.compress true

(1)HistoryServer启动

$SPARK_HOMR/bin/start-histrory-server.sh

(2)HistoryServer停止

$SPARK_HOMR/bin/stop-histrory-server.sh

4. ganglia

通过配置ganglia,可以分析集群的使用状况和资源瓶颈,但是默认情况下ganglia是未被打包的,需要在mvn编译时添加-Pspark-ganglia-lgpl,并修改配置文件$SPARK_HOME/conf/metrics.properties。

5. Executor logs

Standalone模式:$SPARK_HOME/logs

YARN模式:在yarn-site.xml文件中配置了YARN日志的存放位置:yarn.nodemanager.log-dirs,或使用命令获取yarn logs -applicationId。

【其他监控工具】

1. Nmon(http://www.ibm.com/developerworks/aix/library/au-analyze_aix/)

Nmon 输入:c:CPU n:网络 m:内存 d:磁盘

2. Jmeter(http://jmeter. apache.org/)

通常使用Jmeter做系统性能参数的实时展示,JMeter的安装非常简单,从官方网站上下载,解压之后即可使用。运行命令在%JMETER_HOME%/bin下,对于 Windows 用户,直接使用jmeter.bat。

A.启动jmeter:创建测试计划,设置线程组设置循环次数。

B.添加监听器:jp@gc - PerfMon Metrics Collector。

C.设置监听器:监听主机端口及监听内容,例如CPU。

D.启动监听:可以实时获得节点的CPU状态信息,从图4可看出CPU已出现瓶颈。

3. Jprofiler(http://www.ej-technologies.com/products/jprofiler/overview.html)

JProfiler是一个全功能的Java剖析工具(profiler),专用于分析J2SE和J2EE应用程式。它把CPU、线程和内存的剖析组合在一个强大的应用中。JProfiler的GUI可以更方便地找到性能瓶颈、抓住内存泄漏(memory leaks),并解决多线程的问题。例如分析哪个对象占用的内存比较多;哪个方法占用较大的CPU资源等;我们通常使用Jprofiler来监控Spark应用在local模式下运行时的性能瓶颈和内存泄漏情况。

Spark调优

【Spark集群并行度】

在Spark集群环境下,只有足够高的并行度才能使系统资源得到充分的利用,可以通过修改spark-env.sh来调整Executor的数量和使用资源,Standalone和YARN方式资源的调度管理是不同的。

在Standalone模式下:

1. 每个节点使用的最大内存数: SPARK_WORKER_INSTANCES*SPARK_WORKER_MEMORY;

2. 每个节点的最大并发task数: SPARK_WORKER_INSTANCES*SPARK_WORKER_CORES。

在YARN模式下:

  1. 集群task并行度:

SPARK_ EXECUTOR_INSTANCES* SPARK_EXECUTOR_CORES;

  1. 集群内存总量:

(executor个数) * (SPARK_EXECUTOR_MEMORY+ spark.yarn.executor.memoryOverhead)+(SPARK_DRIVER_MEMORY+spark.yarn.driver.memoryOverhead)。

重点强调:

Spark对Executor和Driver额外添加堆内存大小,Executor端:由spark.yarn.executor.memoryOverhead设置,默认值executorMemory * 0.07与384的最大值。Driver端:由spark.yarn.driver.memoryOverhead设置,默认值driverMemory * 0.07与384的最大值。

通过调整上述参数,可以提高集群并行度,让系统同时执行的任务更多,那么对于相同的任务,并行度高了,可以减少轮询次数。举例说明:如果一个stage有100task,并行度为50,那么执行完这次任务,需要轮询两次才能完成,如果并行度为100,那么一次就可以了。

但是在资源相同的情况,并行度高了,相应的Executor内存就会减少,所以需要根据实际实况协调内存和core。此外,Spark能够非常有效的支持短时间任务(例如:200ms),因为会对所有的任务复用JVM,这样能减小任务启动的消耗,Standalone模式下,core可以允许1-2倍于物理core的数量进行超配

【Spark任务数量调整】

Spark的任务数由stage中的起始的所有RDD的partition之和数量决定,所以需要了解每个RDD的partition的计算方法。以Spark应用从HDFS读取数据为例,HadoopRDD的partition切分方法完全继承于MapReduce中的FileInputFormat,具体的partition数量由HDFS的块大小、mapred.min.split.size的大小、文件的压缩方式等多个因素决定,详情需要参见FileInputFormat的代码。

【Spark内存调优】

内存优化有三个方面的考虑:对象所占用的内存,访问对象的消耗以及垃圾回收所占用的开销。

1. 对象所占内存,优化数据结构

Spark 默认使用Java序列化对象,虽然Java对象的访问速度更快,但其占用的空间通常比其内部的属性数据大2-5倍。为了减少内存的使用,减少Java序列化后的额外开销,下面列举一些方法:

(1)使用对象数组以及原始类型(primitive type)数组以替代Java或者Scala集合类(collection class)。fastutil 库为原始数据类型提供了非常方便的集合类,且兼容Java标准类库。

(2)尽可能地避免采用含有指针的嵌套数据结构来保存小对象。

(3)考虑采用数字ID或者枚举类型以便替代String类型的主键。

(4)如果内存少于32GB,设置JVM参数-XX:+UseCom­pressedOops以便将8字节指针修改成4字节。与此同时,在Java 7或者更高版本,设置JVM参数-XX:+UseC­­­­­ompressedStrings以便采用8比特来编码每一个ASCII字符。

2. 内存回收

(1)获取内存统计信息:优化内存前需要了解集群的内存回收频率、内存回收耗费时间等信息,可以在spark-env.sh中设置SPARK_JAVA_OPTS=“-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps $ SPARK_JAVA_OPTS”来获取每一次内存回收的信息。

(2)优化缓存大小:默认情况Spark采用运行内存(spark.executor.memory)的60%来进行RDD缓存。这表明在任务执行期间,有40%的内存可以用来进行对象创建。如果任务运行速度变慢且JVM频繁进行内存回收,或者内存空间不足,那么降低缓存大小设置可以减少内存消耗,可以降低spark.storage.memoryFraction的大小。

3. 频繁GC或者OOM

首先要确定现象是发生在Driver端还是在Executor端,然后在分别处理。

Driver端:通常由于计算过大的结果集被回收到Driver端导致,需要调大Driver端的内存解决,或者进一步减少结果集的数量。

Executor端:

(1)以外部数据作为输入的Stage:这类Stage中出现GC通常是因为在Map侧进行map-side-combine时,由于group过多引起的。解决方法可以增加partition的数量(即task的数量)来减少每个task要处理的数据,来减少GC的可能性。

(2)以shuffle作为输入的Stage:这类Stage中出现GC的通常原因也是和shuffle有关,常见原因是某一个或多个group的数据过多,也就是所谓的数据倾斜,最简单的办法就是增加shuffle的task数量,比如在SparkSQL中设置SET spark.sql.shuffle.partitions=400,如果调大shuffle的task无法解决问题,说明你的数据倾斜很严重,某一个group的数据远远大于其他的group,需要你在业务逻辑上进行调整,预先针对较大的group做单独处理。

【Spark 磁盘调优】

在集群环境下,如果数据分布不均匀,造成节点间任务分布不均匀,也会导致节点间源数据不必要的网络传输,从而大大影响系统性能,那么对于磁盘调优最好先将数据资源分布均匀。除此之外,还可以对源数据做一定的处理:

1. 在内存允许范围内,将频繁访问的文件或数据置于内存中;

2. 如果磁盘充裕,可以适当增加源数据在HDFS上的备份数以减少网络传输;

3. Spark支持多种文件格式及压缩方式,根据不同的应用环境进行合理的选择。如果每次计算只需要其中的某几列,可以使用列式文件格式,以减少磁盘I/O,常用的列式有parquet、rcfile。如果文件过大,将原文件压缩可以减少磁盘I/O,例如:gzip、snappy、lzo。

 

开启推测机制

推测机制后,如果集群中,某一台机器的几个task特别慢,推测机制会将任务分配到其他机器执行,最后Spark会选取最快的作为最终结果。

在spark-default.conf 中添加:spark.speculation true

推测机制与以下几个参数有关:

1. spark.speculation.interval 100:检测周期,单位毫秒;

2. spark.speculation.quantile 0.75:完成task的百分比时启动推测;

3. spark.speculation.multiplier 1.5:比其他的慢多少倍时启动推测。

总结

Spark系统的性能调优是一个很复杂的过程,需要对Spark以及Hadoop有足够的知识储备。从业务应用平台(Spark)、存储(HDFS)、操作系统、硬件等多个层面都会对性能产生很大的影响。借助于多种性能监控工具,我们可以很好地了解系统的性能表现,并根据上面介绍的经验进行调整。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值