个人总结-乱七八糟踩坑

578 阅读29分钟

[TOC]


问题定位与解决

CPU 100% 定位

思路:功能问题,通过日志单步调试;性能问题,确定哪个进程,哪个线程,哪段代码问题

  1. 找到最耗CPU的进程 top -c (键入P,按照CPU使用率排序):找到CPU最高的进程pid:10765
  2. top -Hp pid :一个进程的线程信息列表,找到cpu使用最高的线程id: 10804
  3. 将线程id转换成16进制(堆栈里线程id用16进制表示):printf "%x\n" 10804
    2a34
  4. 查看堆栈:jstack 10765 | grep '0x2a34' -C5 --color

内存诊断

  1. jmap -dump:live,format=b,file=文件名 [pid]
  2. jhat -J-Xmx1024M [file]
  3. 执行后等待console 中输入start HTTP server on port 7000 即可使用浏览器访问 IP:7000

内存溢出与泄露 定位

概念与区别

  • 内存溢出: out of memory

是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

  • 内存泄露: memory leak

是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory!

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。

内存泄露

内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。

一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出. 以发生的方式来分类,内存泄漏可以分为4类:

  1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
  4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及 时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。

真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到

内存溢出

引起内存溢出的原因有很多种,小编列举一下常见的有以下几种:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的BUG;
  5. 启动参数内存值设定的过小

内存溢出的解决方案:

第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

重点排查以下几点:

1. 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内
   存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查
   询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
2. 检查代码中是否有死循环或递归调用。
3. 检查是否有大循环重复产生新对象实体。
4. 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内
   存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查
   询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
5. 检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

第四步,使用内存查看工具动态查看内存使用情况

记录

Redis

redis-cli

  • redis-cli -h hostname -p port -a password

get 58_b_end 46728531872015

sismember

srom

tomcat

  • tomcat无法启动:注释远程调试

    tomcat/bin/catalina.sh 中的 #CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=19000,server=y,suspend=n"


Mysql

  • 连接服务器 mysql -uusername -p123456 -hhostname --port=5003

Visualbox 配置

  • 配置Ubuntu server 固定ip
  1. 为了配置访问外部网路以及和宿主机互联,在vb 管理->主机网络管理器->手动配置网卡->配置仅主机网络属性:ip和网络掩码
  2. 具体虚拟机(如ubuntu),设置->网络->网卡1 选择网络地址转换(NAT)混杂模式(全部允许), 网卡2:选择仅主机网络
  3. 启动虚拟主机,配置ip sudo vim /etc/network/interfaces,配置如下
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet static
address 192.168.1.101
netmask 255.255.255.0
  1. 配置域名解析DNS, sudo vim /etc/resolvconf/resolv.conf.d/base
# 根据个人电脑设置
# ipconfig /all windows查看
nameserver      8.8.8.8
  1. sudo reboot 重启即可

windows

指令

  • 端口查看:netstat -aon | findstr 16661
  • 任务kill:taskkill /F /PID 14560

Linux

指令

date

获取日期:date +%Y%m%d -d '+1 day/min'

转换成时间戳:date -d '2013-2-22 22:14' +%s

时间戳转换成日期:date -d @1512088011 +'%Y%m%d'

netstat

常用

查看端口号:netstat -apn

参数

-a (all)显示所有选项,默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化成数字。
-l 仅列出有在 Listen (监听) 的服務状态

-p 显示建立相关链接的程序名
-r 显示路由信息,路由表
-e 显示扩展信息,例如uid等
-s 按各个协议进行统计
-c 每隔一个固定时间,执行该netstat命令。

提示:LISTEN和LISTENING的状态只有用-a或者-l才能看到

lsof

常用

查看进程使用的文件: lsof -p (list open file),-p指定pid

列出谁在使用某个端口:lsof -i :3306

列出所有网络文件系统: lsof -N

列出多个进程号对应的文件信息:lsof -p 1,2,3

参数

