hive、hbase、mysql数据传输,hive优化实践(一)

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流,qq911283415。 https://blog.csdn.net/HaixWang/article/details/81268861

此次实践缘由

hive自去年学习后,就一直放着少有使用了,前几天接到面试,说hive这块,让再多学习学习。
所以有了这次实践,复习一把。
主要是想做些优化,以及尝试模拟出数据倾斜并解决倾斜的问题

关于实验

实验的前半部分大部分参考的林子雨实验室的案例
林博士团队的案例,确实讲得很详细,
关于这一部分,我在走一遍流程的前提下,提炼下要素,并对原案例进行一些更深入的观察。这些内容大都使用黄色背景高亮

我的重心是,hive做mapreduce工作这一块的优化,以及尝试模拟出数据倾斜并解决倾斜的问题。

hive、hbase、mysql数据传输

这部分,大都来自林子雨实验室

关于集群:Cloudera CDH5.13.2,搭建过程可参考此文
这里写图片描述

数据集下载https://pan.baidu.com/s/1nuOSo7B
【说句题外话,用了一年多的破解版网盘,我欠百度网盘一个会员。。。等秋招顺利拿了offer】

数据集预览

mkdir -p /home/guest/bigdatacase/dataset

scp ~/elk/user.zip user@your_remote_ipaddress:/home/guest/bigdatacase/dataset

unzip /home/guest/bigdatacase/dataset/user.zip

有两个文件:raw_user.csv和small_user.csv。

head /usr/local/bigdatacase/dataset

这里写图片描述

每行记录都包含5个字段,数据集中的字段及其含义如下:

user_id (用户id)
item_id (商品id)
behaviour_type (包括浏览、收藏、加购物车、购买,对应取值分别是1、2、3、4)
user_geohash (用户地理位置哈希值,有些记录中没有这个字段值,后面用脚本做数据预处理时把这个字段全部删除)
item_category (商品分类)
time (该记录产生时间)

删除csv表头如果是更复杂的,hive自定义UDF处理可参考以前这篇文章
sed -i '1d' small_user.csv

数据预处理

为每行记录增加一个id字段(让记录具有唯一性)、增加一个省份字段(用来后续进行可视化分析),并且丢弃user_geohash字段(后面分析不需要这个字段)

脚本如下

下面的代码的基本形式是: awk -F “,” ‘处理逻辑’ $infile > $outfile
#!/bin/bash
infile=$1
outfile=$2
#最后的$infile > $outfile必须跟在}’这两个字符的后面
awk -F "," 'BEGIN{
        srand();
        id=0;
        Province[0]="山东";Province[1]="山西";Province[2]="河南";Province[3]="河北";Province[4]="陕西";Province[5]="内蒙古";Province[6]="上海市";
        Province[7]="北京市";Province[8]="重庆市";Province[9]="天津市";Province[10]="福建";Province[11]="广东";Province[12]="广西";Province[13]="云南"; 
        Province[14]="浙江";Province[15]="贵州";Province[16]="新疆";Province[17]="西藏";Province[18]="江西";Province[19]="湖南";Province[20]="湖北";
        Province[21]="黑龙江";Province[22]="吉林";Province[23]="辽宁"; Province[24]="江苏";Province[25]="甘肃";Province[26]="青海";Province[27]="四川";
        Province[28]="安徽"; Province[29]="宁夏";Province[30]="海南";Province[31]="香港";Province[32]="澳门";Province[33]="台湾";
    }
    {
        id=id+1;
        value=int(rand()*34);       
        print id"\t"$1"\t"$2"\t"$3"\t"$5"\t"substr($6,1,10)"\t"Province[value]
    }' $infile > $outfile
