本文章基于安徽理工大学数据科学与大数据技术2024级《分布式数据库原理与技术》课程授课内容整合重构,搭建条理清晰、体系完整的知识框架,可供同学们复习查阅。
Chiale
2026.6.23

一、大数据领域科普概念(Lesson 1,仅作了解)

数据中台

数据湖仓

数据脱敏

分布式数据库雏形(ICBC Bank)

区块链

TCP 三次握手、四次挥手

联邦学习 / 深度学习


二、分布式数据库理论基础(Lesson 2)

  1. 三级模式/两级映像
    在分布式数据库中仍然保留,并增加了新内容:全局模式分片模式
  2. 性能对比指标
    与传统 RDBMS 对比的指标:存储空间大小检索效率
  3. HDFS(GFS)的共享存储
    通过何种机制实现共享服务(问题提出,未在笔记中展开答案)。
  4. 数据分类
    结构化数据:MySQL、Oracle 等
    半结构化数据:JSON、XML
    非结构化数据:视频等(处理路径:视频 → YOLO → JSON)

三、Hive 基础环境与工作模式(Lesson 2 - Lesson 3)

  1. Hive 的三种部署方式(Lesson 2 Q4)
    嵌入式 Derby、远程 Metastore、本地 Metastore
  2. Hive 的三种工作模式(Lesson 3 Q1)
    Derby 模式远程模式本地模式
  3. 环境操作要点
    启动顺序:关闭防火墙 → 启动 Zookeeper → 检查状态 → 在 master 上 sbin/start-all.sh 启动 Hadoop
    MySQL 安装在 slave2,Hive 启用本地模式
    关闭顺序:Hive 中 exit → master sbin/stop-all.sh → 所有机器 zkServer.sh stop → 全部关机
    统一数据库名:bigdata2024,本地数据目录:/data
  4. Metastore 存储位置
    存储在 HDFS 上的 /warehousedir/home(除 Derby 模式外)

四、Hive 表与数据管理(Lesson 3 - Lesson 9)

4.1 基本操作

查看表结构:desc 表名 (展示字段名)

查看详细参数:desc formatted 表名

显示建表语句:show create table 表名

永久配置:修改 hive-site.xml;临时配置:Hive 控制台直接使用 set命令,例如:

set hive.cli.print.current.db=true;
set hive.cli.print.header=true;

4.2 表的默认存储与数据导入方式

默认存储位置:/warehousedir/home/xxxx.db/表名(即 Directory)

数据导入的三种方式:

  1. insert 语句insert into table 表名 values(...); 或从其他表查询插入,会在表目录下生成文件 000000_0
  2. hdfs 上传hdfs dfs -put 本地文件 表对应的HDFS目录,文件会直接成为表数据的一部分
  3. load data 命令load data local inpath '本地文件路径' into table 表名;会将本地文件复制到表目录下,若文件与表目录中已有文件重名,会自动生成带序号的副本(如 xxx_copy_1)。若使用 overwrite 关键字则会先清空表目录下的所有文件再装载。

4.3 自定义存储位置(LOCATION)

可以使用 location 子句指定表在 HDFS 上的存储路径,此时不会自动创建 数据库.db/表名 的层级目录,而是直接使用给定路径。

要求:location 路径最好与默认管理体系一致,且精确到表名级别;建表时若有其他约束,location 必须放在最后一行。

示例:

create table stu_test(sid int, sname string, sage int, addr string) 
location '/testdb';

此时 HDFS 上只有 /testdb 目录,不会出现 xxx.db/stu_test

通常推荐的做法是让 location 的末级目录与表名一致,并放在数据库目录下,以便管理。

4.4 复杂数据类型(Array、Map、Struct)详细过程

1. Array 类型

建表语句:

create table student1(sid int, sname string, grade array<float>)
row format delimited
fields terminated by ','
collection items terminated by '#'
lines terminated by '\n';

分隔符说明:字段之间用逗号分隔;数组内部元素用 # 分隔;行之间用换行符。