-a: 表示两个参数都必须满足时才显示结果(-a:and的意思)
-c string :显示COMMAND列中包含指定字符的进程所有打开的文件(-c:COMMAND的意思)
-u username :显示所属user进程打开的文件(-u:user的意思)
-t /path/file:显示/path/file的进程id号
-U:显示所有unix domain socket files
-g gid: 显示归属gid的进程情况
-s:列出打开文件的大小,如果没有大小,则留下空白(-s:size的意思)
+d /DIR/: 显示目录下被进程打开的文件
+D /DIR/ :同上,但是会搜索目录下的所有目录,时间相对较长
-i :用以显示符合条件的进程情况
(1)如果-i后不跟任何参数,则表示显示所有Internet and x.25 (HP-UX) network files
(2)如果-i后跟一些参数,则表示相关的文件

grep

参数

-e: 使用正则搜索
-i: 不区分大小写
-v: 查找不包含指定内容的行
-w: 按单词搜索
-c: 统计匹配到的次数
-n: 显示行号
-r: 逐层遍历目录查找
-A: 显示匹配行及前面多少行, 如: -A3, 则表示显示匹配行及前3行
-B: 显示匹配行及后面多少行, 如: -B3, 则表示显示匹配行及后3行
-C: 显示匹配行前后多少行,   如: -C3, 则表示显示批量行前后3行
--color: 匹配到的内容高亮显示
--include: 指定匹配的文件类型
--exclude: 过滤不需要匹配的文件类型

top

常用:top -c, top -Hp pid, top -d 1

语法

top [-] [d] [p] [q] [c] [C] [S] [s] [n]

参数

-d 指定每两次屏幕信息刷新之间的时间间隔。当然用户可以使用s交互命令来改变之。
-H 列出所有的线程
-p 通过指定监控进程ID来仅仅监控某个进程的状态。
-q 该选项将使top没有任何延迟的进行刷新。如果调用程序有超级用户权限,那么top将以尽可能高的优先级运行。
-S 指定累计模式。
-s 使top命令在安全模式中运行。这将去除交互命令所带来的潜在危险。
-i  使top不显示任何闲置或者僵死进程。
-c 显示整个命令行而不只是显示命令名。

交互命令

- Ctrl+L 擦除并且重写屏幕。
- h或者? 显示帮助画面,给出一些简短的命令总结说明。
- k 终止一个进程。系统将提示用户输入需要终止的进程PID,以及需要发送给该进程什么样的信号。一般的终止进程可以使用15信号;如果不能正常结束那就使用信号9强制结束该进程。默认值是信号15。在安全模式中此命令被屏蔽。
- i 忽略闲置和僵死进程。这是一个开关式命令。
- q 退出程序。
- r 重新安排一个进程的优先级别。系统提示用户输入需要改变的进程PID以及需要设置的进程优先级值。输入一个正值将使优先级降低,反之则可以使该进程拥有更高的优先权。默认值是10。
- s 改变两次刷新之间的延迟时间。系统将提示用户输入新的时间,单位为s。如果有小数,就换算成m s。输入0值则系统将不断刷新,默认值是5 s。需要注意的是如果设置太小的时间,很可能会引起不断刷新,从而根本来不及看清显示的情况,而且系统负载也会大大增加。
- f或者F 从当前显示中添加或者删除项目。
- o或者O改变显示项目的顺序。
- l 切换显示平均负载和启动时间信息。
- m 切换显示内存信息。
- t 切换显示进程和CPU状态信息。
- c 切换显示命令名称和完整命令行。
- M 根据驻留内存大小进行排序。
- P 根据CPU使用百分比大小进行排序。
- T 根据时间/累计时间进行排序。
- W 将当前设置写入~/.toprc文件中。这是写top配置文件的推荐方法。
- Shift+M 可按内存占用情况进行排序。

统计信息

前五行是系统整体的统计信息

任务队列信息, 同 uptime 命令的执行结果。其内容如下:

