谈笑间学会大数据-Hive数据类型和文件格式

Hive数据类型和文件格式

目录

Hive数据类型和文件格式

Hive数据类型

数据类型的意义

hive基本数据类型

集合数据类型

文本文件数据编码

读时模式

hive存储格式类型


Hive数据类型

数据类型的意义

Hive支持关系型数据库中的大多数基本数据类型,同时也支持关系型数据库中很少出现的3种集合数据类型,下面我们将简短地介绍一下这样做的原因。

其中一个需要考虑的因素就是这些数据类型是如何在文本文件中进行表示的,同时还要考虑文本存储中为了解决各种性能问题以及其他问题有哪些替代方案。和大多数的数据库相比,Hive具有一个独特的功能,那就是其对于数据在文件中的编码方式具有非常大的灵活性。大多数的数据库对数据具有完全的控制,这种控制既包括对数据存储到磁盘的过程的控制,也包括对数据生命周期的控制。Hive将这些方面的控制权转交给用户,以便更加容易地使用各种各样的工具来管理和处理数据。

hive基本数据类型

Hive支持多种不同长度的整型和浮点型数据类型,支持布尔类型,也支持无长度限制的字符串类型。Hivev0.8.0版本中增加了时间戳数据类型和二进制数组数据类型。

hive基本数据类型
数据类型长度例子
tinyint1byte有符号整数20
smallint2byte有符号整数20
int4byte有符号整数20
bigint8byte有符号整数20
boolean布尔类型,true/falseTRUE
float单精度浮点数3.14159
double双精度浮点数3.14159
string字符序列,可以指定字符集。可以使用单引号或者双引号‘now’,”this“
timestamp整数,浮点数或者字符串1327882394(Unix新纪元秒),1327882394.123456789(Unix新纪元秒并跟随有纳秒数)和'2012020312:34:56.123456789'(JDBC所兼容的java.sql.Timestamp时间格式)
binary字节数组 

和其他SQL方言一样,这些都是保留字。

需要注意的是所有的这些数据类型都是对Java中的接口的实现,因此这些类型的具体行为细节和Java中对应的类型是完全一致的。例如,STRING类型实现的是Java中的String,FLOAT实现的是Java中的float,等等。

在其他SQL方言中,通常会提供限制最大长度的“字符数组”(也就是很多字符串)类型,但需要注意的是Hive中不支持这种数据类型。关系型数据库提供这个功能是出于性能优化的考虑。因为定长的记录更容易进行建立索引,数据扫描,等等。在Hive所处的“宽松”的世界里,不一定拥有数据文件但必须能够支持使用不同的文件格式,Hive根据不同字段间的分隔符来对其进行判断。同时,Hadoop和Hive强调优化磁盘的读和写的性能,而限制列的值的长度相对来说并不重要。

新增数据类型TIMESTAMP的值可以是整数,也就是距离Unix新纪元时间(1970年1月1日,午夜12点)的秒数;也可以是浮点数,即距离Unix新纪元时间的秒数,精确到纳秒(小数点后保留9位数);还可以是字符串,即JDBC所约定的时间字符串格式,格式为YYYYMMDDhh:mm:ss.fffffffff。

TIMESTAMPS表示的是UTC时间。Hive本身提供了不同时区间互相转换的内置函数,也就是to_utc_timestamp函数和from_utc_timestamp函数。

BINARY数据类型和很多关系型数据库中的VARBINARY数据类型是类似的,但其和BLOB数据类型并不相同。因为BINARY的列是存储在记录中的,而BLOB则不同。BINARY可以在记录中包含任意字节,这样可以防止Hive尝试将其作为数字,字符串等进行解析。

需要注意的是如果用户的目标是省略掉每行记录的尾部的话,那么是无需使用BINARY数据类型的。如果一个表的表结构指定的是3列,而实际数据文件每行记录包含有5个字段的话,那么在Hive中最后2列数据将会被省略掉。如果用户在查询中将一个float类型的列和一个double类型的列作对比或者将一种整型类型的值和另一种整型类型的值做对比,那么结果将会怎么样呢?Hive会隐式地将类型转换为两个整型类型中值较大的那个类型,也就是会将FLOAT类型转换为DOUBLE类型,而且如有必要,也会将任意的整型类型转换为DOUBLE类型,因此事实上是同类型之间的比较。

如果用户希望将一个字符串类型的列转换为数值呢?这种情况下用户可以显式地将一种数据类型转换为其他一种数据类型,后面会有这样的一个例子,例子中s是一个字符串类型列,其值为数值:

cast(s AS INT)
cast(s AS int)
cast(s AS STRING)

(这里需要说明的是,ASINT是关键字,因此使用小写也是可以的。)

集合数据类型

Hive中的列支持使用struct,map和array集合数据类型。下表示例中调用的是内置函数

