‘壹’ Hive优化之Hive的配置参数优化
Hive是大数据领域常用的组件之一,主要用于大数据离线数仓的运算,关于Hive的性能调优在日常工作和面试中是经常涉及的一个点,因此掌握一些Hive调优是必不可少的一项技能。影响Hive效率的主要因素有数据倾斜、数据冗余、job的IO以及不同底层引擎配置情况和Hive本身参数和HiveSQL的执行等。本文主要从建表配置参数方面对Hive优化进行讲解。
1. 创建一个普通表
table test_user1(id int, name string,code string,code_id string ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
2. 查看这张表的信息
DESCRIBE FORMATTED test_user1;
我们从该表的描述信息介绍建表时的一些可优化点。
2.1 表的文件数
numFiles表示表中含有的文件数,当文件数过多时可能意味着该表的小文件过多,这时候我们可以针对小文件的问题进行一些优化,HDFS本身提供了解决方案:
(1)Hadoop Archive/HAR:将小文件打包成大文件。
(2)SEQUENCEFILE格式:将大量小文件压缩成一个SEQUENCEFILE文件。
(3)CombineFileInputFormat:在map和rece处理之前组合小文件。
(4)HDFS Federation:HDFS联盟,使用多个namenode节点管理文件。
除此之外,我们还可以通过设置hive的参数来合并小文件。
(1)输入阶段合并
需要更改Hive的输入文件格式,即参数hive.input.format,默认值是org.apache.hadoop.hive.ql.io.HiveInputFormat,我们改成org.apache.hadoop.hive.ql.io.CombineHiveInputFormat。这样比起上面对mapper数的调整,会多出两个参数,分别是mapred.min.split.size.per.node和mapred.min.split.size.per.rack,含义是单节点和单机架上的最小split大小。如果发现有split大小小于这两个值(默认都是100MB),则会进行合并。具体逻辑可以参看Hive源码中的对应类。
(2)输出阶段合并
直接将hive.merge.mapfiles和hive.merge.mapredfiles都设为true即可,前者表示将map-only任务的输出合并,后者表示将map-rece任务的输出合并,Hive会额外启动一个mr作业将输出的小文件合并成大文件。另外,hive.merge.size.per.task可以指定每个task输出后合并文件大小的期望值,hive.merge.size.smallfiles.avgsize可以指定所有输出文件大小的均值阈值,默认值都是1GB。如果平均大小不足的话,就会另外启动一个任务来进行合并。
2.2 表的存储格式
通过InputFormat和OutputFormat可以看出表的存储格式是TEXT类型,Hive支持TEXTFILE, SEQUENCEFILE, AVRO, RCFILE, ORC,以及PARQUET文件格式,可以通过两种方式指定表的文件格式:
(1)CREATE TABLE ... STORE AS <file_format>:在建表时指定文件格式,默认是TEXTFILE
(2)ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT <file_format>:修改具体表的文件格式
如果要改变创建表的默认文件格式,可以使用set
hive.default.fileformat=<file_format>进行配置,适用于所有表。同时也可以使用set
hive.default.fileformat.managed = <file_format>进行配置,仅适用于内部表或外部表。
扩展:不同存储方式的情况
TEXT,
SEQUENCE和
AVRO文件是面向行的文件存储格式,不是最佳的文件格式,因为即便只查询一列数据,使用这些存储格式的表也需要读取完整的一行数据。另一方面,面向列的存储格式(RCFILE,
ORC, PARQUET)可以很好地解决上面的问题。关于每种文件格式的说明,如下:
(1)TEXTFILE
创建表时的默认文件格式,数据被存储成文本格式。文本文件可以被分割和并行处理,也可以使用压缩,比如GZip、LZO或者Snappy。然而大部分的压缩文件不支持分割和并行处理,会造成一个作业只有一个mapper去处理数据,使用压缩的文本文件要确保文件不要过大,一般接近两个HDFS块的大小。
(2)SEQUENCEFILE
key/value对的二进制存储格式,sequence文件的优势是比文本格式更好压缩,sequence文件可以被压缩成块级别的记录,块级别的压缩是一个很好的压缩比例。如果使用块压缩,需要使用下面的配置:set
hive.exec.compress.output=true; set io.seqfile.compression.type=BLOCK
(3)AVRO
二进制格式文件,除此之外,avro也是一个序列化和反序列化的框架。avro提供了具体的数据schema。
(4)RCFILE
全称是Record Columnar File,首先将表分为几个行组,对每个行组内的数据进行按列存储,每一列的数据都是分开存储,即先水平划分,再垂直划分。
(5)ORC
全称是Optimized Row Columnar,从hive0.11版本开始支持,ORC格式是RCFILE格式的一种优化的格式,提供了更大的默认块(256M)
(6)PARQUET
另外一种列式存储的文件格式,与ORC非常类似,与ORC相比,Parquet格式支持的生态更广,比如低版本的impala不支持ORC格式。
配置同样数据同样字段的两张表,以常见的TEXT行存储和ORC列存储两种存储方式为例,对比执行速度。
TEXT存储方式
总结: 从上图中可以看出列存储在对指定列进行查询时,速度更快, 建议在建表时设置列存储的存储方式 。
2.3 表的压缩
对Hive表进行压缩是常见的优化手段,一些存储方式自带压缩选择,比如SEQUENCEFILE支持三种压缩选择:NONE,RECORD,BLOCK。Record压缩率低,一般建议使用BLOCK压缩;
ORC支持三种压缩选择:NONE,ZLIB,SNAPPY。我们以TEXT存储方式和ORC存储方式为例,查看表的压缩情况。
配置同样数据同样字段的四张表,一张TEXT存储方式,另外三张分别是默认压缩方式的ORC存储、SNAPPY压缩方式的ORC存储和NONE压缩方式的ORC存储,查看在hdfs上的存储情况:
TEXT存储方式
默认压缩ORC存储方式
SNAPPY压缩的ORC存储方式
NONE压缩的ORC存储方式
总结 :可以看到ORC存储方式将数据存放为两个block,默认压缩大小加起来134.69M,SNAPPY压缩大小加起来196.67M,NONE压缩大小加起来247.55M,TEXT存储方式的文件大小为366.58M,且默认block两种存储方式分别为256M和128M,ORC默认的压缩方式比SNAPPY压缩得到的文件还小,原因是ORZ默认的ZLIB压缩方式采用的是deflate压缩算法,比Snappy压缩算法得到的压缩比高,压缩的文件更小。 ORC不同压缩方式之间的执行速度,经过多次测试发现三种压缩方式的执行速度差不多,所以建议采用ORC默认的存储方式进行存储数据。
2.4 分桶分区
Num Buckets表示桶的数量,我们可以通过分桶和分区操作对Hive表进行优化:
对于一张较大的表,可以将它设计成分区表,如果不设置成分区表,数据是全盘扫描的,设置成分区表后,查询时只在指定的分区中进行数据扫描,提升查询效率。要注意尽量避免多级分区,一般二级分区足够使用。常见的分区字段:
(1)日期或者时间,比如year、month、day或者hour,当表中存在时间或者日期字段时,可以使用些字段。
(2)地理位置,比如国家、省份、城市等
(3)业务逻辑,比如部门、销售区域、客户等等
与分区表类似,分桶表的组织方式是将HDFS上的一张大表文件分割成多个文件。分桶是相对分区进行更细粒度的划分,分桶将整个数据内容按照分桶字段属性值得hash值进行区分,分桶可以加快数据采样,也可以提升join的性能(join的字段是分桶字段),因为分桶可以确保某个key对应的数据在一个特定的桶内(文件),所以巧妙地选择分桶字段可以大幅度提升join的性能。通常情况下,分桶字段可以选择经常用在过滤操作或者join操作的字段。
创建分桶表
create
table test_user_bucket(id int, name string,code string,code_id string )
clustered by(id) into 3 buckets ROW FORMAT DELIMITED FIELDS TERMINATED
BY ',';
查看描述信息
DESCRIBE FORMATTED test_user_bucket
多出了如下信息
查看该表的hdfs
同样的数据查看普通表和分桶表查询效率
普通表
分桶表
普通表是全表扫描,分桶表在按照分桶字段的hash值分桶后,根据join字段或者where过滤字段在特定的桶中进行扫描,效率提升。
本文首发于: 数栈研习社
数栈是云原生—站式数据中台PaaS,我们在github上有一个有趣的开源项目: FlinkX
FlinkX是一个基于Flink的批流统一的数据同步工具,既可以采集静态的数据,比如MySQL,HDFS等,也可以采集实时变化的数据,比如MySQL
binlog,Kafka等,是全域、异构、批流一体的数据同步引擎,大家如果有兴趣,欢迎来github社区找我们玩~
‘贰’ 如何远程调试hadoop app在yarn服务端
配置hbase远程调试
打开/etc/hbase/conf/hbase-env.sh,找到以下内容:
# Enable remote JDWP debugging of major HBase processes. Meant for Core Developers
# export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8070"
# export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8071"
# export HBASE_THRIFT_OPTS="$HBASE_THRIFT_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8072"
# export HBASE_ZOOKEEPER_OPTS="$HBASE_ZOOKEEPER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8073"
如果想远程李世调式hbase-master进程,请去哪答肢掉对HBASE_MASTER_OPTS的注释,其他依次类推。注意,我这里使用的是cdh-4.3.0中举虚的hbase。
配置hive远程调试
停止hive-server2进程,然后以下面命令启动hive-server2
hive --service hiveserver --debug
进程会监听在8000端口等待调试连接。如果想更改监听端口,可以修改配置文件:${HIVE_HOME}bin/ext/debug.sh
如果Hadoop是0.23以上版本,debug模式启动Cli会报错:
ERROR: Cannot load this JVM TI agent twice, check your java command line for plicate jdwp options.
打开${Hadoop_HOME}/bin/hadoop,注释掉以下代码
# Always respect HADOOP_OPTS and HADOOP_CLIENT_OPTS
HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
配置yarn远程调试
请在以下代码添加调试参数:
if [ "$COMMAND" = "classpath" ] ; then
if $cygwin; then
CLASSPATH=`cygpath -p -w "$CLASSPATH"`
fi
echo $CLASSPATH
exit
elif [ "$COMMAND" = "rmadmin" ] ; then
CLASS='org.apache.hadoop.yarn.client.RMAdmin'
YARN_OPTS="$YARN_OPTS $YARN_CLIENT_OPTS"
elif [ "$COMMAND" = "application" ] ; then
class="org".apache.hadoop.yarn.client.cli.ApplicationCLI
YARN_OPTS="$YARN_OPTS $YARN_CLIENT_OPTS"
elif [ "$COMMAND" = "node" ] ; then
class="org".apache.hadoop.yarn.client.cli.NodeCLI
YARN_OPTS="$YARN_OPTS $YARN_CLIENT_OPTS"
elif [ "$COMMAND" = "resourcemanager" ] ; then
CLASSPATH=${CLASSPATH}:$YARN_CONF_DIR/rm-config/log4j.properties
CLASS='org.apache.hadoop.yarn.server.resourcemanager.ResourceManager'
YARN_OPTS="$YARN_OPTS $YARN_RESOURCEMANAGER_OPTS"
if [ "$YARN_RESOURCEMANAGER_HEAPSIZE" != "" ]; then
JAVA_HEAP_MAX="-Xmx""$YARN_RESOURCEMANAGER_HEAPSIZE""m"
fi
elif [ "$COMMAND" = "nodemanager" ] ; then
CLASSPATH=${CLASSPATH}:$YARN_CONF_DIR/nm-config/log4j.properties
CLASS='org.apache.hadoop.yarn.server.nodemanager.NodeManager'
YARN_OPTS="$YARN_OPTS -server $YARN_NODEMANAGER_OPTS"
if [ "$YARN_NODEMANAGER_HEAPSIZE" != "" ]; then
JAVA_HEAP_MAX="-Xmx""$YARN_NODEMANAGER_HEAPSIZE""m"
fi
elif [ "$COMMAND" = "proxyserver" ] ; then
CLASS='org.apache.hadoop.yarn.server.webproxy.WebAppProxyServer'
YARN_OPTS="$YARN_OPTS $YARN_PROXYSERVER_OPTS"
if [ "$YARN_PROXYSERVER_HEAPSIZE" != "" ]; then
JAVA_HEAP_MAX="-Xmx""$YARN_PROXYSERVER_HEAPSIZE""m"
fi
例如: 如果你想调试resourcemanager代码,请在elif [ "$COMMAND" = "resourcemanager" ] 分支内添加如下代码:
YARN_RESOURCEMANAGER_OPTS="$YARN_RESOURCEMANAGER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=6001"
其他进程,参照上面即可。
注意:端口不要冲突。
配置maprece远程调试
如果想要调试Map 或Rece Task,则修改bin/hadoop已经没用了,因为bin/hadoop中没有Map Task的启动参数。
此时需要修改mapred-site.xml
<property>
<name>mapred.child.java.opts</name>
<value>-Xmx800m -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000</value>
</property>
在一个TaskTracker上,只能启动一个Map Task或一个Rece Task,否则启动时会有端口冲突。因此要修改所有TaskTracker上的conf/hadoop-site.xml中的配置项:
<property>
<name>mapred.tasktracker.map.tasks.maximum</name>
<value>1</value>
</property>
<property>
<name>mapred.tasktracker.rece.tasks.maximum</name>
<value>1</value>
</property>
在Eclipse中使用方法:
打开eclipse,找到Debug Configurations...,添加一个Remout Java Application:
在source中可以关联到hive的源代码,然后,单击Debug按钮进入远程debug模式。
编写个jdbc的测试类,运行代码,这时候因为hive-server2端没有设置端点,故程序可以正常运行直到结束。
在hive代码中设置一个断点,如ExecDriver.java的execute方法中设置断点,然后再运行jdbc测试类。
‘叁’ Hive内置函数之时间函数
零、生产埋凳渗常用组合方式
(0.1)离线数仓获取昨天的日期作为分区,格式yyyyMMdd
regexp_replace(date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1) ,'-','')
或者
date_format(date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1),'yyyyMMdd')
一、源码部分
Hive的函数类为:org.apache.hadoop.hive.ql.exec.FunctionRegistry
二、常用时间函数
对于函数,除了知道怎么用,还需要知道返回值是什么类型,这里给出官方文档,文档中给出了函数的返回值类型
官方文档见: https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF#LanguageManualUDF-DateFunctions
(2.1)from_unixtime(bigint unixtime[, string format])
示例:
select from_unixtime(1591627588); -- 2020-06-08 22:46:28
select from_unixtime(1591627588,'yyyyMMddHHmmss'); -- 20200608224628
(2.2)unix_timestamp()、弯脊unix_timestamp(string date)、unix_timestamp(string date, string pattern)
示例:
select unix_timestamp('2020-06-08 22:50:00'); -- 1591627800
select unix_timestamp('20200608225000','yyyyMMddHHmmss'); -- 1591627800
(2.3)to_date(string timestamp)
示例:
SELECT to_date('2009-07-30 04:17:52'); -- 2009-07-30
(2.4)year(string date)、month(string date)、day(string date)、hour(string date)、minute(string date)、second(string date)
这些函数是差不多的,都是从一个时间字符串中抽取出某个特定的时间字段。具有相同功能的还有extract(field FROM source)函数
示例:
SELECT day('2009-07-29 20:30:40'); -- 29
SELECT minute('2009-07-29 20:30:40'); -- 30
(2.5)date_add(date/timestamp/string startdate, tinyint/smallint/int days)、date_sub(date/timestamp/string startdate, tinyint/smallint/int days)
这两个功能是类似的
示粗搏例:
SELECT date_add('2009-07-30 20:50:59', 1); -- 2009-07-31
(2.6)datediff(string enddate, string startdate)
截图中结果是错误的,应该为-1。
示例:
SELECT datediff('2009-06-30', '2009-07-02'); -- -2
SELECT datediff('2009-07-30', '2009-07-28'); -- 2
(2.7)current_date、current_timestamp
这两个函数使用desc function extended 查看会报错
示例:
(2.8)date_format(date/timestamp/string ts, string fmt)
示例:
SELECT date_format('2015-04-08', 'yyyyMMdd'); -- 20150408
‘肆’ hive起不来,命令行输hive时,报错信息如下:
可以再/hive/bin目录下输入 hive -hiveconf hive.root.logger=debug,console 然后再输入你报错的hql语句,会有更详细的错误提示,这个调试模式很管用的,希望能帮上。