10:37:35	当前时间
up 25 days, 17:29	系统运行时间,格式为时:分
1 user	当前登录用户数
load average: 0.00, 0.02, 0.05	系统负载,即任务队列的平均长度。
三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。

进程和CPU的信息

Tasks: 104 total	进程总数
1 running	正在运行的进程数
103 sleeping	睡眠的进程数
0 stopped	停止的进程数
0 zombie	僵尸进程数
Cpu(s):  0.1%us	用户空间占用CPU百分比
0.0%sy	内核空间占用CPU百分比
0.0%ni	用户进程空间内改变过优先级的进程占用CPU百分比
99.9%id	空闲CPU百分比
0.0%wa	等待输入输出的CPU时间百分比
0.0% hi	 
0.0% si	 
0.0%st	 

内存信息

Mem:   2067816k total	物理内存总量
2007264k used	使用的物理内存总量
60552k free	空闲内存总量
73752k buffers	用作内核缓存的内存量
Swap:   524284k total	交换区总量
315424k used	使用的交换区总量
208860k free	空闲交换区总量
625832k cached	缓冲的交换区总量。
内存中的内容被换出到交换区,而后又被换入到内存,但使用过的交换区尚未被覆盖,该数值即为这些内容已存在于内存中的交换区的大小。
相应的内存再次被换出时可不必再对交换区写入。

进程信息区 统计信息区域的下方显示了各个进程的详细信息。首先来认识一下各列的含义。

序号	列名	含义
a	PID	进程id
b	PPID	父进程id
c	RUSER	Real user name
d	UID	进程所有者的用户id
e	USER	进程所有者的用户名
f	GROUP	进程所有者的组名
g	TTY	启动进程的终端名。不是从终端启动的进程则显示为 ?
h	PR	优先级
i	NI	nice值。负值表示高优先级,正值表示低优先级
j	P	最后使用的CPU,仅在多CPU环境下有意义
k	%CPU	上次更新到现在的CPU时间占用百分比
l	TIME	进程使用的CPU时间总计,单位秒
m	TIME+	进程使用的CPU时间总计,单位1/100秒
n	%MEM	进程使用的物理内存百分比
o	VIRT	进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
p	SWAP	进程使用的虚拟内存中,被换出的大小,单位kb。
q	RES	进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
r	CODE	可执行代码占用的物理内存大小,单位kb
s	DATA	可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
t	SHR	共享内存大小,单位kb
u	nFLT	页面错误次数
v	nDRT	最后一次写入到现在,被修改过的页面数。
w	S	进程状态。
D=不可中断的睡眠状态
R=运行
S=睡眠
T=跟踪/停止
Z=僵尸进程
x	COMMAND	命令名/命令行
y	WCHAN	若该进程在睡眠,则显示睡眠中的系统函数名
z	Flags	任务标志,参考 sched.h
默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。

更改显示内容

- 通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。
- 按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z可以将相应的列向左移动。最后按回车键确定。
按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转。

awk

awk -F":" '{for (f=1; f <= NF; f+=1) {if (f ~ /\"id\"|\"local\"/) {print(f+1)}}}'

Q&S

awk -F"\t" '{if($2 == "1"){print $1}}' | less ,无法匹配出数据,grep 后面 跟less不会对查询结果标红


FE

  • 修改css后页面没有刷新

    缓存问题,可以在css加载语句的后面添加?v=1.0.0让浏览器重新加载资源。

  • js find 过滤器

    var rs = extObj.find("tr[role!='head'][role!='foot']"); 实现多重条件选择


