在工作中我们经常使用的数据库, 数据库一般存放的我们系统中常用的数据, 一般为百万级别. 如果数据量庞大, 达到千万级, 亿级又需要对他们进行关联运算, 该怎么办呢?
前面我们已经介绍了 HDFS 和 MapReduce 了, 它俩结合起来能够进行各种运算, 可是 MapReduce 的学习成本太高了, 如果有一种工具可以直接使用 sql 将 hdfs 中的数据查出来, 并自动编写 mapreduce 进行运算, 这就需要使用到我们的 hive 数据仓库.
Hive 基本概念
什么是 Hive
Hive 是基于 Hadoop 的一个数据仓库工具, 可以将结构化的数据文件映射为一张数据库表, 并提供类 SQL 查询功能.
为什么使用 Hive
直接使用 hadoop 所面临的问题
人员学习成本太高
项目周期要求太短
MapReduce 实现复杂查询逻辑开发难度太大
为什么要使用 Hive
操作接口采用类 SQL 语句, 提供快速开发的能力.
避免了去写 MapReduce, 减少开发人员的学习成本.
扩展功能很方便
Hive 的特点
可扩展
Hive 可以自由的扩展集群的规模, 一般情况下不需要重启服务.
延展性
Hive 支持用户自定义函数, 用户可以根据自己的需求来实现自己的函数. 注意: 这里说的函数可不是存储过程噢.
容错
良好的容错行, 节点出现问题 SQL 仍可以完成执行
基本组成
用户接口: 包括 CLI,JDBC/ODBC,webGUI
元数据存储: 通常是存储在关系数据库如 MySQL,derby 中.
解释器, 编译器, 优化器, 执行器
各组件的基本功能
用户接口主要有三个: CLI,JDBC/ODBC 和 WebGUI. 其中, CLI 为 shell 命令行; JDBC/ODBC 是 Hive 的 JAVA 实现, 与传统数据库 JDBC 类似; WebGUI 是通过浏览器访问 Hive
元数据存储: Hive 将元数据存储在数据库中. Hive 中的元数据包括表的名字, 表的列和分区及其属性, 是否为外部表, 表的数据所在的目录等等.
解释器, 编译器, 优化器完成 HQL 查询语句从此法分析, 语法分析, 编译, 优化以及查询计划的生成. 生成的查询计划存储在 HDFS 中, 并且随后使用 MapReduce 执行.
Hive 与 Hadoop 的关系
sequenceDiagram
客户端 ->>Hive 处理转换成 MapReduce: 发送 HSQL 语句
Hive 处理转换成 MapReduce->>MapReduce 运行: 提交任务到 Hadoop
MapReduce 运行 ->>执行结果文件放到 HDFS 或本地: 执行结果
Hive 与传统数据库对比
--- | Hive | RDBMS |
---|---|---|
查询语言 | HQL | SQL |
数据存储 | HDFS | Raw Device or Local FS |
执行 | MapReduce | Excutor |
执行延迟 | 高 | 低 |
处理数据规模 | 大 | 小 |
索引 | 0.8 版本后加入位图索引 | 有复杂的索引 |
==hive 中具有 sql 数据库, 用来存储元数据信息(如: 表的属性, 数据的位置).hive 只适合用来做批量数据统计分析. 读多写少 ==
Hive 的数据存储
Hive 中所有的数据都存储在 HDFS 中, 没有专门的数据存储格式(可支持 Text,SequenceFile,ParqueFile,RCFile 等)
只需要在创建表的时候告诉 Hive 数据中的列分隔符和行分隔符. 默认列分隔符为 ascii 码的控制符 \ 001, 行分隔符为换行符.
Hive 中包含以下数据模型: DB,Table,External Table,Partition,Bucket.
db: 在 hdfs 中表现为 hive.metastore.warehouse.dir 目录下的一个文件夹
table: 在 hdfs 中表现为所属 db 目录下的一个文件夹
external table: 与 table 类似, 不过其数据存放位置可以在任意指定路径. 删除表时只会删除元数据, 不会删除实际数据
partition: 在 hdfs 中表现为 table 目录下的子目录
bucket: 在 hdfs 中表现为同一个表目录下根据 hash 散列之后的多个文件
Hive 的安装部署
安装
单机版(内置关系型数据库 derby)
元数据库 MySQL 版
这里使用常用的 MySQL 版, 使用 derby 的话不太方便, 因为 derby 会将文件保存在你当前启动的目录. 如果下次你换个目录启动, 会发现之前保存的数据不见了.
元数据库 MySQL 版安装
安装 MySQL 数据库
MySQL 安装仅供参考, 不同版本 MySQL 有各自的安装流程.
- # 删除原有的 MySQL
- rpm -qa | grep MySQL
- rpm -e MySQL-libs-5.1.66-2.el6_3.i686 --nodeps
- rpm -ivh MySQL-server-5.1.73-1.glibc23.i386.rpm
- rpm -ivh MySQL-client-5.1.73-1.glibc23.i386.rpm
- # 修改 MySQL 的密码, 并记得设置允许用户远程连接
- /usr/bin/mysql_secure_installation
- # 登录 MySQL
- MySQL -u root -p
配置 hive
配置 HIVE_HOME 环境变量
- vi conf/hive-env.sh
- # 配置其中的 $hadoop_home
配置元数据库信息
- vi hive-site.xml
- # 添加如下内容
- <configuration>
- <!-- 配置 mysql 的连接地址 -->
- <property>
- <name>javax.jdo.option.ConnectionURL</name>
- <value>jdbc:MySQL://localhost:3306/hive?createDatabaseIfNotExist=true</value>
- <description>JDBC connect string for a JDBC metastore</description>
- </property>
- <!-- 配置 mysql 的驱动 -->
- <property>
- <name>javax.jdo.option.ConnectionDriverName</name>
- <value>com.MySQL.jdbc.Driver</value>
- <description>Driver class name for a JDBC metastore</description>
- </property>
- <!-- 配置登录用户名 -->
- <property>
- <name>javax.jdo.option.ConnectionUserName</name>
- <value>root</value>
- <description>username to use against metastore database</description>
- </property>
- <!-- 配置登录密码 -->
- <property>
- <name>javax.jdo.option.ConnectionPassword</name>
- <value>root</value>
- <description>password to use against metastore database</description>
- </property>
- </configuration>
放驱动包
安装 hive 和 MySQL 完成后, 将 MySQL 的连接 jar 包拷贝到 $HIVE_HOME/lib 目录下
如果出现没有权限的问题, 在 MySQL 授权
- MySQL -uroot -p
- # 执行下面的语句 *.*: 表示所有库下的所有表 %: 任何 ip 地址或主机都可以连接
- GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENDIFIED BY 'root' WITH GRANT OPTION;
- FLUSH PRIVILEGES;
Jline 包版本不一致问题
到这一步其实已经安装好了, 但是由于 hadoop 中的 jline 包版本和我们安装 hive 的 jline 包版本不一致, 会导致 hql 无法被执行.
因此我们还要把 hive 的 lib 目录中的 jline.2.12.jar 替换掉 $HADOOP_HOME/share/hadoop/yarn/lib/jline.0.9.94.jar
启动 hive
bin/hive
登录 hive
- 1.bin/hive
- 2.bin/beeline
- !connect jdbc:hive2://server1:10000
- 3.bin/beeline -u jdbc:hive2://server1:10000 -n hadoop
创建表
创建外部表
create table tb_external(id int,name string) row format delimited fields terminated by',' location 'hdfs://kris/myhiveexternal';
在 hdfs 中已在对应路径存在文件
现在试试直接查询
== 为了保证数据的安全, 我们一般把源数据表设置为外部表. 数据只能通过外部加载导入 ==
创建带桶的表
- hive> create table student(id INT,age INT,name STRING)
- > partitioned by(stat_date STRING)
- > clustered by(id) sorted by(age) into 2 buckets
- > row format delimited fields terminated by ',';
修改表
增加分区
- alter table student add partition(stat_date='20190613') partition(stat_date='20190614');
- alter table student add partition(stat_date='20190615') location '/user/hive/warehouse/student';
删除分区
alter table student drop partition(stat_date='20190613');
创建的分区会在 hdfs 对应的路径上创建文件夹
== 如果增加的分区带了路径, 那么不会在 hdfs 的路径上显示对应的文件夹 ==
显示表分区
show partitions student;
重命名表
alter table student rename to students;
增加列
alter table students add columns(name1 string);
== 增加的列会在所有列后面, 在 partition 列前面 ==
替换所有列
alter table students replace columns(id int,age int,name string);
显示命令
- # 查看表
- show tables
- # 查看数据库
- show databases
- # 查看分区
- show partitions table_name
- # 查看方法
- show functions
- # 显示表详细信息
- desc extended table_name
- # 格式话表信息
- desc formatted table_name
加载数据
使用 load data 操作 hive 会将文件复制到表对应的 hdfs 文件夹下
加载本地数据
load data local inpath "students1.txt" [overwrite] into table students partition(stat_date="20190614");
加上 overwrite 会讲原有对应分区的数据清除.
如果目标表 (分区) 已经有一个文件, 并且文件名和 filepath 中的文件名冲突, 那么现有的文件会被新文件所替代.
导出数据
保存 select 查询结果的几种方式:
1, 将查询结果保存到一张新的 hive 表中
- create table t_tmp
- as
- select * from t_p;
2, 将查询结果保存到一张已经存在的 hive 表中
- insert into table t_tmp
- select * from t_p;
3, 将查询结果保存到指定的文件目录(可以是本地, 也可以是 hdfs)
- insert overwrite local directory '/home/hadoop/test'
- select * from t_p;
- insert overwrite directory '/aaa/test'
- select * from t_p;
分桶示例
插入分桶表的数据需要是已经分好桶的, 创建分桶的表并不会自动帮我们进行分桶.
- # 设置变量, 设置分桶为 true, 设置 reduce 数量是分桶的数量个数
- set mapreduce.job.reduces=2;
- # 或者选择以下方式
- set hive.enforce.bucketing = true;
- # 向分桶表中插入数据
- insert into student partition(stat_date='20190614')
- select id,age,name from tmp_stu where stat_date='20190614' cluster by(id);
可见在 hdfs 上根据 id 分成了两个桶
让我们看看其中一个桶的内容
注意:
==1.order by 会对输入做全局排序, 因此只有一个 reducer, 会导致当输入规模较大时, 需要较长的计算时间.==
==2.sort by 不是全局排序, 它是在数据进去 reduce task 时有序. 因此, 如果用 sort by 进行排序, 并且设置 mapreduce.job.reduces>1, 则 sort by 只保证每个 reduce task 的输出有序, 不保证全局有序.==
==3.distribute by 根据 distribute by 指定的内容将数据分到同一个 reducer==
==4.cluster by 除了具有 distribute by 的功能外, 还会对该字段进行排序. 因此我们可以这么认为 cluster by=distribute by + sort by==
== 但是 cluster by 只能指定同一字段, 当我们要对某一字段进行分桶, 又要对另一字段进行排序时, 用 distribute by + sort by 更加灵活.==
== 分桶表的作用: 最大的作用是用来提高 join 操作的效率;==
思考: select a.id,a.name,b.addr from a join b on a.id=b.id;
如果 a 表和 b 表已经是分桶表, 而且分桶的字段是 id 字段. 做这个 join 操作时, 还需要全表做笛卡尔积吗?(文末给出答案)
分桶原理
数据分桶的原理:
跟 MR 中的 HashPartitioner 的原理一模一样
MR 中: 按照 key 的 hash 值去模除以 reductTask 的个数
Hive 中: 按照分桶字段的 hash 值去模除以分桶的个数
Hive 也是 针对某一列进行桶的组织. Hive 采用对列值哈希, 然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中.
数据分桶的作用
好处:
1, 方便抽样
2, 提高 join 查询效率
如何将数据插入分桶表
将数据导入分桶表主要通过以下步骤
第一步:
从 hdfs 或本地磁盘中 load 数据, 导入中间表(也就是上文用到的 tmp_stu)
第二步:
通过从中间表查询的方式的完成数据导入
分桶的实质就是对 分桶的字段做了 hash 然后存放到对应文件中, 所以说如果原有数据没有按 key hash , 需要在插入分桶的时候 hash, 也就是说向分桶表中插入数据的时候必然要执行一次 MAPREDUCE, 这也就是分桶表的数据基本只能通过从结果集查询插入的方式进行导入
== 我们需要确保 reduce 的数量与表中的 bucket 数量一致, 为此有两种做法 ==
1. 让 hive 强制分桶, 自动按照分桶表的 bucket 进行分桶.(推荐)
set hive.enforce.bucketing = true;
2. 手动指定 reduce 数量
- set mapreduce.job.reduces = num;
- /
- set mapreduce.reduce.tasks = num;
并在 SELECT 后增加 CLUSTER BY 语句
觉得不错记得给我点赞加关注喔~
公众号: 喜讯 XiCent
来源: https://www.cnblogs.com/xicent/p/11835476.html