数据文件student1.txt 的内容格式如下:

1,zhangsan,80#90.5#35
2,lisi,90# #88
3,wangwu,87#87
4,mary, #60#
5,tom,60# #
6,jemy,78#

解析过程:Hive 读取文件时先按行分割,再按逗号切分出 sidsname 和整个数组字符串 "80#90.5#35",然后用 # 切分数组元素,每个元素转换为 float。若某元素为空字符串(如 90# #88 中的第二个 # 之间为空),则解析为 NULL。末位的空元素(如 78#)会被忽略,因此 jemy 的数组只含一个元素 [78.0]

装载数据:

load data local inpath '/opt/data/student1.txt' into table student1;

查询数组元素(索引从 0 开始):

select grade[1] as second_grade from student1 where sid = 101;

2. Map 类型

建表语句:

create table student2(sid int, sname string, grade map<string,float>)
row format delimited
fields terminated by ','
collection items terminated by '#'
map keys terminated by ':'
lines terminated by '\n';

分隔符说明:字段间用逗号;Map 中的键值对之间用 # 分隔;键和值之间用 : 分隔。

数据文件student2.txt 示例:

1,zhangsan,语文:80#数学:90.5#英语:35
2,lisi,语文:90#数学:95#英语:88

装载方式同 Array。

查询 Map 中指定键的值

select grade['数学'] as math_score from student2 where sid = 101;

注意:如果建表时去掉 collection items terminated by '#',则 Hive 无法识别多个键值对之间的分隔,整个 grade 列会被当作一个单一字符串,无法正常解析为 Map。

3. Struct 类型

建表语句:

create table student4(sid int, info struct<name:string, age:int, sex:string>)
row format delimited
fields terminated by ','
collection items terminated by '#'
lines terminated by '\n';

数据文件student4.txt 内容:

1,zhangsan#19#Female
2,lisi#20#male

解析说明:Struct 内部成员之间用 # 分隔,成员顺序与建表时定义的 struct 属性顺序严格一致。可以使用 info.nameinfo.age 等方式访问成员。

4. 复杂类型嵌套限制

array<map<string,float>>map<string,array<float>> 等嵌套结构,无法直接使用分隔符方式定义。

若要实现此类嵌套,必须使用 JSON SerDe(如 'org.apache.hive.hcatalog.data.JsonSerDe'),并将数据文件组织为标准 JSON 格式。示例:

create table student3(sid int, sname string, grade array<map<string,float>>)
row format serde 'org.apache.hive.hcatalog.data.JsonSerDe';

数据文件内容需类似:

{"sid":1,"sname":"zhangsan","grade":[{"语文":80},{"数学":90.5}]}

核心结论:Array、Map、Struct 三种原生集合类型不能互相嵌套使用;Array 和 Struct 内部元素靠 collection items 分隔,Map 由于自带键值对分隔,隐含了 collection items 的功能。

4.5 表的类型与管理

1. 管理表(内部表)与外部表

创建外部表:

create external table student_cp(sid int, sname string) 
row format delimited fields terminated by ',' 
location '/tmpdb/student_cp';

区别过程:使用 drop table 删除表时:

内部表:元数据与 HDFS 上的数据文件一并删除

外部表:仅删除元数据,location 指向的文件夹及数据全部保留。下次创建同名、同结构的外部表并指向同一 location 时,可直接读取原有数据。

数据加载区别

load data inpath '云端路径' into table 表名; 会将 HDFS 上的文件移动到表目录,原路径文件消失。

load data local inpath '本地路径'复制操作,本地文件不删除。

2. 分区表(静态分区与动态分区)

静态分区创建与装载全过程

  1. 建表,指定分区字段(注意:分区字段不能是表中已有的普通字段):
create table part_table(sid int, sname string, sage int) 
partitioned by(addr string) 
row format delimited fields terminated by ',';
  1. 装载数据时明确写出分区值:
load data local inpath '/home/student100' into table part_table partition(addr='guangxi');
  1. HDFS 存储结构:
    数据文件会被放置于 /warehousedir/home/exam.db/part_table/addr=guangxi/ 目录下,文件名保持为 student100。原有数据内容并不包含addr 列,该列的值完全由分区目录名提供,查询时自动作为虚拟列出现。
  2. 若重复执行相同分区的装载,同一目录下会出现 student100_copy_1 等带编号的副本文件。
  3. 管理分区:
alter table part_table add partition(addr='guangxi');   -- 添加分区(新建目录)
alter table part_table drop partition(addr='guangxi');  -- 删除分区(删除对应目录及所有文件)
  1. 多级分区:
create table part_table2(sid int, sname string) 
partitioned by(sage int, addr string) 
row format delimited fields terminated by ',';
-- 装载时需指定所有分区字段的值
load data local inpath '/home/student100' into table part_table2 partition(sage=20, addr='guangxi');

目录结构变为 .../sage=20/addr=guangxi/

非分区表无法转换为分区表,只能重建。

动态分区:建表语法与静态分区完全相同,只是在插入数据时由查询语句决定分区值(例如通过 insert ... select addr from ...),装载速度更快,但对数据质量的控制不如静态分区精准。

3. 桶表

建表时指定分桶字段和排序字段:

create table buck_table(sid int, sname string) 
clustered by(sid) sorted by(sname desc) into 4 buckets 
row format delimited fields terminated by ',';

数据装载的关键区别

  1. 若使用 load data local inpath ... 直接装载文件,HDFS 上不会生成分桶目录,仅有一个原始文件,且数据未按 sid 分桶,也未按 sname 排序。
  2. 必须通过 insert into 从其他表查询导入,才会真正执行分桶和排序:
insert into table buck_table select * from source_table;

此时 HDFS 表目录下会出现 000000_0000001_0 等文件,编号从 0 开始,每个文件对应一个桶。

采样查询

select * from buck_table tablesample(bucket 1 out of 4 on sid);

无论数据是通过 load data 还是 insert 进入的,采样语法都可执行。但对于 load data 进入的未分桶数据,采样可能返回空或不符合预期结果;只有经过 insert 分桶后的数据才能正确按桶采样。


五、数据查询与连接(Lesson 10)

5.1 连接类型详解

笛卡尔连接select * from student100, sc100; 无连接条件,返回两表行数的乘积,通常无实际意义。

内连接(自然连接)join ... on ...,只返回两表中满足等值条件的行,学生必须同时出现在 student100 和 sc100 中(不要求一定有成绩)。

左连接left join ... on ...,以左表(student100)为主,返回左表所有行。右表无匹配时,sc100 的字段为 NULL。

右连接right join ... on ...,以右表(sc100)为主,返回右表所有行。

全外连接full outer join ... on ...,同时包含左右连接的结果,相当于 left joinright join 的并集(已去重)。

左半连接left semi join ... on ...本质是嵌套查询,只返回左表中能够在右表找到匹配键的行,且查询结果中不允许出现右表的字段。它在 Hive 中相当于 where exists (select 1 from 右表 where 条件) 但性能更优。

限制:Hive 的 left semi join 只支持一层嵌套。若要实现多层筛选(如选课人数大于2的学生),必须借助中间表或派生表:

-- 先找出人数>2的课程对应的学号,作为派生表
select * from student100
inner join (
    select sc100.sno from sc100 
    join tempsc100 on sc100.cno = tempsc100.cno 
    where cnt > 2
) a on student100.sno = a.sno;

这样就能避免直接使用多层 left semi join 的限制,且不需要创建物理中间表。

5.2 数据导出方式

输出到 HDFS 目录:

insert overwrite directory '/hdfs路径' 
row format delimited fields terminated by '#' 
select * from 表名;

从 HDFS 下载到本地:hadoop fs -get /hdfs路径 本地路径

拷贝表:

深拷贝create table new_table as select * from old_table; —— 执行 MapReduce,复制表结构和数据。