Idea

  • Jetty 热部署

    1. pom 文件
    <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <configuration>
            <scanIntervalSeconds>1</scanIntervalSeconds> 
            <stopPort>9977</stopPort>
            <stopKey>foo</stopKey> <scanIntervalSeconds>0</scanIntervalSeconds>
            <connectors>
                <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">   
                    <port>8080</port>
                    <maxIdleTime>60000</maxIdleTime>
                </connector>
            </connectors>
            <webAppConfig>
            <contextPath>/</contextPath>
            </webAppConfig>
        </configuration>
    </plugin>
    
    1. ctrl+shift+f9 重新build当前文件 ctrl+f9 重新build整个目录
    2. project structure -> Modules paths 设置build的class文件目录(注意放到web项目的目录下面,如target\nss\WEB-INF\classes

maven

下载source

mvn dependency:resolve -Dclassifier=sources


java

Q&S

问题

2017-10-25 21:52:17,528 FATAL [main] org.apache.hadoop.mapred.YarnChild: Error running child : java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:658)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)
	at com.hadoop.compression.lzo.LzoCompressor.realloc(LzoCompressor.java:261)
	at com.hadoop.compression.lzo.LzoCompressor.init(LzoCompressor.java:270)
	at com.hadoop.compression.lzo.LzoCompressor.reinit(LzoCompressor.java:217)
	at com.hadoop.compression.lzo.LzoCompressor.<init>(LzoCompressor.java:199)
	at com.hadoop.compression.lzo.LzoCodec.createCompressor(LzoCodec.java:168)
	at org.apache.hadoop.io.compress.CodecPool.getCompressor(CodecPool.java:150)
	at com.hadoop.compression.lzo.LzopCodec.getCompressor(LzopCodec.java:171)
	at com.hadoop.compression.lzo.LzopCodec.createOutputStream(LzopCodec.java:72)
	at org.apache.hadoop.mapreduce.lib.output.TextOutputFormat.getRecordWriter(TextOutputFormat.java:270)
	at org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.getRecordWriter(MultipleOutputs.java:475)
	at org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.write(MultipleOutputs.java:433)
	at com.bj58.wireless.statistics.app.job.WuxianDw58AppActionTotalJob$Reducer1.reduce(WuxianDw58AppActionTotalJob.java:134)
	at com.bj58.wireless.statistics.app.job.WuxianDw58AppActionTotalJob$Reducer1.reduce(WuxianDw58AppActionTotalJob.java:118)
	at org.apache.hadoop.mapreduce.Reducer.run(Reducer.java:171)
	at org.apache.hadoop.mapred.ReduceTask.runNewReducer(ReduceTask.java:637)
	at org.apache.hadoop.mapred.ReduceTask.run(ReduceTask.java:397)
	at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:163)
	at java.security.AccessController.doPrivileged(Native Method)
	at javax.security.auth.Subject.doAs(Subject.java:415)
	at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1671)
	at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158) 

解决: reduce 阶段,申请使用操作系统的内存,没有控制好,出现了内存泄露,导致的内存溢出。申请内存超过8192MB和不能被1024MB整除的资源请求

问题
解决

JVM

  • -Xmx Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定;
  • -Xms Java Heap初始值,Server端JVM最好将-Xms和-Xmx设为相同值,开发测试机JVM可以保留默认值;
  • -Xmn Java Heap Young区大小,不熟悉最好保留默认值;
  • -Xss 每个线程的Stack大小,不熟悉最好保留默认值;

hadoop

Notice

  • Map和Reduce阶段不能对集合进行写操作。即使写了在reduce阶段也读不出来。

    map和reduce会在不同的服务器上操作,全局变量无法生效。 1、通过Configuration保存String变量; 2、在reduce或者map阶段读取小文件存入内存,进行操作。

  • reduce阶段,==Iterable的迭代只能遍历一次==,第二次遍历后没有数据,所以会造成结果匹配错误。所以应该先将数据保存,再遍历。

  • set class 要在set configuration之后这样才会保存配置

  • 如果reduce的Value不输出值得话,返回类型用NullWritable.get(),这样可以保证文件中在key的后面不会出现tab

  • Mapper Reducer类的子类要是static,否则会报初始化错误

  • ==对于MR的return要谨慎使用,防止跳出,部分结果无法输出==

hdfs 操作指令

  • hadoop fs -stat [option] hdfsfilepath
