创建伪表&自定义UDF函数&MR解决数据倾斜的问题&行转列案例&列转行案例&使用hive实现wc&修改hadoop的URI带来的hive数据库路径问题&多文件多目录做wc或建表带来的问题

目标

  1. 创建伪表
  2. 自定义UDF函数
  3. MR解决数据倾斜的问题(引入)
  4. 行转列案例
  5. 列转行案例
  6. 使用hive实现wc
  7. 修改hadoop的URI带来的hive数据库路径问题
  8. 多文件多目录做wc或建表带来的问题

创建伪表

  1. 创建表dual

    create table dual(a string);
  2. 创建数据并导入到表

    touch dual.txt
    echo 'X' >dual.txt
    load data local inpath '/home/hadoop/data/dual.txt' overwrite into table dual;

自定义UDF函数

hive官网关于用户如何自定义UDF: https://cwiki.apache.org/confluence/display/Hive/HivePlugins1.

hive查看函数: show functions;

hive查看jar包: list jars;

  1. 首先,需要创建一个扩展UDF的新类,其中有一个或多个名为evaluate的方法

    // 继承UDF
    public class UDFPrintf extends UDF {

    //方法重载
    public void evaluate(){
    System.out.println("你不要老婆吧");
    }
    public void evaluate(String name){
    System.out.println(name + ",你要老婆不要?");
    }
    }
  2. 对方法添加描述,先看系统的

    hive (default)> desc function extended upper;
    upper(str) - Returns str with all characters changed to uppercase
    Synonyms: ucase
    Example:
    > SELECT upper('Facebook') FROM src LIMIT 1;
    'FACEBOOK'
    =================================================================
    @Description(
    name = "upper,ucase",
    value = "_FUNC_(str) - Returns str with all characters changed to uppercase",
    extended = "Example:\n > SELECT _FUNC_('Facebook') FROM src LIMIT 1;\n
    'FACEBOOK'"
    )

    我们仿照上面的给自定义的方法写个描述(Description)

    @Description(
    name = "my_printf,ucase",
    value = "_FUNC_(str) - 返回一个名字加上字符串",
    extended = "Example:\n > SELECT _FUNC_('老王') FROM src LIMIT 1;\n
    '老王,你要老婆不要?'"
    )
  3. 打包上传到Linux,启动hive,jar包添加到class path,创建临时UDF函数,只能在当前session中有效

    add jar /home/hadoop/lib/hive-client-1.0.0.jar;
    create temporary function my_printf as 'org.hadoop.hive.UDF.UDFPrintf';
  4. 伪表使用自定义函数

    select my_printf() from dual;
    hello,小七
    select my_printf("老王") from dual;
    hello,老王
  5. 上面的方法只能创建临时函数,我们接下来将会创建永久函数,需要把jar把上传到hdfs

    hdfs dfs -mkdir /jar
    hdfs dfs -put /home/hadoop/lib/hive-client-1.0.0.jar /jar
    CREATE FUNCTION my_printf AS 'org.hadoop.hive.UDF.UDFPrintf' USING JAR 'hdfs:///jar/hive-client-1.0.0.jar';
  6. 然后可以在多个窗口执行创建的永久函数

MR解决数据倾斜的思想

核心思想==>先加盐(随机数),再去盐(随机数)

public class skew {
private List<String> newList = new ArrayList<>();
private Random r = new Random();
public void incRandom(List list){
newList.clear();
list.forEach( word ->{
int i = r.nextInt(10);
newList.add(i+"_"+word);
});
newList.forEach(System.out::println);
}
public void decRandom(){
newList.forEach(word->{
System.out.println(word.substring(word.lastIndexOf("_")+1));
});
}
public static void main(String[] args) {
skew skew = new skew();
List<String> arr = new ArrayList<>();
arr.add("老王");
arr.add("狗子");
arr.add("张三");
skew.incRandom(arr);
System.out.println("-------------------");
skew.decRandom();
}
}
7_老王
2_狗子
4_张三
-------------------
老王
狗子
张三

行转列案例

实现部门号的所有学生格式为

