Article

数据计算 Hive

更新于:2026-07-13

1. 创建 Hive 表

基本语法

-- 创建一个名为 pokes 的表,其中包含两列,第一列是整数,另一列是字符串。
CREATE TABLE pokes (foo INT, bar STRING);

-- 创建一个名为 invites 的表,其中包含两列和一个名为 ds 的分区列。
-- 分区列是一个虚拟列,不是数据本身的一部分,而是源自加载特定数据集的分区。
-- 默认情况下,表格假定为文本输入格式,分隔符假定为 ^A(ctrl-a)。
CREATE TABLE invites (foo INT, bar STRING) PARTITIONED BY (ds STRING);

完整建表语法

CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
  [(col_name data_type [COMMENT col_comment], ...)]
  [COMMENT table_comment]
  [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
  [CLUSTERED BY (col_name, col_name, ...)
    [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
  [ROW FORMAT row_format]
  [STORED AS file_format]
  [LOCATION hdfs_path]

参数说明:

关键词说明
CREATE TABLE创建一个指定名字的表,如果相同名字的表已存在则抛出异常;可用 IF NOT EXISTS 忽略该异常
EXTERNAL创建外部表
COMMENT为表和列添加注释
PARTITIONED BY创建分区表
CLUSTERED BY创建分桶表
SORTED BY排序(不常用)
ROW FORMAT DELIMITED数据切分格式
STORED AS指定存储文件类型:SEQUENCEFILE(二进制序列文件)、TEXTFILE(文本)、RCFILE(列式存储格式文件)
LOCATION指定表在 HDFS 上的存储位置
LIKE复制现有的表结构,不复制数据

实例 1:创建内部表

数据示例:

1,Lilei,book-tv-code,beijing:chaoyang-shanghai:pudong
2,Hanmeimei,book-Lilei-code,beijing:haidian-shanghai:huangpu

数据格式说明:

  • 字段之间由 , 分割 → FIELDS TERMINATED BY ','
  • 第二个字段是 Array 形式,元素之间由 - 分割 → COLLECTION ITEMS TERMINATED BY '-'
  • 第三个字段是 K-V 形式,K-V 内部由 : 分割,K-V 之间由 - 分割 → MAP KEYS TERMINATED BY ':'
  • 每条数据之间由换行符分割(默认 \n),如果是其他分割方式 → LINES TERMINATED BY ';'
CREATE TABLE IF NOT EXISTS psn (
  id INT,
  name STRING,
  hobbies ARRAY<STRING>,
  address MAP<STRING, STRING>
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY ','
  COLLECTION ITEMS TERMINATED BY '-'
  MAP KEYS TERMINATED BY ':'
  LINES TERMINATED BY ';';

实例 2:根据查询结果创建表

查询结果会插入到新建的表中,此方法不支持分区表

CREATE TABLE IF NOT EXISTS psn1 AS SELECT * FROM psn;

实例 3:根据已有表结构创建表

CREATE TABLE IF NOT EXISTS psn2 LIKE psn;

实例 4:修改内部表为外部表

注意: 'EXTERNAL' = 'TRUE' 大小写敏感。

ALTER TABLE psn1 SET TBLPROPERTIES('EXTERNAL' = 'TRUE');

实例 5:修改外部表为内部表

ALTER TABLE psn1 SET TBLPROPERTIES('EXTERNAL' = 'FALSE');

2. 查看 Hive 表

-- 查看表的类型
DESC FORMATTED psn;

-- 查看所有表
SHOW TABLES;

-- 查看部分表
SHOW TABLES '.*s';

-- 查看建表语句(常用于查看表结构、HDFS 路径)
SHOW CREATE TABLE tablename;

-- 查看指定表的列
DESCRIBE invites;
DESC invites;

-- 查看建库语句
SHOW CREATE DATABASE databasename;

-- 查看分区
SHOW PARTITIONS invites;

-- 查看表的详细信息(HUE 上显示的概览信息,主要用于查询文件存储路径 Location)
DESCRIBE FORMATTED tablename;

3. 更改 Hive 表

重命名表

ALTER TABLE events RENAME TO 3koobecaf;

修改列

-- 增加列(ADD 会在所有列后面、partition 列前面添加)
ALTER TABLE pokes ADD COLUMNS (new_col INT);

-- 增加列并添加注释
ALTER TABLE invites ADD COLUMNS (new_col2 INT COMMENT 'a comment');

-- 更新列
ALTER TABLE invites CHANGE COLUMN foo desc STRING;

-- 删除列(REPLACE 方式)
ALTER TABLE invites REPLACE COLUMNS (foo INT COMMENT 'only keep the column foo');

-- 列名重命名
ALTER TABLE invites RENAME COLUMN foo TO bar;

修改分区

-- 删除指定分区
ALTER TABLE invites DROP PARTITION(dt='20221108');

-- 删除顶层分区下所有分区
-- 假设 invites 有两个分区字段 (year='2021', month='1') 和 (year='2021', month='2')
-- 删除 year='2021' 分区,即可同时删除 (year='2021', month='1') 和 (year='2021', month='2')
ALTER TABLE invites DROP PARTITION(year='2021');

-- 删除指定的底层分区
-- 假设 invites 有 (year='2021', month='1') 和 (year='2022', month='1')
-- 删除 month='1' 分区,即可同时删除两个分区
ALTER TABLE invites DROP PARTITION(month='1');

-- 删除多个分区(不连续分区之间用逗号分割)
ALTER TABLE invites DROP PARTITION(dt='20221108'), PARTITION(dt='20220101');

-- 删除分区加判空条件
ALTER TABLE invites DROP IF EXISTS PARTITION(dt='20221108');

-- 增加分区(不同分区之间不需要逗号分割符)
ALTER TABLE invites ADD PARTITION(dt='20221108') PARTITION(dt='20220101');

-- 增加分区加判空条件
ALTER TABLE invites ADD IF NOT EXISTS PARTITION(dt='20221108') PARTITION(dt='20220101');

-- 分区重命名
ALTER TABLE invites PARTITION(dt='20221108') RENAME TO PARTITION(dt='20220101');

注意: ADD PARTITION 必须指定每一层分区对应值,不支持只指定顶层分区来批量添加。

修改文件格式与路径

-- 修改建表使用的文件格式
ALTER TABLE invites SET FILEFORMAT orc;
ALTER TABLE invites PARTITION (month=2, day=2) SET FILEFORMAT parquet;

-- 修改文件路径
ALTER TABLE invites PARTITION(month=2, day=2) SET LOCATION '/user/lijp/temp';

4. 删除 Hive 内部表

注意: DROP TABLE 仅能删除内部表;如果是外部表,只能删除元数据,不能删除 HDFS 数据。

DROP TABLE pokes;

5. 删除内部表元数据

保留底层文件,后续可用于创建外部表。

DROP TABLE pokes PURGE;

6. 删除内部表数据

删除底层文件,保留内部表元数据。

TRUNCATE TABLE pokes;

删除 Hive 外部表

需要先将外部表改为内部表,再删除:

ALTER TABLE pokes SET TBLPROPERTIES('EXTERNAL' = 'FALSE');
DROP TABLE pokes;

7. 修复 Hive 表

当文件与元数据存在差异时使用:

MSCK REPAIR TABLE tableName;

8. 加载本地文件到 Hive 表

表需要提前创建,按照导入文件的格式指定行分隔符、列分隔符。

LOAD DATA LOCAL INPATH './examples/files/kv1.txt' OVERWRITE INTO TABLE pokes;

参数说明:

关键词说明
LOCAL从本地文件系统加载,若缺省则从 HDFS 加载
OVERWRITE写入前清空表,若缺省则等价于 INTO(追加)

提示: 可以通过修改 hive-default.xml 配置文件中的 hive.metastore.warehouse.dir 来指定 Hive 表根目录。

9. 加载本地文件到不同分区

需要在建表时创建分区表:

LOAD DATA LOCAL INPATH './examples/files/kv2.txt' OVERWRITE INTO TABLE invites PARTITION (ds='2008-08-15');
LOAD DATA LOCAL INPATH './examples/files/kv3.txt' OVERWRITE INTO TABLE invites PARTITION (ds='2008-08-08');

10. 加载集群文件到不同分区

注意: 导入数据后源文件会消失,导入前需要做好备份。

LOAD DATA INPATH '/user/myname/kv2.txt' OVERWRITE INTO TABLE invites PARTITION (ds='2008-08-15');

-- 动态分区插入(需要在建表时创建分区表)
SET hive.exec.dynamic.partition.mode=nonstrict;
LOAD DATA INPATH '/user/myname/kv2.txt' OVERWRITE INTO TABLE invites PARTITION (ds1, ds2);

注意: Hive 不支持集群分区文件夹插入分区表,仅支持单个文件的插入。

11. SELECT 查询

SELECT a.foo FROM invites a;

12. WHERE 与 ON 筛选

基本用法

  • ON:指定连接条件,进行关联操作
  • WHERE:对结果进行过滤
-- ON 用于连接
SELECT a.*, b.*
FROM invites a
LEFT JOIN pokes b
ON a.id = b.id;

-- WHERE 用于过滤
SELECT a.*, b.*
FROM invites a
LEFT JOIN pokes b
ON a.id = b.id
WHERE b.id IS NOT NULL;

执行 JOIN 时的区别

i. 正常情形:ON 用于连接,WHERE 用于过滤

SELECT * FROM
  (SELECT * FROM (SELECT null AS n1, 1 AS id) t1
  UNION ALL
  SELECT * FROM (SELECT 'one' AS n1, 2 AS id) t2) t
JOIN
  (SELECT * FROM (SELECT 'one' AS n1, 1 AS id) p1
  UNION ALL
  SELECT * FROM (SELECT null AS n1, 2 AS id) p2) p
ON t.id = p.id
WHERE t.n1 IS NOT NULL;

结果:

t.n1t.idp.n1p.id
NULL1one1
one2NULL2

ii. 顺序敏感:ON 在前,WHERE 在后

-- 以下会报错
SELECT * FROM ...
WHERE t.n1 IS NOT NULL
ON t.id = p.id;
-- 报错:cannot recognize input near 'on' 't' '.' in expression specification

如果需要在连接前过滤,需要建立一个额外的子查询:

SELECT * FROM
  (SELECT * FROM
    (SELECT * FROM (SELECT null AS n1, 1 AS id) t1
    UNION ALL
    SELECT * FROM (SELECT 'one' AS n1, 2 AS id) t2) t
  WHERE t.n1 IS NOT NULL) tt
JOIN
  (SELECT * FROM (SELECT 'one' AS n1, 1 AS id) p1
  UNION ALL
  SELECT * FROM (SELECT null AS n1, 2 AS id) p2) p
ON tt.id = p.id;

iii. 仅使用 ON 筛选而不指定关联关系

相当于筛选后生成笛卡尔积:

SELECT * FROM
  (SELECT * FROM (SELECT null AS n1, 1 AS id) t1
  UNION ALL
  SELECT * FROM (SELECT 'one' AS n1, 2 AS id) t2) t
JOIN
  (SELECT * FROM (SELECT 'one' AS n1, 1 AS id) p1
  UNION ALL
  SELECT * FROM (SELECT null AS n1, 2 AS id) p2) p
ON t.n1 IS NULL;

-- 等价于
SELECT * FROM ...
WHERE t.n1 IS NULL;

结果:

t.n1t.idp.n1p.id
NULL1one1
NULL1NULL2

注意事项

  1. 连接顺序敏感:在执行 JOIN 操作时,ON 用于指定连接条件,WHERE 用于过滤。通常情况下先进行连接操作,再进行过滤操作
  2. 连接和过滤的区别:ON 中的连接条件用于确定如何将行从一个表与另一个表进行匹配;WHERE 中的过滤条件用于在连接后的结果中筛选满足条件的行
  3. NULL 值的处理:注意连接条件中的 NULL 值、"NULL""" 的区别

NULL 值判断

-- 使用 IS NULL 或 IS NOT NULL 判断 NULL 值
SELECT * FROM t WHERE col IS NULL;
SELECT * FROM t WHERE col IS NOT NULL;

-- 判断 "NULL" 字符串或空字符串
SELECT * FROM t WHERE col = 'NULL';
SELECT * FROM t WHERE col <> 'NULL';

NULL 转换

-- 使用 COALESCE 将 NULL 值替换为默认值
SELECT COALESCE(col, default_value) FROM t;

-- 使用 IF 判断并替换 NULL 字符串
SELECT IF(col = 'NULL', 'IS NULL', 'NOT NULL') FROM t;

-- 使用 CASE WHEN 判断并替换 NULL 字符串
SELECT CASE WHEN col = 'NULL' THEN 'IS NULL' ELSE 'NOT NULL' END FROM t;

13. UNION 合并

版本行为
Hive 1.2.0 之前仅支持 UNION ALL,重复的行不会被删除
Hive 1.2.0 及更高版本UNION 默认从结果中删除重复的行
SELECT * FROM t1
UNION ALL
SELECT * FROM t2;

-- Hive 1.2.0+ 去重
SELECT * FROM t1
UNION
SELECT * FROM t2;

14. 模糊匹配 LIKE 和 RLIKE

-- LIKE:% 表示 0 个或多个字符,_ 表示 1 个字符
SELECT * FROM emp WHERE name LIKE 'J%';

-- RLIKE:支持正则表达式
SELECT * FROM emp WHERE sal RLIKE '^[234]';

15. 查询并写入集群文件

-- DIRECTORY 关键词用于写入集群文件
INSERT OVERWRITE DIRECTORY '/tmp/hdfs_out' SELECT a.* FROM invites a WHERE a.ds='2008-08-15';

-- 写入集群文件时指定格式
INSERT OVERWRITE DIRECTORY '/tmp/hdfs_out'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
SELECT a.* FROM invites a WHERE a.ds='2008-08-15';

-- INTO 追加模式
INSERT INTO DIRECTORY '/tmp/hdfs_out' SELECT a.* FROM invites a WHERE a.ds='2008-08-15';

16. 查询并写入本地文件

-- LOCAL DIRECTORY 关键词用于写入本地文件
INSERT OVERWRITE LOCAL DIRECTORY '/tmp/local_out' SELECT a.* FROM pokes a;
INSERT INTO LOCAL DIRECTORY '/tmp/local_out' SELECT a.* FROM pokes a;

-- 格式化写入
INSERT OVERWRITE LOCAL DIRECTORY '/tmp/local_out'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
SELECT a.* FROM pokes a;

17. 查询并写入表

支持 INTO(追加)和 OVERWRITE(覆盖)两种模式。

注意: 追加插入表时建议使用 LEFT ANTI JOIN。插入时 Hive 只检查列数量,不检查字段名称是否一致,需要人工确保列对齐。

INSERT OVERWRITE TABLE events SELECT a.* FROM profiles a;
INSERT OVERWRITE TABLE events PARTITION(parColName) SELECT a.* FROM profiles a;
INSERT INTO TABLE events SELECT a.* FROM profiles a;

18. 查询并写入分区表(动态分区)

-- 启用动态分区
SET hive.exec.dynamic.partition = true;

-- 设置为非严格模式(允许全动态分区)
SET hive.exec.dynamic.partition.mode = nonstrict;

-- 动态插入分区(不指定具体值,留空表示动态)
INSERT INTO TABLE target_table
PARTITION (partition_col)  -- 不指定具体值,留空表示动态
SELECT col1, col2, ..., partition_col
FROM source_table;

-- 多个分区字段:都不赋值,分区字段放在 SELECT 最后
INSERT INTO TABLE logs PARTITION (dt, region)
SELECT user_id, event, dt, region
FROM raw_logs;

19. 全表导出到文件

EXPORT TABLE default.events TO '/users/lijp/temp';

20. INSERT 插入

-- 标准写法
INSERT OVERWRITE TABLE events
SELECT a.bar, count(*)
FROM invites a
WHERE a.foo > 0
GROUP BY a.bar;

-- FROM 关键词前置写法
FROM invites a
INSERT OVERWRITE TABLE events
SELECT a.bar, count(*)
WHERE a.foo > 0
GROUP BY a.bar;

21. 多表插入

FROM 关键词前置的主要用途:从同一张表中用不同的筛选条件获取记录,对多个不同表进行插入。

FROM src
INSERT OVERWRITE TABLE dest1 SELECT src.* WHERE src.key < 100
INSERT OVERWRITE TABLE dest2 SELECT src.key, src.value WHERE src.key >= 100 AND src.key < 200
INSERT OVERWRITE TABLE dest3 PARTITION(ds='2008-04-08', hr='12') SELECT src.key WHERE src.key >= 200 AND src.key < 300
INSERT OVERWRITE LOCAL DIRECTORY '/tmp/dest4.out' SELECT src.value WHERE src.key >= 300;

22. 创建临时表(CTE)

-- 创建一个临时表
WITH tableName1 AS (SELECT * FROM tableName2)
SELECT * FROM tableName1;

-- 创建多个临时表
WITH tableName1 AS (SELECT * FROM otherTableName1),
     tableName2 AS (SELECT * FROM otherTableName2)
SELECT *
FROM tableName1 AS t1
JOIN tableName2 AS t2 ON t1.id = t2.id;

23. 调用脚本进行流式处理

可以通过 Python 或 Shell 脚本对 Hive 数据进行流式处理。

样例文件 url.txt

http://www.baidu.com|测试规则|分类1|分类2|http://www.baidu.com/1231231231231231231231231312312313

步骤 1:建旧表并导入数据

-- 建旧表,存放样例文件
CREATE TABLE rule_old (
  host STRING,
  class1 STRING,
  class2 STRING,
  class3 STRING,
  url STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|';

-- 导入样例数据(注意:若文件分隔符与表字段分隔符不一致,则每行记录只对应第一列)
LOAD DATA LOCAL INPATH 'url.txt' OVERWRITE INTO TABLE rule_old;

步骤 2:编写 Python 脚本 line_split.py

import sys

for line in sys.stdin:
    line = line.strip()
    # 注意:这里接受的 line 为标准输入,使用 \t 分割,与建表语句中的字段分隔符无关
    [host, class1, class2, class3, url] = line.split("\t")
    # 注意:指定分隔符打印标准输出,分隔符必须与待写入表的字段分隔符一致,否则将写入第一列
    print("\t".join([host, class1, class2, class3, url]))

步骤 3:建新表并执行转换写入

-- 建新表,存放转换后的样例数据
CREATE TABLE rule_new (
  host1 STRING,
  class11 STRING,
  class22 STRING,
  class33 STRING,
  url1 STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t';

-- 处理文件并写入,AS 中的字段名称可随意指定,只根据字段数量进行检查
ADD FILE line_split.py;
INSERT OVERWRITE TABLE rule_new
SELECT TRANSFORM (host, class1, class2, class3, url)
USING 'python line_split.py'
AS (col1, col2, col3, col4, col5)
FROM rule_old;

24. 创建 UDF 函数

a. 创建临时函数

临时函数可以跨库运行。

-- 引入 jar 包
ADD JAR /user/lijp/tool/encrypt.jar;

-- 创建临时函数:CREATE TEMPORARY FUNCTION 函数名 AS 函数在 jar 包中的路径
CREATE TEMPORARY FUNCTION encrypt AS "com.fibodt.encrypt.RuleEncryptUtil.b";

b. 创建永久函数

永久函数需要使用 库名.函数名 的方式调用。

CREATE FUNCTION encrypt AS "com.fibodt.encrypt.RuleEncryptUtil.b"
USING AS "/user/lijp/temp/encrypt.jar";

25. Hive 数据类型

a. 数值类型

数据类型长度范围
TINYINT1 字节-128 ~ 127
SMALLINT2 字节-32,768 ~ 32,767
INT4 字节-2,147,483,648 ~ 2,147,483,647
BIGINT8 字节-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
FLOAT4 字节浮点型
DOUBLE8 字节浮点型
DECIMAL17 字节38 位,存储小数

b. 字符类型

数据类型描述
STRING使用时通常用单引号或双引号引用,支持 C 样式转义
VARCHAR变长字符串,最大长度 65535
CHAR定长字符串,最大长度 255

c. 日期类型

数据类型描述
TIMESTAMP支持 UNIX 时间戳,可选纳秒精度(精度 9)
DATEYYYY-MM-DD 格式
INTERVAL时间间隔
-- INTERVAL 使用示例
SELECT current_date() + INTERVAL '1-2' YEAR TO MONTH;  -- 增加 1 年 2 个月
SELECT current_date() + INTERVAL '1' DAY;               -- 增加 1 天
SELECT current_date() + INTERVAL '1' HOUR;              -- 增加 1 小时
SELECT current_date() + INTERVAL '1' SECOND;            -- 增加 1 秒

d. 布尔类型

数据类型描述
BOOLEANtrue / false

e. 复合类型

数据类型描述示例
ARRAY有序且同类型的数据集合,可用下标索引ARRAY('foo', 'bar')
MAPkey-value 对,通过 key 索引取值MAP('first', 'John', 'last', 'Doe')
STRUCT支持任意结构的组合,点号取值STRUCT('John', 'Doe')
UNIONTYPE一组异构类型集合-

26. Hive 运算符

a. 算术运算符

运算符描述
A + BA 和 B 相加
A - BA 减去 B
A * BA 和 B 相乘
A / BA 除以 B
A % BA 对 B 取余
A & BA 和 B 按位取与
A | BA 和 B 按位取或
A ^ BA 和 B 按位取异或
~AA 按位取反

b. 比较运算符

操作符支持的数据类型描述
A = B基本数据类型A 等于 B 返回 TRUE
A <=> B基本数据类型A 和 B 都为 NULL 返回 TRUE,任一为 NULL 返回 NULL
A <> B, A != B基本数据类型A 不等于 B 返回 TRUE,任一为 NULL 返回 NULL
A < B基本数据类型A 小于 B 返回 TRUE,任一为 NULL 返回 NULL
A <= B基本数据类型A 小于等于 B 返回 TRUE,任一为 NULL 返回 NULL
A > B基本数据类型A 大于 B 返回 TRUE,任一为 NULL 返回 NULL
A >= B基本数据类型A 大于等于 B 返回 TRUE,任一为 NULL 返回 NULL
A [NOT] BETWEEN B AND C基本数据类型A 在 B 和 C 之间返回 TRUE
A IS NULL所有数据类型A 为 NULL 返回 TRUE
A IS NOT NULL所有数据类型A 不为 NULL 返回 TRUE
IN(数值1, 数值2)所有数据类型A 在列表中返回 TRUE
A [NOT] LIKE BSTRINGB 为简单正则:x% 以 x 开头,%x 以 x 结尾,%x% 包含 x
A RLIKE B / A REGEXP BSTRINGB 为正则表达式,JDK 正则接口实现

c. 逻辑运算符

操作符含义
AND逻辑与
OR逻辑或
NOT逻辑非

27. Hive 内置函数

a. 字符函数

返回值函数描述
STRINGconcat(string|binary A, string|binary B…)按次序拼接字符串
INTinstr(string str, string substr)查找子字符串出现的位置
INTlength(string A)返回字符串长度
INTlocate(string substr, string str[, int pos])从 pos 位置后查找 substr 首次出现位置
STRINGlower(string A) / upper(string A)转小写 / 大写
STRINGregexp_replace(string INITIAL_STRING, string PATTERN, string REPLACEMENT)正则替换
ARRAYsplit(string str, string pat)按正则分割字符串
STRINGsubstr(string|binary A, int start, int len)截取子串
STRINGtrim(string A)去除前后空格
MAPstr_to_map(text[, delimiter1, delimiter2])字符串转 Map
BINARYencode(string src, string charset)按字符集编码为二进制

b. 类型转换函数

返回值函数描述
<type>cast(expr AS <type>)类型转换,如 cast("1" AS BIGINT)
BINARYbinary(string|binary)转换为二进制

c. 数学函数

返回值函数描述
DOUBLEround(DOUBLE a)四舍五入取整
DOUBLEround(DOUBLE a, INT d)四舍五入保留 d 位小数
BIGINTfloor(DOUBLE a)向下取整
DOUBLErand(INT seed)返回随机数,seed 为随机因子
DOUBLEpower(DOUBLE a, DOUBLE p)a 的 p 次幂
DOUBLEabs(DOUBLE a)绝对值

d. 日期函数

返回值函数描述
STRINGfrom_unixtime(bigint unixtime[, string format])时间戳转 format 格式
INTunix_timestamp()获取本地时区时间戳
BIGINTunix_timestamp(string date)yyyy-MM-dd HH:mm:ss 格式字符串转时间戳
STRINGto_date(string timestamp)返回日期部分
INTyear(string date) / month / day / hour / minute / second / weekofyear返回对应部分
INTdatediff(string enddate, string startdate)计算相差天数
STRINGdate_add(string startdate, int days)加 days 天
STRINGdate_sub(string startdate, int days)减 days 天
DATEcurrent_date当前日期
TIMESTAMPcurrent_timestamp当前时间戳
STRINGdate_format(date/timestamp/string ts, string fmt)格式化日期

e. 集合函数

返回值函数描述
INTsize(Map<K,V>)返回 Map 键值对个数
INTsize(ARRAY)返回数组长度
ARRAYmap_keys(Map<K,V>)返回 Map 所有 key
ARRAYmap_values(Map<K,V>)返回 Map 所有 value
BOOLEANarray_contains(ARRAY, value)数组是否包含 value
ARRAYsort_array(ARRAY)数组排序

f. 条件函数

返回值函数描述
Tif(boolean testCondition, T valueTrue, T valueFalseOrNull)条件判断,true 返回 valueTrue
Tnvl(T value, T default_value)value 为 NULL 返回 default_value
TCOALESCE(T v1, T v2, …)返回第一个非 NULL 值
TCASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END等值判断
TCASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END条件判断
BOOLEANisnull(a)a 为 NULL 返回 true
BOOLEANisnotnull(a)a 非 NULL 返回 true

g. 表生成函数

返回值函数描述
N rowsexplode(ARRAY)数组每个元素生成一行
N rowsexplode(MAP)每个键值对生成一行,包含 key 和 value 两列
N rowsposexplode(ARRAY)与 explode 类似,额外返回元素位置
N rowsstack(INT n, v_1, v_2, …, v_k)k 列转 n 行,每行 k/n 个字段
tuplejson_tuple(jsonStr, k1, k2, …)从 JSON 字符串中获取多个键,以元组返回

28. 行列转换

a. 行转列

函数描述
concat拼接多列
concat_ws拼接多列,以指定字符串分割
collect_set聚合多行,返回去重集合
collect_list聚合多行,返回列表

b. 列转行

  • EXPLODE(col):将 Array 或 Map 结构拆分成多行
  • LATERAL VIEW udtf(col) table_alias AS column_alias:与 splitexplode 等 UDTF 一起使用,将一列数据拆成多行,可对拆分后数据聚合 EXPLODE(col):将 hive 一列中复杂的 Array 或者 Map 结构拆分成多行。 LATERAL VIEW udtf(col) table_alias AS column_alias:用于和 splitexplodeudtf 一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。 table_alias 是表的别名【可省】, column_alias 是新生成的列名。该语句应当在from语句的后面。 udtf(col):生成虚拟表,table_aliascolumn_alias对应了两种字段选择方式 table_alias:虚拟表别名,需要一次性获得虚拟表中所有字段时使用,例如:table_alias.*(注意:table_alias本质上是只包含了一列,且这一列就是一个 struct 类型的列表) 示例: SELECT tableAlias.* FROM etl_fetch.5_utag_result_30day LATERAL VIEW explode(hosts) table_alias AS column_alias column_alias:虚拟列别名,需要精确获得虚拟表中某一个字段时使用,例如:column_alias.字段名(注意:column_alias本质上是对table_alias的解包,获取了其内部的唯一的列,该列为 struct 类型) 示例: SELECT column_alias.score FROM etl_fetch.5_utag_result_30day LATERAL VIEW explode(hosts) table_alias AS column_alias

29. 窗口函数

a. 分析函数

关键字描述
OVER()指定分析函数工作的数据窗口大小,随行变化
CURRENT ROW当前行
n PRECEDING往前 n 行数据
n FOLLOWING往后 n 行数据
UNBOUNDED PRECEDING从前面的起点
UNBOUNDED FOLLOWING到后面的终点
LAG(col, n)往前第 n 行数据
LEAD(col, n)往后第 n 行数据
NTILE(n)将有序分区分发到 n 个组中,返回组编号(从 1 开始)

b. 聚合函数

函数描述
RANK()排序相同时会重复,总数不变
DENSE_RANK()排序相同时会重复,总数减少
ROW_NUMBER()按顺序递增编号

c. 示例

SELECT name, subject, score,
  RANK() OVER(PARTITION BY subject ORDER BY score DESC) AS rp,
  DENSE_RANK() OVER(PARTITION BY subject ORDER BY score DESC) AS drp,
  ROW_NUMBER() OVER(PARTITION BY subject ORDER BY score DESC) AS rmp
FROM score;

30. 排序

a. 全局排序(ORDER BY)

SELECT * FROM emp ORDER BY deptno [ASC | DESC];

b. 每个 Reduce 内部排序(SORT BY)

-- 设置 reduce 个数
SET mapreduce.job.reduces = 3;

-- 查看设置的 reduce 个数
SET mapreduce.job.reduces;

-- 根据部门编号降序查看员工信息
SELECT * FROM emp SORT BY deptno [ASC | DESC];

c. 每个分区内部排序(DISTRIBUTE BY + SORT BY)

注意: Hive 要求 DISTRIBUTE BY 必须写在 SORT BY 之前。

SELECT * FROM emp DISTRIBUTE BY deptno SORT BY month;

d. CLUSTER BY

当 Reduce 排序和分区排序的字段相同时,可用 CLUSTER BY 简写:

-- 以下二者等价
SELECT * FROM dept_partition DISTRIBUTE BY deptno SORT BY deptno;
SELECT * FROM dept_partition CLUSTER BY deptno;

31. 空值处理

NVL:给值为 NULL 的数据赋默认值。

-- 格式:NVL(string1, replace_with)
-- 如果 string1 为 NULL,返回 replace_with;否则返回 string1
-- 两个参数都为 NULL 则返回 NULL
SELECT NVL(col, 'default_value') FROM t;

32. Hive 文件格式

a. TEXTFILE

Hive 默认格式,以 \n 为换行符,\001 为字段间隔,\002 为 ARRAY/STRUCT 等容器元素间隔,\003 为 MAP 键间隔。

CREATE TABLE test (
  col1 INT,
  col2 STRING
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS TEXTFILE;

b. SEQUENCEFILE

Hadoop API 提供的二进制文件格式,以 <key, value> 形式序列化。Key 为空,value 存放实际值,避免 MR map 阶段排序。比 Text 更紧凑,支持 Split,但无 Metadata,只能新增字段。

生产中基本不会用,k-v 格式比源文本格式占用磁盘更多。

CREATE TABLE test (
  col1 INT,
  col2 STRING
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS SEQUENCEFILE;

c. RCFILE

Hive 推出的面向列的存储格式,遵循”先按列划分,再垂直划分”的设计理念。

生产中用的少,行列混合存储,ORC 是它的升级版。

CREATE TABLE test (
  col1 INT,
  col2 STRING
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS RCFILE;

d. ORC

ORC(Optimized RCFile)在压缩编码和查询性能方面相比 RCFile 做了很多优化。Metadata 用 Protobuf 存储,支持 Schema 变更(新增/删除字段)。

生产中最常用,列式存储。

CREATE TABLE test (
  col1 INT,
  col2 STRING
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS ORC;

e. PARQUET

源自 Google Dremel 系统,Apache Parquet 设计用于存储嵌套式数据(ProtocolBuffer、Thrift、JSON 等),能以列式格式高效压缩编码,使用更少 IO 操作。

相比 ORC 的优势:能够透明地将 Protobuf 和 Thrift 类型数据进行列式存储,支持 Schema 变更。

生产中最常用,列式存储。

CREATE TABLE test (
  col1 INT,
  col2 STRING
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS PARQUET;

f. AVRO

二进制文件格式,更为紧凑,读取大量数据时提供更好的序列化/反序列化性能。天生带有 Schema 定义,多个 Hadoop 子项目(Pig、Hive、Flume、Sqoop、Hcatalog)均支持。

生产中几乎不用的二进制文件格式。

CREATE TABLE test (
  col1 INT,
  col2 STRING
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\001'
  COLLECTION ITEMS TERMINATED BY '\002'
  MAP KEYS TERMINATED BY '\003'
  LINES TERMINATED BY '\n'
STORED AS AVRO;

g. 自定义文件格式

通过继承 InputFormatOutputFormat 来自定义文件格式。

以读取 Base64Text 文件为例:

CREATE TABLE base64example (line STRING)
STORED AS
  INPUTFORMAT 'org.apache.hadoop.hive.contrib.fileformat.base64.Base64TextInputFormat'
  OUTPUTFORMAT 'org.apache.hadoop.hive.contrib.fileformat.base64.Base64TextOutputFormat';

33. 常用设置

注意: 凡是涉及文件大小的参数,均以字节为单位。

1. 引擎设置为 MR 时的优化

动态分区

-- 打开动态分区后,允许所有分区都是动态分区模式
SET hive.exec.dynamic.partition.mode = nonstrict;

-- 是否启动动态分区
SET hive.exec.dynamic.partition = true;

-- 设置每个任务允许创建动态分区的最大数量
SET hive.exec.max.dynamic.partitions = 1000000;

-- 设置每个节点上动态分区的最大数量
SET hive.exec.max.dynamic.partitions.pernode = 10000000;

小文件合并

文件数目小,容易在文件存储端造成瓶颈,给 HDFS 带来压力,影响处理效率。可以通过合并 Map 和 Reduce 的结果文件来消除影响。

-- 设置 map 端输出进行合并,默认为 true
SET hive.merge.mapfiles = true;

-- 设置 reduce 端输出进行合并,默认为 false
SET hive.merge.mapredfiles = true;

并行执行与聚合

-- 开启并行执行:MapReduce 阶段、抽样阶段、合并阶段、limit 阶段等并行执行
SET hive.exec.parallel = true;

-- 关闭并发,防止锁表
SET hive.support.concurrency = false;

-- 是否在 map 端进行聚合,默认为 true,可防止数据倾斜
SET hive.map.aggr = true;

调整 Mapper 和 Reducer 个数

减少 Map 数:

下面三个参数确定合并文件块的大小:大于 128MB 的按 128MB 分隔,小于 128MB 且大于 100MB 的按 100MB 分隔,小于 100MB 的进行合并。

SET mapred.max.split.size = 100000000;
SET mapred.min.split.size.per.node = 100000000;
SET mapred.min.split.size.per.rack = 100000000;

-- 执行前进行小文件合并
SET hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

增加 Map 数:

当 input 文件都很大、任务逻辑复杂、map 执行非常慢时,可以增加 Map 数来使每个 map 处理的数据量减少,提高任务执行效率。

SET mapred.reduce.tasks = 10;

调整 Reducer 个数:

-- 增加 reducer 数
SET mapreduce.job.reduces = ?;

-- 每个 reduce 任务处理的数据量,默认 1GB
SET hive.exec.reducers.bytes.per.reducer = 1000000000;

-- 每个任务最大的 reduce 数,默认为 999
SET hive.exec.reducers.max = 1009;

-- 方法一:调整每个 reduce 处理的数据量(如 500MB)
SET hive.exec.reducers.bytes.per.reducer = 500000000;

-- 方法二:直接设置每个 job 的 reduce 个数
SET mapreduce.job.reduces = 15;

其他 MR 优化

-- 严格模式:默认关闭,禁止三种类型查询:分区表不指定分区查询、笛卡尔积、使用 ORDER BY
SET mapred.mode = strict;

-- JVM 重用:使用派生 JVM 来执行 map 和 reduce 任务
SET mapred.job.jvm.numtasks = 1;

-- Map Join 优化:不太大的表直接通过 map 过程做 join
SET hive.auto.convert.join = true;
SET hive.auto.convert.join.noconditionaltask = true;

2. 引擎设置为 Spark 时的优化

若内存允许,优先考虑使用 Spark。

SET hive.execution.engine = spark;
-- 合并小文件(Hive on Spark 下的选项)
SET hive.merge.sparkfiles = true;

-- Hive on Spark 下改用在内存中存储的近似大小,迁移时要适当调高(如 100~200MB)
SET hive.auto.convert.join.noconditionaltask.size = 100000000;

3. 通用优化参数汇总

-- 简单查询仅包含一个 GROUP BY 和 ORDER BY 时,可设为 1 或 2
SET hive.optimize.reducededuplication.min.reducer = 4;

-- 数据已按相同 key 聚合时,去除多余 map/reduce 作业
SET hive.optimize.reducededuplication = true;

-- 合并小文件相关
SET hive.merge.smallfiles.avgsize = 16000000;
SET hive.merge.size.per.task = 256000000;
SET hive.merge.sparkfiles = true;
SET hive.merge.orcfile.stripe.level = true;

-- Map Join 优化
SET hive.auto.convert.join = true;
SET hive.auto.convert.join.noconditionaltask = true;

-- 可转化为 HashMap 放入内存的表大小(Spark 建议 200MB)
SET hive.auto.convert.join.noconditionaltask.size = 20971520;

-- 如果数据按 join 的 key 分桶,Hive 将简单优化 inner join(官方推荐关闭)
SET hive.optimize.bucketmapjoin = false;
SET hive.optimize.bucketmapjoin.sortedmerge = false;

-- 所有 map 任务可用作 Hashtable 的内存百分比(OOM 时调小,默认 0.5)
SET hive.map.aggr.hash.percentmemory = 0.5;

-- map 端聚合(与 GROUP BY 相关),会用更多内存
SET hive.map.aggr = true;

-- 关闭对所有字段排序(Hive 0.13 存在 bug)
SET hive.optimize.sort.dynamic.partition = false;

-- 新创建的表/分区是否自动计算统计数据
SET hive.stats.autogather = true;
SET hive.stats.fetch.column.stats = true;
SET hive.compute.query.using.stats = true;

-- ORDER BY LIMIT 查询中分配存储 Top K 的内存比例
SET hive.limit.pushdown.memory.usage = 0.4;

-- 是否开启自动使用索引
SET hive.optimize.index.filter = true;

-- 单个 reduce 处理的数据量(影响 reduce 数量)
SET hive.exec.reducers.bytes.per.reducer = 67108864;

-- Map Join 任务 HashMap 中 key 对应 value 数量
SET hive.smbjoin.cache.rows = 10000;

-- 将只有 SELECT、FILTER、LIMIT 转化为 FETCH,减少等待时间
SET hive.fetch.task.conversion = more;
SET hive.fetch.task.conversion.threshold = 1073741824;

-- 显示列名
SET hive.cli.print.header = true;

-- 不显示表名
SET hive.resultset.use.unique.column.names = false;

34. SparkSQL 的 Tips

PySpark 环境下换行

df = spark.sql("""
SELECT *
FROM tableName1
JOIN tableName2
ON tableName1.id = tableName2.id
""")

Spark 环境下换行

val df = spark.sql("""
SELECT *
FROM tableName1
JOIN tableName2
ON tableName1.id = tableName2.id
""")

35. Hive 安装与配置

a. 通过 Homebrew 安装

brew install hive

b. 系统环境配置

编辑 ~/.bash_profile

vim ~/.bash_profile

Hive 系统变量

# hive
HIVE_HOME=/usr/local/Cellar/hive/3.1.3
PATH=$HIVE_HOME:$PATH
PATH=$HIVE_HOME/bin:$PATH
export PATH

Hadoop 系统变量

# hadoop
HADOOP_HOME=/usr/local/Cellar/hadoop/3.3.2
PATH=$HADOOP_HOME/bin:$PATH
PATH=$HADOOP_HOME/sbin:$PATH
export PATH

Java 系统变量

# java
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home
PATH=$JAVA_HOME:$PATH
PATH=$JAVA_HOME/bin:$PATH
export PATH

使配置生效

source ~/.bash_profile

c. Hive 环境配置

编辑 $HIVE_HOME/libexec/conf/hive-site.xml

vim $HIVE_HOME/libexec/conf/hive-site.xml

i. 配置元数据库 MySQL

注意: 端口不能有错,createDatabaseIfNotExist=true 表示初始化时自动创建。

<property>
    <name>hive.metastore.local</name>
    <value>true</value>
</property>
<property>
    <name>javax.jdo.option.ConnectionURL</name>
    <value>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true</value>
</property>

ii. 配置数据库连接驱动

<property>
    <name>javax.jdo.option.ConnectionDriverName</name>
    <value>com.mysql.jdbc.Driver</value>
</property>

iii. 配置数据库账号密码

<property>
    <name>javax.jdo.option.ConnectionUserName</name>
    <value>root</value>
</property>
<property>
    <name>javax.jdo.option.ConnectionPassword</name>
    <value>5718515abc</value>
</property>

iv. 配置数据库 Schema 相关参数

<property>
    <name>hive.metastore.schema.verification</name>
    <value>false</value>
</property>
<property>
    <name>datanucleus.schema.autoCreateAll</name>
    <value>true</value>
</property>

v. 配置执行计划与临时文件目录

Hive 用来存储不同阶段的 Map/Reduce 执行计划目录,同时也存储中间输出结果。

<property>
    <name>hive.exec.local.scratchdir</name>
    <value>/tmp/hive</value>
</property>
<property>
    <name>hive.downloaded.resources.dir</name>
    <value>/tmp/hive</value>
</property>
<property>
    <name>hive.server2.logging.operation.log.location</name>
    <value>/tmp/hive</value>
</property>

vi. 配置数据存储目录

后续创建的数据库、表都会在该目录下,该目录若没有写权限会导致建库建表失败。

<property>
     <name>hive.metastore.warehouse.dir</name>
     <value>/Users/lijp/hive/warehouse</value>
</property>

vii. 【可选】配置 Beeline 免密认证

设置为 NONE 可免密登录。注意: 不能与账户密码同时配置。

<property>
    <name>hive.server2.authentication</name>
    <value>NONE</value>
    <description>
      Expects one of [nosasl, none, ldap, kerberos, pam, custom].
      Client authentication types.
        NONE: no authentication check
        LDAP: LDAP/AD based authentication
        KERBEROS: Kerberos/GSSAPI authentication
        CUSTOM: Custom authentication provider
        PAM: Pluggable authentication module
        NOSASL: Raw transport
    </description>
</property>

viii. 【可选】配置 Beeline 账户密码

注意: 不能与 hive.server2.authentication 同时配置。

<property>
    <name>hive.server2.thrift.client.user</name>
    <value>root</value>
    <description>Username to use against thrift client</description>
</property>
<property>
    <name>hive.server2.thrift.client.password</name>
    <value>123456</value>
    <description>Password to use against thrift client</description>
</property>

配置后的登录方式:

# 方式一:命令行直接登录
sudo hadoop fs -chmod -R 777 /tmp/hive && beeline -u jdbc:hive2://localhost:10000 -n root -p 123456

# 方式二:进入 beeline 命令行后连接
beeline
!connect jdbc:hive2://localhost:10000 root 123456

d. 常见报错与解决方案

i. 元数据库表缺失

报错:

java.sql.SQLSyntaxErrorException: Table 'hive.version' doesn't exist

原因: MySQL 中对应的元数据库未正确初始化。

方案 1 - 自动初始化:

schematool -dbType mysql -initSchema

方案 2 - 手动初始化:

# 切换至对应目录
cd $HIVE_HOME/libexec/scripts/metastore/upgrade/mysql

# 打开 MySQL 命令行
mysql -u root -p

# 切换至元数据库并执行初始化脚本
use hive;
source /usr/local/Cellar/hive/3.1.3/libexec/scripts/metastore/upgrade/mysql/hive-schema-3.1.0.mysql.sql;

ii. 无写入权限

报错:

Exception in thread "main" java.lang.RuntimeException: The dir: /tmp/hive on HDFS should be writable. Current permissions are: rwxr-xr-x

原因: /tmp/hive 无写入权限。

解决:

sudo hadoop fs -chmod -R 777 /tmp

iii. 无建库建表权限

报错:

FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. MetaException(message:Unable to create database path file:/user/hive/warehouse/test.db, failed to create database test)

原因: 对应目录 /user/hive/warehouse 无写入权限。

解决: 修改 hive-site.xml,改为有写入权限的目录:

<property>
     <name>hive.metastore.warehouse.dir</name>
    <value>/Users/lijp/hive/warehouse</value>
</property>