%b:打印文件大小(目录为0)
%n:打印文件名
%o:打印block size (我们要的值)
%r:打印备份数
%y:打印UTC日期 yyyy-MM-dd HH:mm:ss
%Y:打印自1970年1月1日以来的UTC微秒数
%F:目录打印directory, 文件打印regular file
  • hadoop job -status jobid job_1509011180094_5418072

    结果说明:

Uber job : false-----uber模式:false,Uber模式简单地可以理解成JVM重用。
以Uber模式运行MR作业,所有的Map Tasks和Reduce Tasks将会在ApplicationMaster所在的容器(container)中运行,
也就是说整个MR作业运行的过程只会启动AM container,因为不需要启动
mapper 和reducercontainers,所以AM不需要和远程containers通信,整个过程简单了。

Number of maps: 13702 -----map总数:
Number of reduces: 500 -----reduces总数
map() completion: 1.0  
reduce() completion: 1.0  
Job state: SUCCEEDED -----job状态  
retired: false  
reason for failure: -----failure原因  

Counters: 58 -----counter总数  
    File System Counters -----这个group表示job与文件系统交互的读写统计
        FILE: Number of bytes read=0 -----job读取本地文件系统的文件字节数。假定我们当前map的输入数
        据都来自于HDFS,那么在map阶段,这个数据应该是0。但reduce在在执
        行前,它的输入数据是经过Shuffle的merge后存储在reduce端本地磁盘
        中,所以这个数据就是所有reduce的总输入字节数。
        FILE: Number of bytes written=5654852533 -----map的中间结果都会spill到本地磁盘中,在map执行完后,形成
        最终的spill文件。所以map端这里的数据就表示MapTask往本地磁盘
        中共写了多少字节。与Map端相对应的是,reduce端在Shuffle时,会
        不断拉取Map端的中间结果,然后做merge并不断spill到自己的本地
        磁盘中。最终形成一个单独文件,这个文件就是reduce的输入文件。
        FILE: Number of read operations=0 -----
        FILE: Number of large read operations=0
        FILE: Number of write operations=0
        HDFS: Number of bytes read=3560544443952 -----job执行过程中,累计写入HDFS的数据大小,整个job执行过程中
        ,只有map端运行时,才会从HDFS读取数据,这些数据不限于源文件
        内容,还包括所有map的split元数据。所以这个值应该比
        FileInputFormatCounter.BYTES_READ要略大些。
        HDFS: Number of bytes written=317076139 -----Reduce的最终结果都会写入HDFS,就是一个Job执行结果的总量。
        HDFS: Number of read operations=70010
        HDFS: Number of large read operations=0
        HDFS: Number of write operations=4491
        VIEWFS: Number of bytes read=0
        VIEWFS: Number of bytes written=0
        VIEWFS: Number of read operations=0
        VIEWFS: Number of large read operations=0
        VIEWFS: Number of write operations=0
        
    Job Counters -----这个group描述与job调度相关的统计
        Killed map tasks=4
        Launched map tasks=13706 -----此job启动了多少个map task
        Launched reduce tasks=500 -----此job启动了多少个reduce task
        Data-local map tasks=13043 -----Job在被调度时,如果启动了一个data-local(源文件的副本在执行map task的TaskTracker本地)
        Rack-local map tasks=663 ----- 处理的文件不在map task节点上
        Total time spent by all maps in occupied slots (ms)=437335720 -----所有map task占用slot的总时间,包含执行时间和创建/销毁子JVM的时间
        Total time spent by all reduces in occupied slots (ms)=83963148 -----
        Total time spent by all map tasks (ms)=218667860
        Total time spent by all reduce tasks (ms)=27987716
        Total vcore-seconds taken by all map tasks=218667860
        Total vcore-seconds taken by all reduce tasks=27987716
        Total megabyte-seconds taken by all map tasks=557165707280
        Total megabyte-seconds taken by all reduce tasks=128631542736
        
    Map-Reduce Framework -----这个Countergroup包含了相当多的job执行细节数据。
    这里需要有个概念认识是:一般情况下,record就表示一行数据,
    而相对的byte表示这行数据的大小是多少,这里的group
    表示经过reduce merge后像这样的输入形式{"aaa",[5,2,8,...]}
        "Map input records=4486906030" -----所有MapTask从HDFS读取的文件总行数
        Map output records=93940285 -----MapTask的直接输出record是多少,就是在map方法中调用
        context.write的次数,也就是未经过Combine时的原生输出条数。
        Map output bytes=5011599765 -----Map的输出结果key/value都会被序列化到内存缓冲区中,
        所以这里的bytes指序列化后的最终字节之和。
        Map output materialized bytes=3532812262 -----map 输出物化到磁盘的数据量,
        也就是reduce shuffle的数据量
        Input split bytes=2839207
        "Combine input records=93940285" -----Combiner是为了尽量减少需要拉取和移动的数据
        "Combine output records=79274144" -----经过Combiner后,相同key的数据经过压缩,
        在map端自己解决了很多重复数据,表示最终在map端中间
        文件中的所有条目数
        Reduce input groups=13757989 -----Reduce总共读取了多少个这样的groups,
        等于reduce处理的key个数
        "Reduce shuffle bytes=3532812262" -----Reduce端的copy线程总共从map端抓去了多少的中间数据
        ,表示各个MapTask最终的中间文件总和。
        "Reduce input records=79274144" -----如果有Combiner的话,那么这里的数值就会等于Map端
        Combiner运算后的最后条数,如果没有,那么就会等于Map的输出条数
        Reduce output records=0 -----所有reduce执行后输出的总条目数
        "Spilled Records=79274144" -----spill过程在map和reduce端都会发生,
        这里统计的是总共从内存往磁盘中spill了多少条数据。
        Shuffled Maps =6851000 -----每个reduce几乎都得从所有Map端拉取数据,
        每个copy线程拉取成功一个map的数据,那么增1,
        所以它的总数基本等于reduce number*(map number - fiald)
        Failed Shuffles=0 -----copy线程在抓取map端中间数据时,
        如果因为网络连接异常或是IO异常,所引起的Shuffle错误次数。
        "Merged Map outputs=6851000" -----记录着Shuffle过程中总共经历了多少次merge动作
        "GC time elapsed (ms)=2890881"
        CPU time spent (ms)=299372980 -----job运行使用的cpu时间,是衡量任务的计算量
        总结:任务运行使用的CPU时间=counter:
        "Map-Reduce Framework:CPU time spent (ms)"
        Physical memory (bytes) snapshot=12848748335104 -----进程的当前物理内存使用大小
        Virtual memory (bytes) snapshot=45156357689344 -----进程的当前虚拟内存使用大小
        Total committed heap usage (bytes)=31420302491648 -----获取jvm的当前堆大小
        
     SHUFFLECOUNTER
        SHUFFLE_IDLE_TIME=21427585
        SHUFFLE_TOTAL_TIME=25507722
        
    HIVE
        CREATED_FILES=1
            
    Shuffle Errors -----这组内描述Shuffle过程中的各种错误情况发生次数,
    基本定位于Shuffle阶段copy线程抓取map端中间数据时的各种错误。
        BAD_ID=0 -----每个map都有一个ID,
        如attempt_201109020150_0254_m_000000_0,
        如果reduce的copy线程抓取过来的元数据中的这个ID不是标准格式,
        那么此Counter会增加。
        CONNECTION=0 -----表示copy线程建立到map端的连接有误。
        IO_ERROR=0 -----Reduce的copy线程如果在抓取map端数据时出现IOException,
        那么这个值会相应增加。
        WRONG_LENGTH=0 -----map端的那个中间结果是有压缩好的有格式数据,
        它有两个length信息:元数据大小和压缩后数据大小。
        如果这两个length信息传输的有误,那么此Counter会增加。
        WRONG_MAP=0 -----每个copy线程当然是有目的的:为某个reduce抓取
        某些map的中间结果,如果当前抓取的map数据不是copy
        线程之前定义好的map,那么就表示把数据拉错了。
        WRONG_REDUCE=0 -----与上述描述一致,如果抓取的数据表示它不是
        为此reduce而准备的,那还是拉错数据了。
        DESERIALIZE_ERRORS=0
        
    File Input Format Counters 
                Bytes Read=0
                
    File Output Format Counters 
                Bytes Written=0

