环境:CentOS7
hive-1.1.0-cdh5.14.0
hadoop-2.6.0-cdh5.14.0
Hive中数据库的概念类似于表的所在目录或者命名空间,在这一点上跟mysql有点像,database即schema,而不像Oracle,database跟schema是两个不同的粒度。因此,在hive中,schemas/schema关键字一般都可以代替databases/database。
在hive中使用use dbname显式指定数据库,如果没有显式指定,则会使用默认的数据库default,存储在default库中的表目录会直接在HDFS的根目录中生成。
创建数据库:CREATE DATABASE [IF NOT EXISTS] dbname [comment xxx] ; 如果不使用IF NOT EXISTS子句,则在创建的数据库与已有的库同名的情况下,会抛出错误,comment子句为数据库增加描述信息
查看所有数据库:SHOW DATABASES; 这里可以使用LIKE子句来进行模糊查询。
删除数据库:DROP DATABASE dbname [cascade]; 使用cascade会删除带表的数据库,否则当数据库含表时使用drop的时候会抛出错误
查看指定数据库信息:DESC DATABASES dbname;查看数据库的描述信息和所在的文件目录位置路径
另外,可以在hive-site.xml中添加hive.cli.print.current.db=true,来在提示符里显示当前所在的数据库,但是beeline的话,官方文档指出的是使用--showDbInPrompt=true参数启动也可以有这个功能,但是实际测试并没有生效,有知道的可以留言一下>,<
Hive中的表有几种,临时表,内部表,外部表,再细分有分区表,桶表。
注意:临时表只存在于当前session,且不支持索引和分区。
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name --(不加TEMPORARY/EXTERNAL的就是内部表)
[(col_name data_type [COMMENT col_comment], ... [constraint_specification])] --(列名,数据类型)
[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]
[SKEWED BY (col_name, col_name, ...) --(倾斜表,指定经常出现的值,以便应对数据倾斜时候的JOIN)
ON ((col_value, col_value, ...), (col_value, col_value, ...), ...)
[STORED AS DIRECTORIES] --(存储格式,有textfile/SEQUENCEFILE/ORC/PARQUET/JSONFILE等)
[
[ROW FORMAT row_format] --(row format指定的是SerDe,SerDe说明hive如何去处理一条记录,如果不指定或使用delimited,则使用自带SerDe)
[STORED AS file_format] --(设置表数据的存储格式)
| STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)]
]
[LOCATION hdfs_path] --(设置表目录)
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]; -- (CTAS有三个限制,目标表不能是分区表,外部表,桶表)
内部表中的数据受表定义影响,表删除后表中数据随之删除。
一般会使用外部表,这样删除表的时候只会删除metadata,数据还是会在HDFS上的,而内部表则直接都删了
Hive的一个分区名对应一个目录名,子分区名就是子目录名,并不是一个实际字段,但分区名可以作为查询的条件(where子句)而存在。
分区表有两种构造方式:静态分区和动态分区
create table student (name string,stunum int) partitioned by (sex string) row format delimited fields terminated by ',';
load data local inpath '/student_boy.txt' into table par_tab partition (sex='man');
load data local inpath '/student_girl.txt' into table par_tab partition (sex='woman');
进行动态分区前必须要指定一些参数
SET hive.exec.dynamic.partition=true; //开启动态分区功能
SET hive.exec.dynamic.partition.mode=nonstrict; //nostrict表示所有的分区字段都使用动态分区
SET hive.exec.max.dynamic.partitions.pernode = 1000; //在每个执行MR的节点上,最大可以创建多少个动态分区。
SET hive.exec.max.dynamic.partitions=1000; //最多可以创建多少个动态分区
注意:如果分区的数量大于所设定的值,是会报错的
先创建未分区的表
SELECT date,url FROM test;
2015-05-10 url1
2015-05-10 url2
2015-06-14 url1
2015-06-14 url2
2015-06-15 url1
2015-06-15 url2
再创建目标表,按日期分区存储url
CREATE TABLE test_partitioned (
url STRING
) PARTITIONED BY (date)
stored AS textfile;
注意,作为分区字段的那个字段要放在最后,如果有多个,要按次序放在select语句的最后
INSERT overwrite TABLE test_partitioned PARTITION (date)
SELECT url,date
FROM t_lxw1234;
show partitions t1; //查看t1表的所有分区
desc formatted t1 partition (date='2015-07-29'); //查看表t1的这个分区的详细信息,包括存储路径等
ALTER TABLE t1 DROP PARTITION (date='2018-07-29'); //删掉t1表的这个分区
ALTER TABLE t1 ADD PARTITION (date='2018-07-30'); //删掉t1表的这个分区
特别的,当创建了分区表,然后在HDFS中创建分区文件夹并传入数据后,select该表是看不到数据的,因为mysql中的metastore里没有该分区的数据,解决方法是使用msck repair table dept,或者是使用alter table手动添加对应分区
分区表是将大的表文件划分成多个小文件以利于查询,但是如果数据分布不均衡也会影响查询效率。桶表可以对数据进行哈希取模目的是让数据能够均匀的分布在表的各个数据文件中,它其实是对分区表的补充。以往查询时需要将数据全部加载到内存中再过滤,而分桶后则可以只将表的部分文件加载到内存中提高查询效率。桶表属于内部表,关键字clustered。
//建立桶表,这里是按照user_id分了256个桶
CREATE TABLE user_info_bucketed(user_id BIGINT, firstname STRING, lastname STRING)
COMMENT 'A bucketed copy of user_info'
PARTITIONED BY(ds STRING)
CLUSTERED BY(user_id) INTO 256 BUCKETS;
//在0.x和1.x版本中需要设置这个参数允许根据表自动选择正确数量的Reducer和cluster by列
set hive.enforce.bucketing = true;
//桶表的数据必须要从其他表导入
FROM user_id
INSERT OVERWRITE TABLE user_info_bucketed
PARTITION (ds='2009-02-25')
SELECT userid, firstname, lastname WHERE ds='2009-02-25';
总的来说,分区是粗粒度的,根据分区的key作为文件夹名来存储数据,而分桶是根据hash来对数据进行切分成多个文件
通过指定经常出现的值(严重倾斜),hive将会在元数据中记录这些倾斜的列名和值,在join时能够进行优化。若是指定了STORED AS DIRECTORIES,也就是使用列表桶(ListBucketing),hive会对倾斜的值建立子目录,查询会更加得到优化。
//倾斜表可以指定多个列
CREATE TABLE skewed_table (c1 STRING, c2 int, c3 STRING)
SKEWED BY (c1, c2) ON (('a1',21), ('a2',22), ('a3',13));
//改变倾斜表倾斜的key
ALTER TABLE skewed_table SKEWED BY (c1, c2) ON ('a100', '100') ;
//关闭倾斜,使倾斜表变回普通表
ALTER TABLE table_name NOT SKEWED;
//关闭listbucketing
ALTER TABLE t1 NOT STORED AS DIRECTORIES;
//修改list bucketing倾斜值的存储位置映射。
ALTER TABLE table_name SET SKEWED LOCATION (col_name1="location1" [, col_name2="location2", ...] );
视图可以理解为一组查询语句的结果集,基于库中已经存在的表。视图是只读的,不能用作LOAD / INSERT / ALTER的目标。
//创建视图
CREATE VIEW [IF NOT EXISTS] [db_name.]view_name [(column_name [COMMENT column_comment], ...) ]
[COMMENT view_comment]
[TBLPROPERTIES (property_name = property_value, ...)]
AS SELECT ...;
//删除视图
DROP VIEW [IF EXISTS] [db_name.]view_name;
Hive里的索引不像传统事务型数据库上的索引一样,建立在表上面,而是单独的会生成一个索引表,用show tables也可以看见索引的存在。索引可以避免全表扫描和资源浪费,还可以加快含有group by语句的查询的计算速度。
需要注意的是,如果表的数据发生了改变,则需要alter index rebuild重建索引
索引分两种,压缩索引(Compact)和位图索引(Bitmap)
压缩索引是将列中相同的值的字段进行压缩,而位图索引我觉得可以用一个例子来说明
比如Sales表分为四个维度
SHOP 有4个商店
PRODUCT 有200种商品
DATE 有365天
CHANNEL 有两个渠道,自购(walk-in)和递送(delivery)
CHANNEL索引的位图如下
WALK-IN 11010111000101011100010101...
DELIVERY 00101000111010100010100010...
这指示前两行销售给自购用户,第三次销售是自购,以此类推
SHOP索引的位图如下
LONDON 11001001001001101000010000....
OXFORD 00100010010000010001001000....
READING 00010000000100000100100010....
GLASGOW 00000100100001000001000101....
如果执行以下查询:
SELECT COUNT(*) FROM sales WHERE channel='WALK-IN' AND shop='OXFORD';
Oracle会检索两个位图,并执行与操作
WALK-IN 11010111000101011100010101....
OXFORD 00100010010000010001001000....
与后 00000010000000010000000000....
位图的合并速度极快,可使用AND,OR,NOT操作符的任意组合,实现复杂的布尔操作,而且可以包含NULL
一般在以下条件使用位图索引:
1.列的基数(不同值的个数)小
2.表中的行数多
3.列用于布尔运算
//创建压缩索引,使用with defferred rebuild子句则会先创建一个空索引
create index student_index on table student(id) as 'Compact' with deferred rebuild;
//重建索引,填充索引数据
alter index student_index on student rebuild;
//查看索引信息,可以从中获取到索引的存储地址
desc formatted default__student_student_index__;
如Location: hdfs://centos:9000/usr/hive/warehouse/default__student_student_index__
//用hdfs的cat命令查看的话,可以发现里面记录的是索引列的值,值对应的HDFS文件存储地址以及值在文件中的偏移量
1hdfs://centos:9000/student/student.txt0
2hdfs://centos:9000/student/student.txt14
3hdfs://centos:9000/student/student.txt30
4hdfs://centos:9000/student/student.txt49
//创建位图索引也是差不多的
create index student_index_bitmap on table student(id) as 'BITMAP' with deferred rebuild;
alter index student_index_bitmap on student rebuild;
在执行索引字段查询时候,首先额外生成一个MR job,根据对索引列的过滤条件,从索引表中过滤出索引列的值对应的hdfs文件路径及偏移量,输出到hdfs上的一个文件中,然后根据这些文件中的hdfs路径和偏移量,筛选原始input文件,生成新的split,作为整个job的split,这样就达到不用全表扫描的目的。