大数据面试题整理——Hive

系列文章目录

大数据面试题专栏点击进入


文章目录

  • 系列文章目录
  • Hive 面试知识点全面解析
    • 一、函数相关
      • (一)函数分类与特点
      • (二)`concat`和`concat_ws`的区别
    • 二、SQL 的书写和执行顺序
      • (一)书写顺序
      • (二)执行顺序
    • 三、where 和 having 的区别
      • (一)筛选时机
      • (二)示例
    • 四、表连接的方式及区别
      • (一)连接方式
      • (二)区别示例
    • 五、Hive 的排序方式及区别
      • (一)排序方式
      • (二)区别
    • 六、Hive 的体系架构
      • (一)Hive 概述
      • (二)主要组件
    • 七、Hive 的表分类
      • (一)管理表(内部表)
      • (二)外部表
      • (三)分区表
      • (四)桶表
    • 八、将数据导入Hive的方式
      • (一)将文件数据导入Hive表中
      • (二)直接将查询结果放入新创建的表中(执行查询的创建)
      • (三)将查询结果导入已经存在表
      • (四)将HDFS中已经存在文件导入新建的Hive表中
      • (五)`insert into 表名 values`
    • 九、开窗函数
      • (一)开窗函数语法及分类
      • (二)应用场景
    • 十、Hive SQL 执行的方式
    • 十一、Hive 的自定义函数
      • (一)函数类型及定义
      • (二)应用场景及临时函数与永久函数的区别
    • 十二、Hive 优化
      • (一)开启本地模式
      • (二)JVM 重用
      • (三)设置并行执行
      • (四)开启`hive`严格模式
      • (五)通过mapjoin提高大表和小表的join执行速度、通过桶表的join解决大表和大表的join
      • (六)列裁剪与分区裁剪
      • (七)避免数据倾斜
    • 十三、Hive 文件存储格式
    • 十四、Hive 序列化
    • 十五、Hive 中`maptask`和`reducetask`的数量
    • 十六、Hive中的mapjoin、commonjoin、smbjoin的特点与区别
      • MapJoin原理及特点
      • CommonJoin原理及特点
      • smbJoin原理及特点
    • 十七、Hive 中的压缩
      • (一)Hive 在不同阶段的压缩
    • 十八、Hive 如何导出数据
      • (一)使用 ETL 工具
      • (二)导出数据到本地目录
      • (三)导出到 HDFS 的目录下
    • 十九、Hive SQL 的行转列和列转行(适用于 MySQL)
      • (一)示例表及数据
      • (二)行转列操作
      • (三)列转行操作
    • 二十、如何实现 Hive 的动态分区
      • (一)需求场景
      • (二)实现步骤
    • 二十一、Hive 表的字段类型
    • 二十二、Hive 的相关概念及特点总结
      • (一)Hive 概述
      • (二)Hive 与传统关系型数据库的区别
      • (三)Hive 的架构及组件作用
      • (四)Hive 的分区和桶
      • (五)Hive 支持的连接方式及特点
      • (六)Hive 中查询性能优化方法
      • (七)Hive 中数据倾斜的相关问题


Hive 面试知识点全面解析

一、函数相关

(一)函数分类与特点

  1. 单行函数
    • 特点:一进一出。
    • 细分类型及示例:
      • 字符串函数
        • substring:用于截取字符串的子串。例如,substring('abcdefg', 2, 4)返回bcde,表示从字符串'abcdefg'的第2个位置开始截取,截取长度为4。
        • concat:连接多个字符串。例如,concat('hello', 'world')返回helloworld,将'hello''world'连接在一起。
        • concat_ws:与concat类似,但可以指定连接字符串之间的分隔符。例如,concat_ws(', ', 'apple', 'banana', 'cherry')返回apple, banana, cherry,使用, 作为分隔符连接三个字符串。
      • 日期函数
        • datediff:计算两个日期之间的天数差。例如,datediff('2023-10-10', '2023-10-01')返回9,表示从2023-10-012023-10-10相差9天。
        • date_add:在给定日期上增加指定的天数。例如,date_add('2023-10-10', 5)返回2023-10-15,在2023-10-10的基础上增加5天。
      • 数值函数
        • round:对数值进行四舍五入。例如,round(3.14159, 2)返回3.14,将3.14159保留两位小数四舍五入。
        • floor:向下取整。例如,floor(3.9)返回3,取不大于3.9的最大整数。
        • ceil:向上取整。例如,ceil(3.1)返回4,取不小于3.1的最小整数。
        • cast:用于类型转换。例如,cast('123' as int)将字符串'123'转换为整数123。
      • 流程控制函数
        • case when then end:用于条件判断。例如,select case when age > 18 then '成年' else '未成年' end from users,根据age字段的值判断是成年还是未成年。
        • if:简单的条件判断函数。例如,if(condition, value1, value2),如果condition为真,则返回value1,否则返回value2
      • 集合函数
        • nvl:用于处理空值。例如,nvl(column_name, default_value),如果column_nameNULL,则返回default_value,否则返回column_name的值。
  2. 聚合函数
    • 特点:多进一出。
    • 示例:
      • max:求最大值。例如,select max(salary) from employees,返回employees表中salary列的最大值。
      • min:求最小值。
      • avg:求平均值。
      • count:计数。
      • sum:求和。
      • collect_list:将分组后的某个列值存放在一个数组中,不去重。例如,select collect_list(id) from table,会将id列的值收集到一个数组中,每个分组对应一个数组。
      • collect_set:与collect_list类似,但会对集合中元素去重。
  3. 炸裂函数
    • 特点:一进多出。
    • 示例:
      • explode:将数组或映射中的元素拆分成多行。例如,对于一个包含数组字段的表,explode(array_column)可以将数组中的每个元素作为一行输出。
      • posexplode:除了将元素拆分成多行,还会返回元素的位置索引。