Hadoop优化

MR job 流程

Map

shuffle

  • io.sort.mb:100m

    1. 存储map中间数据的缓存默认大小,当map任务产生了非常大的中间数据时可以适当调大该参数,使缓存能容纳更多的map中间数据,而不至于大频率的IO磁盘,当系统性能的瓶颈在磁盘IO的速度上,可以适当的调大此参数来减少频繁的IO带来的性能障碍。
    2. 查看日志,spill次数多说明设置太低。(根据map的输出量进行设置)
  • io.sort.spill.percent:80%

    达到一定百分比,从后台进程对buffer进行排序,然后spill到磁盘。如果map的输出基本有序可以适当提高这个阈值。

  • io.sort.factor:10

    1. 最多能有多少并行的stream向merge文件中写入
    2. 当一个map task执行完之后,本地磁盘上(mapred.local.dir)有若干个spill文件,map task最后做的一件事就是执行merge sort,把这些spill文件合成一个文件(partition),有时候我们会自定义partition函数,就是在这个时候被调用的。
    3. merge sort会生成两个文件,一个是数据文件,一个是index:记录每个不同的key在数据文件中的偏移量(这就是partition)
  • min.num.spill.for.combine:3

    当job中设定了combiner,并且spill数最少有3个的时候, 那么combiner函数就会在merge产生结果文件之前运行。减少写入到磁盘文件的数据数量,同样是为了减少对磁盘的读写频率,有可能达到优化作业的目的。

  • mapred.compress.map.output:false

    那么map在写中间结果时,就会将数据压缩后再写入磁盘,读结果时也会采用先解压后读取数据。cpu换IO

  • mapred.map.output.compression.codec:org.apache.hadoop.io.compress.De faultCodec(GzipCodec,LzoCodec,BZip2Codec,LzmaCodec)
    当采用map中间结果压缩的情况下,用户还可以选择压缩时采用哪种压缩格式进行压缩