(dept01,A	laowang|wangwu)
  1. 数据

    laowang	dept01	A
    zhangsan dept02 A
    lisi dept01 B
    wangwu dept01 A
    zhaoliu dept02 A
  2. 创建表结构

    create external table row2col(
    name string,
    dept string,
    grade string
    )
    row format delimited
    fields terminated by "\t";
  3. 导入数据

    load data local inpath '/home/hadoop/data/row2col.txt' into table row2col;
  4. 实现(dept01,A laowang|wangwu)

    1. 第一步组合dept和grade成dept_grade

      select concat_ws(',',dept,grade) dept_grade,name from row2col;
    2. 第二步按dept_grade分组求collect_set()将返回的数组使用concat_ws()函数转成字符串

      select 
      t.dept_grade,concat_ws('|',collect_set(t.name)) names
      from
      (select concat_ws(',',dept,grade) dept_grade,name from row2col) t
      group by
      t.dept_grade;

      collect_set()返回一个数组,concat_ws()返回一个指定字符切分数组的字符串

    3. 结果

      dept_grade|names           |
      ----------|----------------|
      dept01,A |laowang|wangwu |
      dept01,B |lisi |
      dept02,A |zhangsan|zhaoliu|

列转行案例

实现所有课程的格式为

1       zhangsan        化学
1 zhangsan 物理
...
  1. 数据

    1,zhangsan,化学:物理:数学:语文
    2,lisi,化学:数学:生物:生理:卫生
    3,wangwu,化学:语文:英语:体育:生物
  2. 建表

    create external table col2row(
    id int,
    name string,
    subjects array<string>
    )
    row format delimited
    fields terminated by ","
    collection items terminated by ":";
  3. 导数据

    load data local inpath '/home/hadoop/data/col2row.txt' into table col2row;
  4. 实现

    select 
    id,name,subject
    from
    col2row
    lateral view explode(subjects) t as subject;
  5. 结果

    id|name    |subject|
    --|--------|-------|
    1|zhangsan|化学 |
    1|zhangsan|物理 |
    1|zhangsan|数学 |
    1|zhangsan|语文 |
    2|lisi |化学 |
    2|lisi |数学 |
    2|lisi |生物 |
    2|lisi |生理 |
    2|lisi |卫生 |
    3|wangwu |化学 |
    3|wangwu |语文 |
    3|wangwu |英语 |
    3|wangwu |体育 |
    3|wangwu |生物 |

使用hive实现wc

  1. 数据

    化学:物理:数学:语文:英语
    化学:数学:生物:生理:卫生
    化学:语文:英语:体育:生物
    数学:语文:英语:体育:生物
  2. 切分返回数组

    select split(line,":") as subjects from wc
  3. 炸开数组返回单词

    select
    words
    from
    (select split(line,":") subjects from wc) as t
    lateral view explode(t.subjects) t1 as words;
  4. 每个单词分组求count()

    select 
    t2.words,count(1)
    from
    (select
    words
    from
    (select split(line,":") subjects from wc) as t
    lateral view explode(t.subjects) t1 as words) t2
    group by t2.words;

修改hadoop的URI带来的hive数据库路径问题

hive的数据保存在hadoop中,而hive的源数据保存在mysql中

这里就有一个问题,如果修改了hadoop的fs.defaultFS这个参数

<property>
<name>fs.defaultFS</name>
<value>hdfs://aliyun:9000</value>
</property>

那么hive的元数据没有修改就会找mysql中保存的路径

*************************** 1. row ***************************
DB_ID: 1
DESC: Default Hive database
DB_LOCATION_URI: hdfs://aliyun:9000/user/hive/warehouse
NAME: default
OWNER_NAME: public
OWNER_TYPE: ROLE
...

这时候需要使用到一个命令metatool来将mysql中的元数据修改为新的仓库路径

-updateLocation <new-loc> <old-loc>

多文件多目录做wc或建表带来的问题

我们查看一下hdfs中的目录结构

[hadoop@aliyun ~]$ hdfs dfs -ls /mdir/*
-rwxr-xr-x 1 hadoop supergroup 13 2020-02-05 00:59 /mdir/1.txt
-rwxr-xr-x 1 hadoop supergroup 14 2020-02-05 00:59 /mdir/2.txt
-rwxr-xr-x 1 hadoop supergroup 11 2020-02-05 00:59 /mdir/mdir2/3.txt

存在嵌套目录,在做wc或者建表时,将会报错

create external table dir (name string) location '/mdir';
Failed with exception java.io.IOException:java.io.IOException: Not a file: hdfs://aliyun:9000/mdir/*

通过设置参数mapreduce.input.fileinputformat.input.dir.recursivetrue来解决这个问题,该参数默认为false

select * from dir;
...

可以直接在mapped-site.xml文件中直接配配置参数

<property>
<name>mapreduce.input.fileinputformat.input.dir.recursive</name>
<value>true</value>
</property>
Author: Tunan
Link: http://yerias.github.io/2018/11/07/hive/7/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.