(二)concatconcat_ws的区别

  • concat函数用于简单地连接多个字符串,没有指定分隔符,直接将字符串拼接在一起。
  • concat_ws函数可以指定一个分隔符,用于在连接字符串时插入分隔符,使得连接后的字符串更具可读性和规范性。例如,在连接多个单词组成一个句子时,concat_ws(' ', 'hello', 'world')可以清晰地用空格分隔每个单词,而concat('hello', 'world')则是直接连接成helloworld,没有分隔。

二、SQL 的书写和执行顺序

(一)书写顺序

select.. from.. join.. group by.. having.. order by.. limit

(二)执行顺序

from.. join.. group by.. having.. select.. order by.. limit

例如,在执行一个查询时,首先从指定的数据源(from)读取数据,然后进行表连接(join)操作,接着按照group by指定的列进行分组,对分组后的数据应用having筛选条件,之后进行select列的选择和计算,最后按照order by进行排序,并根据limit限制返回的行数。

三、where 和 having 的区别

(一)筛选时机

  • where在分组之前对数据进行筛选,它可以直接使用表中的任意字段进行条件判断,但不能使用聚合函数。
  • having在分组之后对分组结果进行筛选,只能使用分组字段和聚合函数作为筛选条件,不能使用分组字段以外其他原表字段。

(二)示例

假设我们有一个销售数据表,包含字段product_id(产品ID)、quantity(销售数量)、date(销售日期)等。

  • 如果我们要查询销售数量大于100的记录,可以使用whereselect * from sales where quantity > 100
  • 如果我们要查询每个产品的总销售数量,并找出总销售数量大于500的产品,需要先分组再筛选,此时使用havingselect product_id, sum(quantity) as total_quantity from sales group by product_id having total_quantity > 500

四、表连接的方式及区别

(一)连接方式

  • 左连接(Left Outer Join):以左边表为主表,如果主表中某条数据没有和右表数据连接上,则左表这条数据也会出现在查询结果中,右边表对应列输出null。例如,员工表有一个员工没有部门编号,如果使用左连接,以员工表为主表则该员工会被查询出来,对应的部门表的列为null
  • 右连接(Right Outer Join):以右边表为主表,右表中所有的行以及与左表匹配的行都会出现在结果中,如果左表中没有匹配的行,将返回NULL值。
  • 内连接(Inner Join):返回两个表中匹配的行,即只返回两个表中共有的行,没有连接上的行不会输出。
  • 全连接(Full Outer Join):返回两个表中所有的行,并将不匹配的行填充为NULL值,两张表都是主表。

(二)区别示例

假设有两张表,table1(包含id1value1字段)和table2(包含id2value2字段),部分数据如下:

table1
id1 value1
1 a
2 b
3 c
table2
id2 value2
2 x
3 y
4 z
  • 左连接select * from table1 left join table2 on table1.id1 = table2.id2结果:
id1 value1 id2 value2
1 a null null
2 b 2 x
3 c 3 y
  • 右连接select * from table1 right join table2 on table1.id1 = table2.id2结果:
id1 value1 id2 value2
2 b 2 x
3 c 3 y
null null 4 z
  • 内连接select * from table1 inner join table2 on table1.id1 = table2.id2结果:
id1 value1 id2 value2
2 b 2 x
3 c 3 y
  • 全连接select * from table1 full outer join table2 on table1.id1 = table2.id2结果:
id1 value1 id2 value2
1 a null null
2 b 2 x
3 c 3 y
null null 4 z

五、Hive 的排序方式及区别

(一)排序方式

  1. order by(全局排序)
    • 特点:对应的 MapReduce 任务中只有一个reduceTask,即使设置了set mapreduce.job.reduces大于1也不行。适用于分组聚合的结果数据量已经不大的情况下,可以保证整个数据集的有序性。
    • 示例:select * from table order by column_name,会对table表中的数据按照column_name列进行全局排序。
  2. sort by(局部排序)
    • 特点:对应的 MapReduce 任务可以有多个reduceTask,每个reduceTask输出的数据是有序的。它只保证每个分区内的数据有序,不保证全局有序。
    • 示例:select * from table sort by column_name,会在每个分区内对数据按照column_name列进行排序。
  3. distribute by(自定义分区)
    • 特点:负责指定分区规则,将某一类数据写入同一个reduceTask中。通常和sort by配合使用,distribute by指定如何分区,sort by指定分区内数据的排序方式。
    • 示例:select * from table distribute by partition_column sort by sort_column,首先根据partition_column进行分区,然后在每个分区内按照sort_column进行排序。

(二)区别

  • order by对整个数据集进行排序,确保所有数据按照指定列有序,但在大数据集情况下可能导致性能问题,因为只有一个reduceTask处理所有排序工作,数据量大时可能会内存不足等。
  • sort by在每个reduceTask内进行局部排序,适合数据量较大且不需要全局严格有序的场景,能提高处理效率,多个reduceTask可以并行处理排序任务。
  • distribute by主要用于将数据按照指定规则分区到不同的reduceTask,结合sort by可以实现更灵活的数据分布和局部有序处理。

六、Hive 的体系架构

(一)Hive 概述

Hive是一个构建在Hadoop上的数据仓库软件,它提供了类似SQL的查询语言,使得用户可以用SQL来查询存放在Hadoop上的数据。Hive是一种结构化数据的存储和查询机制,它可以将SQL语句转换为MapReduce任务在Hadoop上执行。

(二)主要组件

  1. 用户接口(User Interface)
    • 支持多种方式,包括CLI(命令行界面),用户可以直接在命令行中输入Hive命令执行SQL操作。
    • JDBC/ODBC:供其他程序通过JDBC或ODBC接口访问Hive,方便与其他应用程序集成,例如在Java程序中可以使用JDBC连接Hive进行数据查询和处理。
    • WebUI(浏览器访问):提供了一个可视化的界面,用户可以通过浏览器进行一些基本的操作和查询监控等。
  2. 元数据存储(Metastore)
    • 存储关于数据的信息,比如表的结构、分区、数据所在的HDFS路径等。通常使用关系型数据库(如MySQL)来存储元数据。它提供了一组API和服务,用于查询、更新和管理Hive表的元数据,以便不同的用户和进程可以共享和访问相同的元数据,从而协调和共享表的结构和属性。
  3. 驱动器(Driver)
    • 包含解析器、编译器、优化器和执行器。
    • 解析器:将SQL字符串转换成抽象语法树AST,并进行语法分析,检查SQL语句的语法是否正确。
    • 编译器:将AST编译生成逻辑执行计划,确定如何执行查询,例如选择使用哪些表连接算法等。
    • 优化器:对逻辑执行计划进行优化,例如选择最优的执行路径、调整执行顺序等,以提高查询性能。
    • 执行器:把逻辑执行计划转换成可运行的物理计划,对于Hive来说就是MapReduce或Spark任务,负责实际执行查询操作,与Hadoop进行交互,调度和管理任务的执行。
  4. 与Hadoop的关系
    • Hive底层依赖Hadoop的HDFS进行数据存储,数据以文件的形式存储在HDFS中,Hive表对应为HDFS上的指定目录。
    • 使用MapReduce或其他计算框架(如Tez、Spark等)进行数据计算,根据执行计划将任务分解为MapReduce或其他任务在集群上执行。

七、Hive 的表分类

(一)管理表(内部表)

  • 默认创建就是管理表,管理表被删除时会将HDFS存储的数据删除。例如,创建一个管理表employees
CREATE TABLE employees (
    id INT,
    name STRING,
    salary FLOAT
) STORED AS ORC;

当删除这个表时,HDFS中存储该表数据的目录也会被删除。

(二)外部表

  • 创建表时使用external关键字,如create external table 表名。外部表被删除时,只会删除MySQL中对应的元数据信息,并不会删除HDFS上表中的数据。外部表可以防止误删除实际数据,推荐使用。如果外部表被删除,重写创建表,通过location指向原本的数据目录可以在创建完表的情况下,直接查询表到中的数据。例如:
CREATE EXTERNAL TABLE external_employees (
    id INT,
    name STRING,
    salary FLOAT
) LOCATION '/user/data/external_employees' STORED AS ORC;

删除该外部表时,HDFS上/user/data/external_employees目录中的数据不会被删除。

(三)分区表

  • Hive中的表对应为HDFS上的指定目录,在查询数据时候,默认会对全表进行扫描,这样时间和性能的消耗都非常大。分区为HDFS上表目录的子目录,数据按照分区存储在子目录中。如果查询的where字句中包含分区条件,则直接从该分区去查找,而不是扫描整个表目录,合理的分区设计可以极大提高查询速度和性能。通常,在管理大规模数据集的时候都需要进行分区,比如将日志文件按天进行分区,从而保证数据细粒度的划分,使得查询性能得到提升。
  • 创建分区表语法:可以使用PARTITIONED BY子句创建分区表。表可以包含一个或多个分区列,如partitioned by (分区列类型,分区列类型..)
  • 添加分区:alter table 表名 add partition (分区字段=值)。例如,为一个日志表log_table添加一个分区dt=2023-10-10alter table log_table add partition (dt='2023-10-10')
  • 删除分区:alter table 表名 drop partition(分区字段=值),如alter table log_table drop partition (dt='2023-10-10')

(四)桶表

  • 桶表就是对指定列进行哈希(hash)计算,然后会根据hash值进行切分数据,将具有不同hash值的数据写到每个桶对应的文件中。创建桶表语法:CLUSTERED BY(分区字段) INTO 桶的数量 BUCKETS。例如:
CREATE TABLE bucket_table (
    id INT,
    data STRING
) CLUSTERED BY (id) INTO 5 BUCKETS STORED AS ORC;

这会将bucket_table表按照id列进行哈希分桶,共分为5个桶,数据会根据id的哈希值分配到不同的桶文件中。桶表可以用于数据抽样、高效连接等操作,例如在连接操作中,如果两个表按照相同的列进行分桶,并且桶的数量成倍数关系,可以提高连接效率。

八、将数据导入Hive的方式

(一)将文件数据导入Hive表中

  1. 非分区表
    • load data local inpath '文件的路径' overwrite into table 表:从本地文件系统加载数据到Hive表中,会覆盖表中原有数据。例如,load data local inpath '/home/user/data.txt' overwrite into table my_table; 这里假设data.txt是本地文件,包含了适合my_table表结构的数据。执行该命令后,data.txt中的数据将被加载到my_table表中,并且如果my_table表中原来有数据,会被新数据覆盖。
    • load data inpath '文件的路径' overwrite into table 表:从HDFS文件系统加载数据到Hive表中,也会覆盖表中原有数据。例如,load data inpath '/user/hive/data/hive_data.txt' overwrite into table my_table; 这里假设hive_data.txt是HDFS上的文件,执行该命令后,hive_data.txt中的数据将被加载到my_table表中,同样会覆盖原有的数据。如果文件在HDFS上的路径不存在,可能会导致加载失败,需要确保文件路径的正确性。

(二)直接将查询结果放入新创建的表中(执行查询的创建)

create table[view]as select 语句 where.. group by;

例如:

create table new_table as select id, name, sum(salary) as total_salary from employees group by id, name;

这会创建一个名为new_table的新表,并将employees表中按照idname分组后计算的每个组的总工资插入到新表中。如果使用create view,则会创建一个视图,而不是实际的表,视图是一个虚拟的表,它基于查询结果定义,可以像表一样进行查询,但不存储实际数据。例如:

create view employee_salary_view as select id, name, salary from employees where department = 'sales';

这里创建了一个名为employee_salary_view的视图,它显示了sales部门员工的idnamesalary信息。视图可以方便地对数据进行筛选和展示,而不需要实际创建一个新的表来存储数据,当原始表employees的数据发生变化时,视图中的数据也会相应更新。

(三)将查询结果导入已经存在表

  1. insert into table 表名 select语句...:将查询结果插入到已存在的表中,如果表中已有数据,会在原有数据基础上追加新数据。
    • 例如:
    insert into table existing_table select id, name from new_employees;
    
    这里假设existing_table是已经存在的表,new_employees是另一个表或查询结果集。执行该语句后,new_employees表中的idname列的数据将被插入到existing_table表中,如果existing_table表原来有数据,新数据会追加到原有数据之后。
  2. insert overwrite table 表名 select语句...:会覆盖表中原有数据,将查询结果插入到表中。
    • 例如:
    insert overwrite table existing_table select id, name 

你可能感兴趣的:(大数据面试题整理,hive,大数据,数据仓库)