reduce

优化场景

  • Map逻辑处理后数据被展开,写磁盘次数剧增,可以观察日志中的spill次数,调整各个参数

  • 中间结果能不展开就不展开,尽量缩小Mapper和reducer之间的数据传递

  • 处理速度很慢时候首先要怀疑Mapper和Reducer之间传输数据量过大

  • 观察GC的情况,有时候是因为内存占用量高,频繁GC,严重影响处理速

  • 适当控制mapper的数量,特别是有distribute cache的场景

  • distribute cache

    • 加载的数据能不用hashmap就尽量不要用,hashmap会使得内存占用量是原数据的5-10倍。
    • 加载的数据要尽可能简单,如果有复杂的处理逻辑可以单独开辟Mapper Reducer进行一轮处理
    • 避免每次mapper都要处理一遍,尽可能减少distribute cache的数据量

hadoop配置说明

tasktracker.http.threads:

决定作为server端的map用于提供数据传输服务的线程数  

mapred.reduce.parallel.copies:

决定作为client端的reduce同时从map端拉取数据的并行度(一次同时从多少个map拉数据)

Hive

Notice

  • hive 查询,字段无法进行单引号比较,如p8='2',无法查到数据

    当某一字段数据全是纯数字字符串的时候,它会自动转成数字去做比较。

  • 10位的时间戳值,即1970-1-1至今的秒,可以用from_unixtime()转为时间,而13位的所谓毫秒的是不可以的;from_unixtime(cast(substring(t3.time ,1,10)as BIGINT), 'yyyyMMdd HH:mm:ss')

  • insert overwrite/into table 只会有mapjob,没有reducejob

  • 增加reduce数目

    1. set hive.exec.reducers.bytes.per.reducer=500000000;
    2. set mapred.reduce.tasks = 15;
  • 设置reduce的文件大小
    set hive.merge.size.per.task = 10000000; set hive.merge.mapfiles=false; set hive.groupby.skewindata=true;

