Hive是将一个现有的数据基础架构转移到Hadoop上,而这个基础架构是基于传统关系型数据库和结构化查询语句的(SQL)。
Hadoop对Hive的限制就是Hive不支持记录级别的更新、插入或删除操作。但用户可通过查询生成新表或将查询结果导入到文件中。
Hadoop是一个面向批处理的系统,而MapReduce任务的启动过程需要消耗较长的时间,所以Hive查询延时比较严重。
Hive不支持事务,不支持OLTP(联机事务处理)所需的关键功能,而更接近成为一个OLAP(联机分析技术)工具。
Hive所有的命令和查询都会进入Driver,通过改模块对输入进行解析编译,对需求的计算进行优化,然后按照指定的步骤执行。Hive本身不会生成MapReduce程序,通过执行计划的XML文件驱动执行内置的、原生的Mapper和Reducer模块。
metastore是一个独立的关系型数据库,Hive会在其中保存表模式和其他系统元数据信息。通常情况下会使用一个关系型数据库中的表来保存这些信息。元数据存储了如表的模式和分区信息等元数据信息。
--define key = value
可以定义用户自定义变量,Hive会将这些键值对放到hivevar命名空间,这样可以和其他3种命名空间区分。
在CLI中,使用set命令显示或修改变量值。
和hivevar变量不同,用户必须使用system:或env:前缀来指定系统属性和环境变量。
临时应急时可以将查询结果保存到一个文件中
hive -S -e "select * from table limit 3 > /tmp/myquery"
Hive中可使用-f执行指定文件中的一个或多个查询语句。
hive -f /path/file/queries.hql
cli -i 允许用户指定一个文件,当CLI启动时,在提示符出现前会先执行这个文件,需要频繁执行的命令,使用这个文件会很方便。
用户可以在Hive CLI中执行hdfs指令,只需将hadoop命令中的关键字hadoop去掉,然后以分号结尾。这种方式比在shell中执行命令更高效。后者每次都会启动一个新的JVM实例,Hive会在同一个进程中执行这些命令。
让CLI打印出字段名称,可通过设置hiveconf配置项hive.cli.print.header=true来开启功能。
Hive中所有数据类型都是对Java中的接口的实现。
Hive中的列支持使用struct,map,array集合数据类型。大多数关系型数据库并不支持这些集合数据类型,因为使用它们会趋向于破坏标准格式。
破坏标准格式带来的实际问题是会增大数据冗余的风险, 进而导致消耗不必要的磁盘空间,还有可能造成数据不一致,只有当数据发生改变时冗余的拷贝数据可能无法进行相应的同步。
不遵循标准格式的一个好处就是可提供更高吞吐量的数据。以头部寻址来从磁盘扫描数据是非常必要的,外键关系关联需要进行磁盘间的寻址操作,会有非常高的性能消耗。
row format delimited必须写在其他子句前。
传统的数据库是写时模式,数据在写入数据库时对模式进行检查。
Hive不会在数据加载时进行验证,而是在查询时进行,也就是读时模式。
如果每行记录中的字段个数少于对应的模式中定义的字段个数,用户将会看到查询结果中有很多null值。如果某些字段是数值型的,但是Hive在读取时发现存在非数值型的字符串值的话,对于那些字段将会返回null值。
Hive中数据库的概念本质上仅仅是表的一个目录或命名空间。
Hive会为每个数据库创建一个目录,数据库中的表将会以这个数据库目录的子目录形式存储。
Hive是不允许用户删除一个包含有表的数据库的。用户要么先删除数据库中的表,然后再删除数据库;要么在删除命令的最后面加上关键字cascade,这样可以使hive自行先删除数据库中的表
drop database if exists d cascade;
如果不想使用默认的表路径,那么最好使用外部表。
管理表不方便和其他工作共享数据,可以创建一个外部表指向这份数据,而不需要对其具有所有权。
外部表Hive并非认为其完全拥有这份数据,删除表并不会删除掉这份数据,但描述表的元数据信息会被删除掉。
对数据进行分区,最重要的原因是为了更快地查询,
如果表中的数据及分区个数都非常大,执行一个包含有所有分区的查询会触发一个巨大的MapReduce任务。一个高度建议的安全措施是将Hive设置为严格模式,这样如果对分区表进行查询而where子句没有加分区过滤,会禁止提交。
textfile意味着每一行是一个单独的记录。sequencefile和rcfile是使用二进制编码和压缩来优化磁盘空间使用以及I/O带宽性能的。
大多数表的属性可通过alter table来修改,这种操作会修改元数据,但不会修改数据本身。
load data local inpath 'path'
overwrite into table employees
partition (country='US', state='CA');
如果目录不存在的话,这个命令会先创建分区目录,然后再将数据拷贝到该目录下。
如果用户指定了overwirte,目标文件夹中之前存在的数据将会被删除掉。如果没有这个关键字,仅仅会把新增的文件增加到目标文件夹中而不会删除之前的数据。如果目标文件夹中已经存在和装载的文件同名的文件,那么旧的文件将会被覆盖重写。
通过查询语句向目标表中插入数据
insert overwrite table employees
partition (country='US', state='OR')
select * from staged_employees se
where se.cnty='US' and se.st='OR';
若没有使用overwrite,hive会以追加的方式写入数据而不会覆盖掉之前的内容。
常用的场景:数据已经存在于某个目录下,对Hive来说是外部表,现在想将其导入到最终的分区表,如果用户想将源表导入到一个具有不同记录格式的目标表中的话,这种方式是很好的。
如果表非常大,分区比较多,需要扫描多次。
一次扫描输入数据,按多种方式划分
from staged_employees se
insert overwirte table employees
partition (country='US', state='OR')
select * where se.cnty='US' and se.st='OR'
insert overwirte table employees
partition (country='US', state='CA')
select * where se.cnty='US' and se.st='CA'
insert overwirte table employees
partition (country='US', state='IL')
select * where se.cnty='US' and se.st='IL';
静态分区和动态分区混合使用时,静态分区键必须出现在动态分区键之前。
单个查询语句中创建表并加载数据
create table ca_employees
as select name, salary, address
from employees
where se.state='CA';
这个功能不能用于外部表,因为这里本身没有进行数据装载,而是将元数据中指定一个指向数据的路径。
与聚合函数相反的是表生成函数,可以将单列扩展成多列或多行。
当使用表生成函数时,hive要求使用列别名。
Hive对某些情况的查询可以不必触发MR,即本地模式。
select * from employee;
这种情况下,Hive可以简单读取表对应的存储目录下的文件,然后输出格式化后的内容到控制台。
对于where语句中过滤条件只是分区字段这种情况,也无需MR
select * from employee
where country='US' and state='CA';
where语句中不能使用列别名,但是可以使用一个嵌套的select
关于浮点数的比较
当用户写一个浮点数,比如0.2,Hive会将值保存成double。如果表中定义的是float,Hive将隐式地将值转换成double再比较。但是这样是行不通的。
0.2不能使用float或double进行准确表示。0.2的最近似的精确值应略大于0.2,也就是0.2后面若干个0后存在非零的数值。
可以说0.2对于float是0.2000001,对于double搜0.20000000001.
隐式转换比较过程中,Hive有两种规避方法。
如果是从textfile文本文件读取数据,hive对读取字符串0.2然后转换为一个数字。这种变化会增加查询时所需的内存消耗。如果存储格式是二进制文件格式,就不能这样改变。
第二个规模方案就是显示指出0.2为float。
第3中,和钱相关的都避免使用浮点数。
Hive支持join,但是只支持等值连接。
当对多个表进行join连接时,如果每个on子句使用相同的连接键,只会产生一个MR。
Hive同时假定查询最后一个表示最大的表,对每行记录进行连接操作时,会尝试将其他表缓存起来,扫描最后那个表计算。用户需要保证连续查询中的表大小从左到右依次增加。
左半开连接(left semi-join)会返回左边表的记录,前提是其记录对于右边表满足on语句中的判定条件。相当于是in exists。
semi-join比通常的inner join更高效,对于左表中一条指定的记录,在右边表中一旦找到匹配的记录,Hive就会立即停止扫描。
如果所有表中只有一张表是小表,可以在最大的表通过mapper的时候将小标完全放到内存中。Hive可在map端执行连接过程,因为Hive可以和内存中的小表逐一匹配,从而省略掉常规连接操作需要的reduce过程。
用户设置hive.auto.convert.JOIN=true,才有必要启动这个优化。
order by会对查询结果执行全局排序,也就是会有一个所有的数据通过一个reducer进行处理的过程。
sort by会在每个reducer中对数据进行排序,也就是执行一个局部排序过程。
distribute by控制map的输出在reducer中是如何划分的。
假设希望具有相同股票交易码的数据在一起处理,可以使用distribute by保证具有相同股票交易码的记录会分发到同一个reducer中进行处理,然后使用sort by按照我们的期望对数据排序。
需要注意,Hive要求distributed by要写在sort by之前。
如果sort by和distributed by后的列完全相同,可以简写为cluster by
强制类型转换,cast。将浮点数转换成整数推荐使用round或floor。
对于非常大的数据集,有时用户需要使用的是一个具有代表性的查询结果而不是全部结果。Hive可以通过对表进行分桶抽样来满足这个需求。
select * from numbers tablesample(bucket 5 out of 10 on number) s;
分桶语句的分母表示的是数据将会被散列的通的个数,分子表示将会选择的桶的个数。
按百分比抽样
select * from numbersflat tablesample(0.1 percent) s;
这种抽样的最小单元是一个HDFS数据块。