我们都知道hive被称为数据仓库,那么数据仓库相对于我们传统的关系型数据库(MySQL、SQL Server)有什么区别呢?
1.传统关系型数据库对实时性要求相对较高,客户端发出一条指令需要在秒级单位甚至是毫秒级单位内返回结果,二数据库仓库通常是对海量数据做分析,客户端发出一条指令一般不需要实时返回结果。
2.传统关系型数据库一般保存的是某已业务线的数据,数据仓库可以将多个数据源的数据经过统一的规则清洗之后进行集中统一管理。
3.数据库存储的数据可以修改,而数据仓库存储的数据虽然也可以修改,但是代价较大,因此数据仓库一般不会对数据做修改操作。
hive的数据是以文件的形式存在hdfs上的,同时hive的一些计算操作是需要依赖map-reduce或者是spark等计算框架,因此以下的案例是需要搭建好hadoop运行环境(包括 commodity hardware Distributed File System、yarn(计算资源管理服务)以及hive服务)才可以试验。
hive作为数据仓库在操作上和我们操作传统关心型数据库有很多类似的地方,当然它也有自己的不同之处。
比如,
数据库DDL
创建数据库的完整语法是:
CREATE (DATABASE|SCHEMA) [IF NOT EXISTS] database_name
[COMMENT database_comment]
[LOCATION hdfs_path]
[MANAGEDLOCATION hdfs_path]
[WITH DBPROPERTIES (property_name=property_value, ...)];
简单使用:
create database test;
通过show databases查看所有数据库
默认情况下是default数据库,使用use database_name可以切换数据库。
删除数据库也和传统关系型数据库用法一样,drop database database_name。
当然,已经创建好的数据库也可以进行修改,详见官网LanguageManual DDL
数据库表DDL
创建表达完整语法如下:
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name -- (Note: TEMPORARY available in Hive 0.14.0 and later)
[(col_name data_type [column_constraint_specification] [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, ...) -- (Note: Available in Hive 0.10.0 and later)]
ON ((col_value, col_value, ...), (col_value, col_value, ...), ...)
[STORED AS DIRECTORIES]
[
[ROW FORMAT row_format]
[STORED AS file_format]
| STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)] -- (Note: Available in Hive 0.6.0 and later)
]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)] -- (Note: Available in Hive 0.6.0 and later)
[AS select_statement]; -- (Note: Available in Hive 0.5.0 and later; not supported for external tables)
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
LIKE existing_table_or_view_name
[LOCATION hdfs_path];
data_type
: primitive_type
| array_type
| map_type
| struct_type
| union_type -- (Note: Available in Hive 0.7.0 and later)
primitive_type
: TINYINT
| SMALLINT
| INT
| BIGINT
| BOOLEAN
| FLOAT
| DOUBLE
| DOUBLE PRECISION -- (Note: Available in Hive 2.2.0 and later)
| STRING
| BINARY -- (Note: Available in Hive 0.8.0 and later)
| TIMESTAMP -- (Note: Available in Hive 0.8.0 and later)
| DECIMAL -- (Note: Available in Hive 0.11.0 and later)
| DECIMAL(precision, scale) -- (Note: Available in Hive 0.13.0 and later)
| DATE -- (Note: Available in Hive 0.12.0 and later)
| VARCHAR -- (Note: Available in Hive 0.12.0 and later)
| CHAR -- (Note: Available in Hive 0.13.0 and later)
array_type
: ARRAY < data_type >
map_type
: MAP < primitive_type, data_type >
struct_type
: STRUCT < col_name : data_type [COMMENT col_comment], ...>
union_type
: UNIONTYPE < data_type, data_type, ... > -- (Note: Available in Hive 0.7.0 and later)
row_format
: DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]
[NULL DEFINED AS char] -- (Note: Available in Hive 0.13 and later)
| SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]
file_format:
: SEQUENCEFILE
| TEXTFILE -- (Default, depending on hive.default.fileformat configuration)
| RCFILE -- (Note: Available in Hive 0.6.0 and later)
| ORC -- (Note: Available in Hive 0.11.0 and later)
| PARQUET -- (Note: Available in Hive 0.13.0 and later)
| AVRO -- (Note: Available in Hive 0.14.0 and later)
| JSONFILE -- (Note: Available in Hive 4.0.0 and later)
| INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname
column_constraint_specification:
: [ PRIMARY KEY|UNIQUE|NOT NULL|DEFAULT [default_value]|CHECK [check_expression] ENABLE|DISABLE NOVALIDATE RELY/NORELY ]
default_value:
: [ LITERAL|CURRENT_USER()|CURRENT_DATE()|CURRENT_TIMESTAMP()|NULL ]
constraint_specification:
: [, PRIMARY KEY (col_name, ...) DISABLE NOVALIDATE RELY/NORELY ]
[, PRIMARY KEY (col_name, ...) DISABLE NOVALIDATE RELY/NORELY ]
[, CONSTRAINT constraint_name FOREIGN KEY (col_name, ...) REFERENCES table_name(col_name, ...) DISABLE NOVALIDATE
[, CONSTRAINT constraint_name UNIQUE (col_name, ...) DISABLE NOVALIDATE RELY/NORELY ]
[, CONSTRAINT constraint_name CHECK [check_expression] ENABLE|DISABLE NOVALIDATE RELY/NORELY ]
说实话,看着这一长段有点吓人,不过我们也看到了里面有很多都是可选项,因此我们可以使用如下方式创建一个简单的表:
CREATE TABLE stu(id int,name string,age int);
我们发现没有报错,证明这张表是可以创建成功的,事实也是如此。
通过上面创建表的语法我们发现,hive不但支持了我们关系型数据支持的数据类型,同时还扩展了一些其他负载类型,比如MAP、MAP以及STRUCT等。
如果我们可以使用一些相对负载的数据结构来创建一张表
create table stu2
(
id int,
name string,
hobbies array<string>,
address map<string,string>
);
使用 desc table_name可以查看表的信息:
当然也可以添加 formatted 关键字查看表的详细信息:
再回到创建表的语法,create 后面可以添加一个关键字,external(外部的),hive创建表默认是内部表,添加了关键字external就会创建外部表。通过desc foramtted table_name可以查看表的信息,其中table type会显示是外部表还是内部表;
那么内部表和外部表有什么区别呢?
1.内部表创建的时候不需要添加特殊关键字,数据存储在hive默认的路径,外部表创建需要添加external关键字,且需要使用location关键字指定数据存储位置。
2.内部表再删除的时候会删除元数据信息和数据,外部表删除的时候只会删除元数据信息。(元数据信息可以通过配置hive时指定的数据库中TBLS表中查看)
hive默认的数据字段之间的分隔符是^A,这不符合我们平常的使用习惯,其实我们看到了建表的语法有row format关键字,因此我们可以通过它来自定义分隔符。
create table stu4
(
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 ':';
准备数据data5:
1,'zhangsan',music|dance|learn,china:shanghai
2,'lisi',math|dance,china:beijin
3,'wangwu',music|dance|learn,china:yangzhou
4,'zhaoliu',music|dance|learn,china:luoyang
导入数据:
至此就实现了自定义数据的分隔符
同传统关系型数据库一样,使用insert into table_name (column_name…) values (column_value) 可以向表中插入数据,同时我们也看到使用这方式向表中插入数据会转化成一个map-reduce任务,因此这种插入数据的方式效率很低。
同时通过select * 我们可以查询到刚刚插入的数据:
下面做一个有趣的试验:
到linux系统准备数据:
vi data
输入数据:
2^Alisi^A30
3^Awangwu^A40
4^Azhaoliu^A50
5^Aqianqi^A60
注意,其中的’A’符号是通过CTRL+V和CTRL+A来输入的,而不是通过SHIFT+数字键6输出和A的。
数据准备完毕执行命令 load data local inpath ‘/var/bigdata/data’ into table stu;
从命令的字面意思也可以看出从某个路径导入数据到某个表。
通过select查看我们发现刚才的数据真的被导入到stu表了。
继续,准备数据data2,数据如下:
10
100
1000
10000
然后重新执行上面的命令
load data local inpath ‘/var/bigdata/data2’ into table stu;
我们发现依然可以正常导入数据,没有报错,但是我刚刚明明只有一列数据,查看数据:
我们发现刚才的数据也导入进去了,只不过没有的字段是NULL。
我们知道hive的数据是存储在hdfs上面的,那么我们通过查看hdfs文件系统发现在我们指定的hive数据库/warehouse下有对应的数据,数据库下有对应的表,找到stu文件夹我们发现里面有data、data2两个文件。
准备数据data3:
22^A'小明'^A22
33^A'小红'^A33
44^A'小丽'^A44
55^A'小刘'^A55
66^A'小张'^A66
通过hdfs我们把这个文件上传到 /user/hive_remote/warehouse/stu。
查看stu数据:
我们发现刚刚上传的data3的数据也被读出来了,至此我们发现了一个现象:hive中通过selet命令读取某一张表的数据的实质是hive到hdfs的执行目录下加载目录里面的所有文件。
接着试验继续:
准备数据data4:
111
222
333
444
555
通过hdfs把文件上传到 /usr
执行命令 load data inpath ‘/usr/data4’ into table stu;
执行成功,查看数据:
导入数据成功!
至此我们发现了另一个细节:
load data [local] inpath into table table_name命令中,加local是指通过本地文件系统导入数据到hive,不加local是指从hdfs导入数据到hive。
再次查看文件系统 /usr:
我们发现原来目录下我们上传的data4文件不见了,相信大家已经猜到了,文件被移动到了hive指定表的目录下:
事实果然如此,通过load data命令导入数据到hive时,如果是从hdfs导入数据,其实质是把这个数据文件从原来的路径移动到hive对应表下的指定路径。
hive还有另外一种插入数据的方式,即通过查询语句中获取数据插入某张表。
create table stu5
(
id int,
name string
)
执行如下语句:
insert overwrite table stu5 select id,name from stu4;
同样,这条语句也会被转化为一个map-reduce任务执行。
注意,使用这种方式插入数据需要提前建好存结果的数据表,如上例的stu5。
最后,hive中是支持Update和Delete操作的,但是实际上,是需要事务的支持的,Hive对于事务的支持有很多的限制,因此,在使用hive的过程中,我们一般不会产生删除和更新的操作。