[关于shell脚本中,=两边的空格](https://blog.csdn.net/solarsaber/article/details/65938545) ![这里写图片描述](https://img-blog.csdn.net/20180729115358780?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhaXhXYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 先用小的数据集走下流程
bash ./pre_deal.sh small_user.csv user_table.txt

处理之后的数据:
这里写图片描述

导入数据到hive

补充 导入数据到hive表的六种方式
后文优化,可以从这个角度进行部分优化
● 加载本地文件
● 加载HDFS文件
● 加载并覆盖
● 通过查询到的文件加载:insert into table tablename select value from tablename
● 创建表时使用select加载
● 指定location加载

这里创建外部表,所以先得把数据放入hdfs。
其实数据放入外部表本身就已经是一种优化策略了,因为hive数据仓库的概念,这里的数据,我们可以考虑为,多个业务部门都需要访问此数据。所以,在实践中,我们可以将最大的原始数据放入hdfs,然后子业务根据各自需要,从最大的表中取数据。而该原始表,最好使用外部表——External_Table ,外部表与管理表——Manage_Table最大的区别在于外部表删除表时,仅仅删除存储在关系型数据库中的元数据,而不会删除hdfs中数据。

sudo -u hdfs hadoop fs -mkdir /tmp/data21080729
sudo -u hdfs hdfs dfs -put /home/user_table.txt /tmp/data21080729[你也许需要修改文件权限
这里写图片描述

创建外部表

【外部表创建后会自动加载数据】
create database tmp

CREATE EXTERNAL TABLE tmp.bigdata_user(id INT,uid STRING,item_id STRING,behavior_type INT,item_category STRING,visit_date DATE,province STRING) COMMENT 'Welcome to xmu dblab!' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '/tmp/data21080729';

先简单感受下mapreduce离线的慢
这里写图片描述
其实真正的mapreduce时间也并没有35秒那么多
namenode,8088端口:
这里写图片描述

进一步的查询,以及使用较大的那一份数据集,放在后面,先完成下hive到mysql,mysql到hbase

hive到mysql

额,学校的VPN,连着老是断,我们先screen走一波
screen -S hmh
screen -ls
这里我们不在之前的表上操作,我们创建一个临时表,hive中,临时表只在当前会话起作用,会话结束,表 自动删除。
注意,林子雨实验室的案例,创建的并不是临时表,缺关键字temporary

hive --version
从Hive1.1开始临时表可以存储在内存或SSD,使用hive.exec.temporary.table.storage参数进行配置,该参数有三种取值:memory、ssd、default。
存储层次
所以,在内存装得下的情况下【一般临时表也使用select from的形式创建,所以就这个应该不是大问题】,我们还可以让临时表常驻内存,加快运算速度,这也可算作优化策略之一。

相关issue:HIVE-7090
set hive.exec.temporary.table.storage=memory;

  • 创建临时表
    create temporary table tmp.user_action(id STRING,uid STRING, item_id STRING, behavior_type STRING, item_category STRING, visit_date DATE, province STRING) COMMENT 'temporary table to mysql use sqoop! ' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;

describe formatted tmp.user_action;
可以发现,临时表是这样存储的【不指定location】:

hdfs://ip10:8020/tmp/hive/hdfs/de47d44d-1eb4-411b-bb4f-2b9eaf19842f/_tmp_space.db/4784a44c-95db-43d7-847c-4b05879f6e8a

非临时表,且不指定location:

/user/hive/warehouse/tmp.db

  • 从另外一个表以select的方式加载数据
    INSERT OVERWRITE TABLE tmp.user_action select * from tmp.bigdata_user;
    观察打印的信息发现,这里跑了三个mapreduce,好奇为什么会有三个,具体是哪些阶段发生了mapreduce,可以使用EXPLAIN命令查看。
  • 用sqoop导入mysql
    create database hive_to_mysql;
CREATE TABLE `hive_to_mysql`.`user_action` (`id` varchar(50),`uid` varchar(50),`item_id` varchar(50),`behavior_type` varchar(10),`item_category` varchar(50), `visit_date` DATE,`province` varchar(20)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

sqoop export --connect jdbc:mysql://localhost:3306/hive_to_mysql --username root --password sxdx2018 --table user_action --export-dir '/tmp/hive/hdfs/e6429a88-b953-4075-8c96-ad532ea08164/_tmp_space.db/31370dcc-5587-4531-898d-62cbad947f0e' --fields-terminated-by '\t';

查看执行计划

我们看下执行计划

EXPLAIN [EXTENDED|DEPENDENCY|AUTHORIZATION|LOCKS|VECTORIZATION] query

【也可以理解为HQL转换为mapreduce的过程??】

官网doc

  • EXPLAIN INSERT OVERWRITE TABLE tmp.user_action select * from tmp.bigdata_user;
hive> create temporary table tmp.user_action(id STRING,uid STRING, item_id STRING, behavior_type STRING, item_category STRING, visit_date DATE, province STRING) COMMENT 'temporary table to mysql use sqoop! ' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;
OK
Time taken: 1.438 seconds
hive> EXPLAIN INSERT OVERWRITE TABLE tmp.user_action select * from tmp.bigdata_user;
OK
STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-7 depends on stages: Stage-1 , consists of Stage-4, Stage-3, Stage-5
  Stage-4
  Stage-0 depends on stages: Stage-4, Stage-3, Stage-6
  Stage-2 depends on stages: Stage-0
  Stage-3
  Stage-5
  Stage-6 depends on stages: Stage-5

STAGE PLANS:
  Stage: Stage-1
    Map Reduce
      Map Operator Tree: // 发生在job的 map 处理阶段
          TableScan          // 读取表的数据
            alias: bigdata_user // 表的别名
            Statistics: Num rows: 33601 Data size: 15590918 Basic stats: COMPLETE Column stats: NONE
            Select Operator  // 我们select了哪些列
              expressions: id (type: int), uid (type: string), item_id (type: string), behavior_type (type: int), item_category (type: string), visit_date (type: date), province (type: string)
              outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6
              Statistics: Num rows: 33601 Data size: 15590918 Basic stats: COMPLETE Column stats: NONE
              File Output Operator
                compressed: false    // 是否有压缩
                Statistics: Num rows: 33601 Data size: 15590918 Basic stats: COMPLETE Column stats: NONE
                table:
                // 采用了哪些输入/输出/压缩格式
                    input format: org.apache.hadoop.mapred.TextInputFormat
                    output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
                    serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
                    name: tmp.user_action

  Stage: Stage-7
    Conditional Operator  // 条件运算

  Stage: Stage-4
    Move Operator  // 文件系统相关阶段,这里是去hdfs读取数据
      files:
          hdfs directory: true
          destination: hdfs://ip10:8020/tmp/hive/hdfs/e6429a88-b953-4075-8c96-ad532ea08164/_tmp_space.db/31370dcc-5587-4531-898d-62cbad947f0e/.hive-staging_hive_2018-07-29_15-24-29_649_851564283197837409-1/-ext-10000

  Stage: Stage-0
    Move Operator
      tables:
          replace: true
          table:  
          // hdfs获取数据
              input format: org.apache.hadoop.mapred.TextInputFormat
              output format: // 输出为HiveIgnoreKeyTextOutputFormat格式org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
              serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
              name: tmp.user_action

  Stage: Stage-2
    Stats-Aggr Operator

  Stage: Stage-3
    Map Reduce
      Map Operator Tree:
          TableScan
            File Output Operator
              compressed: false
              table:
                  input format: org.apache.hadoop.mapred.TextInputFormat
                  output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
                  serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
                  name: tmp.user_action

  Stage: Stage-5
    Map Reduce
      Map Operator Tree:
          TableScan
            File Output Operator
              compressed: false
              table:
                  input format: org.apache.hadoop.mapred.TextInputFormat
                  output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
                  serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
                  name: tmp.user_action

  Stage: Stage-6
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://ip10:8020/tmp/hive/hdfs/e6429a88-b953-4075-8c96-ad532ea08164/_tmp_space.db/31370dcc-5587-4531-898d-62cbad947f0e/.hive-staging_hive_2018-07-29_15-24-29_649_851564283197837409-1/-ext-10000
  • 首先打印的是依赖树,指明那些阶段发生在哪些阶段之后
    说明:
    Stage-1是最先执行的,Stage-7依赖于Stage-1,Stage-7又由Stage-4、3、5组成
STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-7 depends on stages: Stage-1 , consists of Stage-4, Stage-3, Stage-5
  Stage-4
  Stage-0 depends on stages: Stage-4, Stage-3, Stage-6
  Stage-2 depends on stages: Stage-0
  Stage-3
  Stage-5
  Stage-6 depends on stages: Stage-5
  • 接下来可以看出
    stage1、3、5是mapreduce stage
  • 还有些文件系统相关阶段、条件运算符、是否压缩等信息
  • EXPLAIN extended INSERT OVERWRITE TABLE tmp.user_action select * from tmp.bigdata_user;
    这个,打印的信息太多了,暂时还没有能看懂的实力。。。

mysql到Hbase

hive、hbase、mysql之间的数据传输,以前用过几次。
1、仍然使用sqoop,换汤不换药,先不做了。
2、java api读取hive数据再存入HBase,可参考我以前这篇文章Hbase Java API简单实践

使用较大的数据集,为优化做准备

这里开始,使用较大的数据集进行实验。

bash ./pre_deal.sh raw_user.csv user_table_big.txt

sudo -u hdfs hdfs dfs -put /home/user_table_big.txt /tmp/data21080729_1/

CREATE EXTERNAL TABLE tmp.optimize(id INT,uid STRING,item_id STRING,behavior_type INT,item_category STRING,visit_date DATE,province STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '/tmp/data21080729_1';

  • count

    select count(1) from tmp.optimize;
    共20759743条数据,count(1)操作耗时37秒。

  • 错误的做法,大表join小表

    select * from tmp.optimize a left join tmp.user_action b on(a.id=b.id)limit 5;
    33秒

  • 尝试容易数据倾斜的group by和distinct等

    select distinct a.item_category from optimize a left join bigdata_user b on (a.id = b.id)
    select count(*) from (select count(1) from optimize group by item_category) a;
    数据太小了。各个mapreduce的执行时间差异不大,40秒左右运算结束。
    从事大数据 正式工作前,应该是模拟不出数据倾斜了。
    select count(*) from opyimize where behavior_type=’1’ and visit_date<’2015-12-13’ and visit_date>’2012-12-10’;

伪优化开始

哎,又是理论版

  • 合并大量小文件,以减少map数量
    set mapred.max.split.size
    set mapred.min.split.size.per.node
    set mapred.min.split.size.per.rack等参数
  • 控制任务的 reduce 数:

    不指定 reduce 个数的情况下, Hive 会猜测
    确定一个 reduce 个数,基于以下两个设定:
    参数1:hive.exec.reducers.bytes.per.reducer(每个 reduce 任务处理的数据量,默认为
    1000^3=1G)
    参数2:hive.exec.reducers.max(每个任务最大的 reduce 数,默认为 999)
    计算 reducer 数的公式很简单 N=min(参数 2,总输入数据量/参数 1)
    set hive.exec.reducers.bytes.per.reducer
    set mapred.reduce.tasks

  • 分表
    1、假设原始的数据很大,我们可以搜索出原始表中的部分数据,然后创建一张表。获取部分数据我们可以写python脚本解析数据。
    2、在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念。也就是分目录的意思。【指定根据哪个字段进行分区就行】
    3、比如说每天的日志,按日期分表,加载数据到表时,也需要指定partition,然后数据会自动分目录。

  • JVM重用
    每个mapreduce会单独启一个JVM。我们可以在配置文件中设置一些参数,来配置某些task的stage重用JVM,减少任务的开启时间

  • join 时,小表放前面
    hive会将小表进行缓存;
    也可以用配置来自动优化:
    set hive.auto.convert.join=true;

  • 并行运行
    某些任务之间没有依赖关系时,就可以设置并行运行
    让一些不相关的stage能够并行运行,加速任务的完成

  • 考虑表的设计,大表拆分为子表,数据压缩等
    考虑设置中间和reduce结果进行压缩,减少占用空间。

  • 数据倾斜

    发生数据倾斜的可能原因:
    key分布不均匀。
    map端数据倾斜,输入文件太多且大小不一 。
    reduce端数据倾斜,分区器问题。

使用Hive对数据做一些类型统计的时候遇到某种类型的数据量特别多,而其他类型数据的数据量特别少。当按照类型进行group by的时候,会将相同的group by字段的reduce任务需要的数据拉取到同一个节点进行聚合,而当其中每一组的数据量过大时,会出现其他组的计算已经完成而这里还没计算完成,其他节点的一直等待这个节点的任务执行完成,所以会看到一直map 100% reduce 99%的情况。

  解决方法:set hive.map.aggr=true
       set hive.groupby.skewindata=true
  原理:hive.map.aggr=true 这个配置项代表是否在map端进行聚合
     hive.groupby.skwindata=true 当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个job再进行真正的group by key处理。
  优化map/reduce数量
  join小表放进内存
  遇到需要进行join的但是关联字段有数据为空,给空值分配随机的key值。

参考

[1] 进阶推荐阅读——Hive SQL执行计划深度解析
[2] hive执行计划简单分析

猜你喜欢

转载自blog.csdn.net/HaixWang/article/details/81268861