hive集合数据类型
数据类型描述字面语法示例
struct和C语言中的struct或者“对象”类似,都可以通过“点”符号访问元素内容。例如,如果某个列的数据类型是STRUCT{firstSTRING,lastSTRING},那么第1个元素可以通过字段名.first来引用struct('tom',‘tony’)
mapMAP是一组键值对元组集合,使用数组表示法(例如[‘key’])可以访问元素。例如,如果某个列的数据类型是MAP,其中键〉值对是‘first’>‘John’和‘last’>‘Doe’,那么可以通过字段名[‘last’]获取最后1个元素map(‘first’,'second','three')
array数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,编号从零开始。例如,数组值为[‘John’,‘Doe’],那么第2个元素可以通过数组名[1]进行引用array('tom',‘tony’)

和基本数据类型一样,这些类型的名称同样是保留字。

大多数的关系型数据库并不支持这些集合数据类型,因为使用它们会趋向于破坏标准格式。例如,在传统数据模型中,structs可能需要由多个不同的表拼装而成,表间需要适当地使用外键来进行连接。

破坏标准格式所带来的一个实际问题是会增大数据冗余的风险,进而导致消耗不必要的磁盘空间,还有可能造成数据不一致,因为当数据发生改变时冗余的拷贝数据可能无法进行相应的同步。

然而,在大数据系统中,不遵循标准格式的一个好处就是可以提供更高吞吐量的数据。当处理的数据的数量级是T或者P时,以最少的“头部寻址”来从磁盘上扫描数据是非常必要的。按数据集进行封装的话可以通过减少寻址次数来提供查询的速度。而如果根据外键关系关联的话则需要进行磁盘间的寻址操作,这样会有非常高的性能消耗。

在Hive中并没有键的概念,但是用户可以对表建立索引

这里有一个用于演示如何使用这些数据类型的表结构声明语句,这是一张虚构的人力资源应用程序中的员工表:

CREATE TABLE employees(
    name            STRING,
    salary          FLOAT, 
    subordinates   ARRAY<STRING>, 
    deductions     MAP<STRING,FLOAT>, 
    address        STRUCT<street:STRING,city:STRING,state:STRING,zip:INT>
);

其中,name是一个简单的字符串;对于大多数雇员来说,salary(薪水)使用float浮点数类型来表示就已经足够了;subordinates(下属员工)列表是一个字符串值数组。在该数组中,我们可以认为name是“主键”,因此subordinates中的每一个元素都将会引用这张表中的另一条记录。对于没有下属的雇员,这个字段对应的值就是一个空的数组。在传统的模型中,将会以另外一种方式来表示这种关系,也就是雇员和雇员的经理这种对应关系。这里我们并非强调我们的模型对于Hive来说是最好的,而只是为了举例展示如何使用数组。

字段deductions是一个由键值对构成的map,其记录了每一次的扣除额,这些钱将会在发薪水的时候从员工工资中扣除掉。map中的键是扣除金额项目的名称(例如,“国家税收”),而且键可以是一个百分比值,也可以完全就是一个数值。在传统数据模型中,这个扣除额项目的名称(这里也就是map的键)可能存在于不同的表中。这些表在存放特定扣除额值的同时,还有一个外键指向对应的雇员记录。

最后,每名雇员的家庭住址使用struct数据类型存储,其中的每个域都被作了命名,并且具有一个特定的类型。

请注意后面是如何使用Java语法惯例来表示集合数据类型的。例如,MAP<STRING,FLOAT>表示map中的每个键都是STRING数据类型的,而每个值都是FLOAT数据类型的。对于ARRAY<STRING>,其中的每个条目都是STRING类型的。STRUCT可以混合多种不同的数据类型,但是STRUCT中一旦声明好结构,那么其位置就不可以再改变。

文本文件数据编码

下面我们一起来研究文件格式,首先举个最简单的例子,也就是文本格式文件。毫无疑问,用户应该很熟悉以逗号或者制表符分割的文本文件,也就是所谓的逗号分隔值(CSV)或者制表符分割值(TSV)。只要用户需要,Hive是支持这些文件格式的,在后面将会介绍其具体使用方式。然而,这两种文件格式有一个共同的缺点,那就是用户需要对文本文件中那些不需要作为分隔符处理的逗号或者制表符格外小心。也因此,Hive默认使用了几个控制字符,这些字符很少出现在字段值中。Hive使用术语field来表示替换默认分隔符的字符,下表我们将可以看到。

Hive中默认的记录和字段分隔符
分隔符描述
\n对于文本文件来说,每行都是一条记录,因此换行符可以分割记录
^A(ctrl+A)用于分隔字段(列)。在CREATE TABLE语句中可以使用八进制编码\001表示
^B用于分隔ARRARY或者STRUCT中的元素,或用于MAP中键值对之间的分隔。在CREATE TABLE语句中可以使用八进制编码\002表示
^C用于MAP中键和值之间的分隔。在CREATETABLE语句中可以使用八进制编码\003表示

前面介绍的employees表的记录看上去和下面展示的这个例子是一样的,其中使用到了^A等字符来作为字段分隔符。像Emacs这样的文本编辑器将会以这种形式来展示这些分隔符。因为页面篇幅有限,所以每行数据并非显示在同一行。为了能较清晰地展示每行记录,我们在行与行间额外增加了一个空行:

John Doe^A100000.0^AMary Smith^BTodd Jones^AFederal Taxes^C.2^BStateTaxes^C.05^BInsurance^C.1^A1Michigan Ave.^BChicago^BIL^B60600