HQL

  • 创建hive表
create external table tablename(
    pid int,
    imei string,
    userid string,
    cates array<int>,
    cities array<int>,
    pv int
) commment ''
partition by (dt string)
row format delimited
fields terminated by '\001'
collection items terminated by '\002'
map keys terminated by '\003';
  • desc tablename;

  • show partitions tablename;

  • 修改表名

    alter table oldname rename to newname;

  • 增加列

    alter table tablename add columns (c1 type, c3 type);

  • hive增加分区映射到文件

    alter table tablename drop if exists partition(dt='20171130');
    alter table tablename add if not exists partition(dt='20171130') location 'path';

  • 修改表的分割字符

    alter table tablename set SERDEPROPERTIES('field.delim'='\t');

  • 修改字段顺序

    alter table tablename change column cololdname colnewname coltype after colname2;

alter table tablename change column c1(string);

Hive 和hadoop 配置说明

Hive 配置说明

Hadoop 配置说明

mapred.compress.map.output ##指定map的输出是否压缩。有助于减小数据量,减小io压力,但压缩和解压有cpu成本,需要慎重选择压缩算法。

mapred.map.output.compression.codec ##map输出的压缩算法

mapred.output.compress ##reduce输出是否压缩

mapred.output.compression.codec ##控制mapred的输出的压缩的方式

hive.exec.compress.intermediate=true; ##hive中间数据压缩

set hive.exec.compress.intermediate=true;
set mapred.map.output.compression.codec= org.apache.hadoop.io.compress.SnappyCodec set mapred.map.output.compression.codec=com.hadoop.compression.lzo.LzoCodec;

set hive.exec.compress.output=false; ##Hive reduce最终数据不压缩

set hive.exec.compress.output=true;
set mapred.output.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;

set hive.merge.smallfiles.avgsize=256000000; ###设置输出文件的平均值

Hive Job 细节优化

  • map

set mapred.min.split.size=1;
set mapred.max.split.size=256000000;

  • reduce

set mapred.reduce.tasks=100;--直接指定Reduce个数
set mapred.exec.reducers.bytes.per.reducer=1G;

  • map 与reduce过程中

set io.sort.mb;--增大Mapper输出buffer的大小,避免Spill的发生
set io.sort.factor;--一次性能够合并更多的数据
set sort mapred.reduce.slowstart.completed.maps=0.05;--Reduce端copy的进度
set mapred.reduce.parallel.copies;--可以决定作为client端的Reduce同时从Map端拉取数据的并行度

  • 文件格式

set hive.default.fileformat = SequenceFile;
set hive.exec.compress.output = true;

对于sequencefile,有record和block两种压缩方式可选,block压缩比更高

set mapred.output.compression.type = BLOCK;
set hive.hadoop.supports.splittable.combineinputformat=true;--小文件合并

  • Job 整体优化
    • job 执行模式

set hive.exec.mode.local.auto;--自动开启local mr模式
set hive.exec.mode.local.auto.tasks.max;--文件数量
set hive.exec.mode.local.auto.inputbytes.max;--数据量大小

  • jvm重用

set mapred.job.reuse.jvm.num.tasks=5;--一个jvm运行多次任务之后再退出

  • 索引
  • join

set hive.auto.convert.join = true;

Hive会自动判断当前的join操作是否合适做Map join

  • 数据倾斜

set hive.map.aggr=true;
set hive.groupby.skewindata;

Reduce操作的时候,拿到的key并不是所有相同值给同一个Reduce,而是随机分发,然后Reduce做聚合,做完之后再做一轮MR,拿前面聚合过的数据再算结果

  • sql 整体优化
    • job 并行
      set hive.exec.parallel = true; set hive.exec.parallel.thread.number;

    • 减少Job数
      group by 代替 join