浅拷贝create table new_table like old_table; —— 仅复制表结构,数据为空。


六、视图、临时表与行列转换(Lesson 11 - Lesson 12)

6.1 视图与物理表的选择

创建视图:

create view viewdb as select ... from tb_case group by dname;

创建过程不启动 MapReduce,视图只是存储查询定义。当执行 select * from viewdb 时会启动 MapReduce 计算。

创建物理表:

create table tmp_case as select ... from tb_case group by dname;

会在执行时启动 MapReduce,并将结果持久化到 HDFS,后续查询不重复计算。

临时性结果集:with t as (select ...) 可用于复杂查询的分解,提升可读性。

6.2 条件函数与行列转换

if(条件, 真值, 假值) 用于二分类。

case when ... then ... else ... end 用于多条件转换,例如:

select name, dname,
       case gender when '男' then 'm' when '女' then 'f' end
from tb_case;

行转列(透视)示例:统计各部门男女数量。

select dname,
       count(*) as cnt,
       sum(if(gender='男',1,0)) as M,
       sum(if(gender='女',1,0)) as F
from tb_case
group by dname;

聚合注意事项:sum(jb+jj+tc) 不能简写为 sum(jb, jj, tc)

greatest() 是行内最大值函数(取一行多列的最大值),max() 是列聚合函数(取多行某一列的最大值)。


七、常用函数与实战案例(Lesson 13 - Lesson 16)

7.1 字符串函数

length(), concat(), trim()(只去除开头和结尾空格),upper(), lower(), substr(), instr()

示例:

select length('你好');   -- 返回字符数
select trim(concat(' hello ', ' world '));  -- 返回去掉首尾空格的结果

7.2 日期与数值函数(身份证号实战)

提取出生日期并格式化

alter table cid_info add columns(birthday string);
-- 使用 insert overwrite 实现只更新新增列(需要包含所有旧字段)
insert overwrite table cid_info 
select id, cid, substr(cid,8,8) as birthday from cid_info;

alter table cid_info add columns(birth_date string);
insert overwrite table cid_info 
select id, cid, birthday,
       from_unixtime(unix_timestamp(substr(cid,8,8),'yyyyMMdd'),'yyyy-MM-dd') as birth_date
from cid_info;

计算实岁与虚岁

alter table cid_info add columns(age_shi float, age_xu int);
insert overwrite table cid_info
select id, cid, birthday, birth_date, birth_year, birth_month, birth_day,
       round(datediff(current_date, birth_date)/365, 1) as age_shi,
       year(current_date) - birth_year as age_xu
from cid_info;

实岁:通过当前日期与出生日期相差天数除以365,保留一位小数。

虚岁:当前年份减去出生年份。

round 函数取整规则

round(45.836, -1) 对个位四舍五入,结果为 50.0

round(45.836, -2) 对十位四舍五入,结果为 0.0

round(95.836, -2) 对十位四舍五入,结果为 100.0

7.3 Hive 查询核心思路与限制

查询语句结构:selectdistinctfromjoin/onwheregroup byorder bylimit

ORC 表在开启事务后可执行 updatedelete,而 TextFile 格式不支持行级更新,只能进行 insertinsert overwrite 覆盖。

当需要对已有表增加列并填充数据时,不能直接 update 某一列,通常采用 alter table add columns 添加列,然后通过insert overwrite table 全量回填所有列的方式实现。


八、JSON 数据处理(Lesson 15)

解析 JSON 需借助外部 jar 包或内置函数 get_json_object

对于复杂嵌套类型(如 array<map<string,float>>),必须使用 JSON SerDe,并在建表时指定:

create table student3(sid int, sname string, grade array<map<string,float>>)
row format serde 'org.apache.hive.hcatalog.data.JsonSerDe';

数据文件每行必须为标准 JSON,例如:

{"sid":1,"sname":"zhangsan","grade":[{"语文":80},{"数学":90.5}]}
最后修改:2026 年 06 月 23 日
如果觉得我的文章对你有用,请随意赞赏