Mary Smith^A80000.0^ABill King^AFederal Taxes^C.2^BState Taxes^C.05^BInsurance^C.1^A100 OntarioSt.^BChicago^BIL^B60601

其可读性并不好,但是当然了,我们可以使用Hive来读取这些数据。我们先来看看第1行记录来了解一下这个结构。首先,如果使用JavaScript数据交换格式(JSON)来表示这条记录的话,那么如果增加了额表结构中的字段名称的话,样式将会和如下所示的一样:

{ "name":"JohnDoe", "salary":100000.0, "subordinates":["MarySmith","ToddJones"], "deductions":{  "FederalTaxes": .2,  "StateTaxes":  .05,  "Insurance":   .1 }, "address":{  "street":"1MichiganAve.",  "city":"Chicago",  "state":"IL",  "zip":60600 }}

这里,用户可能会发现在JSON中map类型和struct类型其实是一样的。

那么,我们来看看文本文件的第1行分解出来的结果。

  • ①JohnDoe对应name字段,表示用户名。
  • ②100000.0对应salary字段,表示薪水。
  • ③Mary Smith^BTodd Jones对应subordinates字段,表示下属是“MarySmith”和“ToddJones”。
  • ④Federal Taxes^C.2^BState Taxes^C.05^BInsurance^C.1对应deductions字段,表示扣除的金额,其中20%用于上缴“国税”,5%用于上缴“州税”,还有10%用于上缴“保险费。”
  • ⑤1MichiganAve.^BChicago^BIL^B60600对应address字段,表示住址是“芝加哥第一密歇根大道60600号。”

用户可以不使用这些默认的分隔符,而指定使用其他分隔符。当有其他应用程序使用不同的规则写数据时,这是非常必要的。下面这个表结构声明和之前的那个表是一样的,不过这里明确地指定了分隔符:

CREATE TABLE employees( 
    name             STRING, 
    salary           FLOAT, 
    subordinates        ARRAY<STRING>, 
    deductions        MAP<STRING,FLOAT>, 
    address            STRUCT<street:STRING,city:STRING,state:STRING,zip:INT>
)ROW FORMAT DELIMITED FIELDS TERMINATED BY'\001'
COLLECTION ITEMS TERMINATED BY'\002'
MAP KEYS TERMINATED BY'\003'
LINES TERMINATED BY'\n'
STORED AS TEXTFILE
;
  • ROW FORMAT DELIMITED 这组关键字必须要写在其他子句(除了STORED AS...子句)之前。字符\001是^A的八进制数。ROW FORMAT DELIMITED FIELDS TERMINATEDBY '\001'这个子句表明Hive将使用^A字符作为列分割符
  • 同样地,字符\002是^B的八进制数。
  • ROW FORMAT DELIMITED COLLECTION ITEMS TERMINATED BY '\002'这个子句表明Hive将使用^B作为集合元素间的分隔符
  • 最后,字符\003是^C的八进制数。
  • ROW FORMAT DELIMITED MAP KEYS TERMINATED BY '\003'  这个子句表明Hive将使用^C作为map的键和值之间的分隔符
  • 子句LINES TERMINATED BY'…' 和STORED AS… 不需要 ROW FORMAT DELIMITED 关键字。
  • 事实上,Hive到目前为止对于LINES TERMINATED BY…仅支持字符‘\n’,也就是说行与行之间的分隔符只能为‘\n’。因此这个子句现在使用起来还是有限制的。
  • 用户可以重新指定列分割符及集合元素间分隔符,而map中键值间分隔符仍然使用默认的文本文件格式,因此子句STORED AS TEXTFILE很少被使用到。大多数情况下,我们使用的都是缺省情况下默认的TEXTFILE文件格式

因此,虽然用户可以指明但是大部分情况只需要指明修改的分隔符即可,不需要全部指定。

读时模式

当用户向传统数据库中写入数据的时候,不管是采用装载外部数据的方式,还是采用将一个查询的输出结果写入的方式,或者是使用UPDATE语句,等等,数据库对于存储都具有完全的控制力。数据库就是“守门人”。传统数据库是写时模式(schema on write),即数据在写入数据库时对模式进行检查。

Hive对底层存储并没有这样的控制。对于Hive要查询的数据,有很多种方式对其进行创建、修改,甚至损坏。因此,Hive不会在数据加载时进行验证,而是在查询时进行,也就是读时模式(schema on read)。

那么如果模式和文件内容并不匹配将会怎么样呢?Hive对此做的非常好,因为其可以读取这些数据。如果每行记录中的字段个数少于对应的模式中定义的字段个数的话,那么用户将会看到查询结果中有很多的null值。如果某些字段是数值型的,但是Hive在读取时发现存在非数值型的字符串值的话,那么对于那些字段将会返回null值。除此之外的其他情况下,Hive都极力尝试尽可能地将各种错误恢复过来。

 

hive存储格式类型

可以参考这篇博客:https://blog.csdn.net/MrZhangBaby/article/details/94163834

后续再做详细讲解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MrZhangBaby

请博主喝杯奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值