Hive学习笔记

因为工作中用到了通过Hive从公司数据仓库拉取数据,而之前也没有接触过大数据相关知识,了解了一下Hadoop相关原理,并没有实际使用Hadoop做过什么,或者实现Hive直接功能实现,所以这篇总结可能也很皮毛

Hadoop是什么

自己了解Hive之前先读了一遍《Hadoop权威指南》,里面的基本下载启动是照着做了,不过有的代码编写没做,Hadoop的总结写到这里吧,毕竟刚入门,能总结的东西也不是很多很深入。

hadoop架构

我们遇到的问题很简单:在硬盘存储容量多年不断提升的同时,访问速度(硬盘数据读取速度)却没有与时俱进。1990年,一个普通硬盘可以存储1370MB的数据,传输速度为4.4MB/S,因此只需要5分钟就可以读完整个硬盘中的数据。20年过了,1TB的硬盘已然成为主流,氮气数据传输速度为100MB/S,读完整个硬盘中的数据至少得花2.5个小时。

读完整个硬盘中的数据需要更长时间。写入数据就更别提了。一个很简单的,减少读取时间的办法是同时从多个硬盘上读数据。试想,如果我们有100个硬盘。每个硬盘存储1%的数据,并行读取,那么不到两分钟就可以读完所有数据。

仅使用硬盘容量的1%似乎很浪费,但是我们可以存储100个数据集,每个数据集1TB。并实现共享硬盘的读取。可以想象,用户肯定很乐于通过硬盘共享来缩短数据分析时间;并且,从统计角度来看,用户的分析工作都是在不同时间点进行的,所以彼此之间的干扰并不大。

虽然如此,但要对多个硬盘中的数据并行进行读写数据,还有更多问题要解决。第一个需要解决的是硬件故障问题,一旦开始使用多个硬件,其中个别硬件就很有可能发生故障。为了避免数据丢失,最常用的做法是复制:系统保存数据的复本。一旦有系统发生故障,就可以使用另外保存的复本。例如,冗余硬盘阵列(RAID)就是按这个原理实现的,另外,Hadoop文件系统(HDFS,Hadoop
Distributed[分布式的] File System)也是一类,不过它采用的方法稍有不同,这个在后面会详细说明。

第二个问题是大多数分析任务需要以某种方式结合大部分数据来共同完成分析,即从一个硬盘读取的数据可能需要与从另外99个硬盘中读取的数据结合使用,各种分布式系统允许结合不同来源的数据进行分析,但保证其正确性是一个非常大的挑战。MapReduce提出一个编程模型,该模型抽象出这些硬盘读写问题并将其转化为对一个数据集(由键值对组成)的计算。后面会详细讨论这个模型,这样的计算由map和reduce两部分组成,而且只有这两部分提供对外的接口。与HDFS类似,MapReduce自身也有很高的可靠性。

简而言之,Hadoop为我们提供了一个可靠的共享存储和分析系统,HDFS实现数据的存储,MapReduce实现数据的分析和处理。虽然Hadoop还有其他功能,但HDFS和MapReduce是它的核心价值。

MapReduce看似采用一种蛮力方法。每个查询需要处理整个数据集或至少一个数据集的绝大部分。但反过来想,这也正是它的能力。MapReduce是一个批量查询处理器,能够在合理的时间范围内处理针对整个数据集的动态查询【这里指查询的内容是动态的,Hadoop并不能处理动态的数据,处理动态的数据用Spark】。它改变了我们队数据的传统看法,解放了以前只是保存在磁带和硬盘上的数据。它让我们有机会对数据进行创新。以前需要很长时间处理才能获得结果的问题,到现在变得顷刻之间就迎刃而解,同时还可以引发新的问题和新的见解。

1.3.1关系型数据库管理系统(简称RDBMS r=relationnal关系 db=database数据库 m=management管理
s=system系统)

为什么不能用数据库来对大量硬盘上的大规模数据进行批量分析呢?我们为什么需要MapReduce?

这两个问题的答案来自于计算机硬盘的另一个发展趋势,寻址时间的提升远远不低敌于传输速率的提升。寻址是将磁头移动到特定硬盘位置进行读写操作的过程。它是导致磁盘操作延迟的主要原因,而传输速率取决于硬盘的贷款。

如果数据访问模式中包含大量的硬盘寻址,那么读取大量数据集j就必然会花更长的时间(相较于流数据读取模式,流读取主要取决于传输速率)。另一方面,如果数据库系统只更新一小部分记录,那么传统的B树就更有优势(关系型数据库中使用的一种数据结构,受限于寻址的比例)。但数据库系统如果有大量数据更新时,B树的效率就明显落后于MapReduce,因为需要使用“排序/合并”(sort/merge:合并)来重建数据库。

在许多情况下,可以将MapReduce视为关系型数据库管理系统的补充。两个系统之间的差异如表:
关系型数据库和MapReduce差异

MapReduce比较适合批处理方式处理需要分析整个数据集的问题,尤其是动态分析。RDBMS适用于点查询和更新,数据集被索引之后,数据库系统能够提供延迟的数据检索和快速的少量数据更新。MapReduce适合一次写入、多次读取数据的应用,关系型数据库则更适合持续更新的数据集。

MapReduce和关系型数据库之间的另一个区别在于它们所操作的数据集的机构化程度。结构化程度是具有既定格式的实体化数据,如XML文档或满足特定格式的数据库表。这是RDBMS包括的内容。另一方面,半结构化数据比较松散,虽然可能有格式,但经常被忽略,所以它只能作为对数据结构的一般性指导。例如电子表格,它在结构上是由单元格组成的网格,但是每个单元格内可以保证任何形式的数据。非结构化数据没有什么特别的内部结构,例如纯文本或图像数据。MapReduce对非结构化或半结构化数据非常有效,因为它是在处理数据时才对数据进行解释。换句话说,MapReduce输入的键和值并不是数据固有的属性,而是分析数据的人来选的。

关系型数据往往是规范的,以保证其数据的完整性且不含冗余。规范给MapReduce带来了问题,因为它使记录读取成为非本地操作,而MapReduce的核心假设之一偏偏就是可以进行(高速的)流读写操作。

Web服务器日志是典型非规范化数据记录(例如,每次都需要记录客户端主机全名,只会导致同一客户端的全名多次出现),这也是MapReduce非常适用于分析各种日志文件的原因之一。

MapReduce是一种线性的可伸缩编程模型。程序员要写两个函数,分别为map函数和reduce函数,每个函数定义从一个键值对集合到另一个键值对集合的映射,这些函数不必关注数据集及其所用集群的大小,可以原封不动地应用于小规模数据集或大规模的数据集。更重要的是,如果输入的数据量是原来的两倍,那么运行的时间也需要两倍,但如果集群是原来的两倍,作业的运行速度却仍然与原来一样快。SQL查询一般不具备该特性。

但是,在不久的将来,关系型数据库和MapReduce系统之间的差异很可能变得模糊。关系型数据库都开始吸收MapReduce的一些思路m另一方面,基于MapReduce的高级查询语言使传统数据库的程序员更容易接受MapReduce系统。

MapReduce

MapReduce是一种 适合处理大量数据的编程模型
。Hadoop能够运行用各种语言编写的MapReduce程序:Java,Ruby,Python和C++。 MapReduce程序本质上是并行的
,因此对于使用群集中的多台机器执行大规模数据分析非常有用。

处理流程

MapReduce 处理数据过程主要分成 MapReduce 两个阶段。首先执行 Map 阶段,再执行 Reduce 阶段。Map 和
Reduce 的处理逻辑由用户自定义实现,但要符合 MapReduce 框架的约定。处理流程如下所示:

  1. 在正式执行 Map 前,需要将输入数据进行 分片 。所谓分片,就是将输入数据切分为大小相等的数据块,每一块作为单个 Map Task 的输入被处理,以便于多个 Map Task 同时工作。
  2. 分片完毕后,多个 Map Task 便可同时工作。每个 Map Task 在读入各自的数据后,进行计算处理,最终输出给 Reduce。Map Task 在输出数据时,需要为每一条输出数据指定一个 Key,这个 Key 值决定了这条数据将会被发送给哪一个 Reduce Task。 Key 值和 Reduce Task 是多对一的关系 ,具有相同 Key 的数据会被发送给同一个 Reduce Task,单个 Reduce Task 有可能会接收到多个 Key 值的数据。
  3. 在进入 Reduce 阶段之前,MapReduce 框架会对数据按照 Key 值 排序 ,使得具有相同 Key 的数据彼此相邻。如果指定了 合并操作(Combiner) ,框架会调用 Combiner,它负责对中间过程的输出具有相同 Key 的数据进行本地的聚集,这会有助于降低从Mapper到 Reducer数据传输量。Combiner 的逻辑可以自定义实现。这部分的处理通常也叫做 洗牌(Shuffle)
  4. 接下来进入 Reduce 阶段。相同 Key 的数据会到达同一个 Reduce Task。同一个 Reduce Task 会接收来自多个 Map Task 的数据。每个 Reduce Task 会对 Key 相同的多个数据进行 Reduce 操作。最后,一个 Key 的多条数据经过 Reduce 的作用后,将变成一个值。

Map任务处理

reduce任务处理

MapReduce运行过程的深入了解

从前面的WordCount可以看出, 一个MapReduce作业经过了input,map,combine,reduce,output
五个阶段,其中combine阶段并不一定发生, map输出的中间结果被分发到reducer的过程被称为shuffle (数据混洗)。

从输入到输出的状态

Mapreduce运行过程

在shuffle阶段还会发生copy(复制)和sort(排序)。
在MapReduce
的过程中,一个作业被分成Map和Reduce计算两个阶段,它们分别由两个或者多个Map任务和Reduce任务组成,这个在前面已经说过了。Reduce任务默认会在Map任务数量完成5%后才开始启动。

Map任务的执行过程可概括为:

  1. 首先通过用户指定的Inputformat类中的getSplits方法和next方法将输入文件切片并解析成键值对作为map函数的输入。
  2. 然后map函数经过处理之后输出并将中间结果交给指定的Partitioner处理,确保中间结果分发到指定的Reduce任务处理,此时如果用户指定了Combiner,将执行combine操作。
  3. 最后map函数将中间结果保存到本地。

Reduce 任务的执行过程可概括为:

首先需要将已经完成的Map任务的中问结果复制到Reduce任务所在的节点,待数据复制完成后,再以key进行排序,通过排序,将所有key相同的数据交给reduce函数处理,处理完成后,结果直接输出到HDFS上。


。。。这里更详细的有看到,但是没仔细看,又到了源码部分,暂时先了解这么多。


mapReduce的局限性

  1. 从MapReduce 的特点可以看出MapReduce 的优点非常明显,但是MapReduce 也有其局限性,井不是处理海量数据的普适方法。它的局限性主要体现在以下几点:
    MapReduce
    的执行速度慢。一个普通的MapReduce作业一般在分钟级别完成,复杂的作业或者数据量更大的情况下,也可能花费一小时或者更多,好在离线计算对于时间远没有OLTP那么敏感。所以MapReduce
    现在不是,以后也不会是关系型数据库的终结者。MapReduce的慢主要是由于磁盘I/0 , MapReduce
    作业通常都是数据密集型作业,大量的中间结果需要写到磁盘上并通过网络进行传输,这耗去了大量的时间。

  2. MapReduce过于底层。与SQL相比,MapReduce显得过于底层。对于普通的查询,一般人是不会希望写一个map 函数和reduce函数的。对于习惯于关系型数据库的用户,或者数据分析师来说,编写map 函数和reduce 函数无疑是一件头疼的事情。好在Hive的出现,大大改善了这种状况。

  3. 不是所有算法都能用MapReduce 实现。这意味着,不是所有算法都能实现并行。例如机器学习的模型训练, 这些算法需要状态共享或者参数间有依赖,且需要集中维护和更新。

HDFS

HDFS是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超大文件的需求而开发的,可以运行于廉价的商用服务器上。它所具有的高容错、高可靠性、高可扩展性、高获得性、高吞吐率等特征为海量数据提供了不怕故障的存储,为超大数据集(Large
Data Set)的应用处理带来了很多便利。

这里重点介绍其中涉及到的几个概念:(1) 超大文件 。目前的hadoop集群能够存储几百TB甚至PB级的数据。(2) 流式数据访问
。HDFS的访问模式是: 一次写入,多次读取 ,更加关注的是读取整个数据集的整体时间。(3) 商用硬件。
HDFS集群的设备不需要多么昂贵和特殊,只要是一些日常使用的普通硬件即可,正因为如此,hdfs节点故障的可能性还是很高的,所以
必须要有机制来处理这种单点故障 ,保证数据的可靠。(4) 不支持低时间延迟的数据访问
。hdfs关心的是高数据吞吐量,不适合那些要求低时间延迟数据访问的应用。(5) 单用户写入,不支持任意修改。
hdfs的数据以读为主,只支持单个写入者,并且写操作总是以添加的形式在文末追加,不支持在任意位置进行修改。

HDFS数据块

每个磁盘都有默认的数据块大小,这是文件系统进行数据读写的最小单位。这涉及到磁盘的相应知识,这里我们不多讲,后面整理一篇博客来记录一下磁盘的相应知识。

HDFS同样也有数据块的概念,默认一个块(block)的大小为128MB(HDFS的块这么大主要是为了最小化寻址开销),要在HDFS中存储的文件可以划分为多个分块,每个分块可以成为一个独立的存储单元。与本地磁盘不同的是,HDFS中小于一个块大小的文件并不会占据整个HDFS数据块。

对HDFS存储进行分块有很多好处:

  • 一个文件的大小可以大于网络中任意一个磁盘的容量,文件的块可以利用集群中的任意一个磁盘进行存储。
  • 使用抽象的块,而不是整个文件作为存储单元,可以简化存储管理,使得文件的元数据可以单独管理。
  • 冗余备份。数据块非常适合用于数据备份,进而可以提供数据容错能力和提高可用性。每个块可以有多个备份(默认为三个),分别保存到相互独立的机器上去,这样就可以保证单点故障不会导致数据丢失。

namenode和datanode

HDFS集群的节点分为两类:namenode和datanode,以管理节点-
工作节点的模式运行,即一个namenode和多个datanode,理解这两类节点对理解HDFS工作机制非常重要。

namenode作为管理节点,它负责整个文件系统的命名空间,并且维护着文件系统树和整棵树内所有的文件和目录,这些信息以两个文件的形式(命名空间镜像文件和编辑日志文件)永久存储在namenode
的本地磁盘上。除此之外,同时,namenode也记录每个文件中各个块所在的数据节点信息,但是不永久存储块的位置信息,因为块的信息可以在系统启动时重新构建。

datanode作为文件系统的工作节点,根据需要存储并检索数据块,定期向namenode发送他们所存储的块的列表。

nameNode和dateNode

由此可见,namenode作为管理节点,它的地位是非同寻常的,一旦namenode宕机,那么所有文件都会丢失,因为namenode是唯一存储了元数据、文件与数据块之间对应关系的节点,所有文件信息都保存在这里,namenode毁坏后无法重建文件。因此,必须高度重视namenode的容错性。

为了使得namenode更加可靠,hadoop提供了两种机制:

  • 第一种机制是备份那些组成文件系统元数据持久状态的文件,比如:将文件系统的信息写入本地磁盘的同时,也写入一个远程挂载的网络文件系统(NFS),这些写操作实时同步并且保证原子性。

  • 第二种机制是运行一个辅助namenode,用以保存命名空间镜像的副本,在namenode发生故障时启用。(也可以使用热备份namenode代替辅助namenode)。

nameNode和dataNode

块缓存

数据通常情况下都保存在磁盘,但是对于访问频繁的文件,其对应的数据块可能被显式的缓存到datanode的内存中,以堆外缓存的方式存在,一些计算任务(比如mapreduce)可以在缓存了数据的datanode上运行,利用块的缓存优势提高读操作的性能。

联邦HDFS

namenode在内存中保存了文件系统中每个文件和每个数据块的引用关系,这意味着,当文件足够多时,namenode的内存将成为限制系统横向扩展的瓶颈。hadoop2.0引入了联邦HDFS允许系统通过添加namenode的方式实现扩展,每个namenode管理文件系统命名空间中的一部分,比如:一个namenode管理/usr下的文件,另外一个namenode管理/share目录下的文件。

HDFS的高可用性

通过备份namenode存储的文件信息或者运行辅助namenode可以防止数据丢失,但是依旧没有保证了系统的高可用性。一旦namenode发生了单点失效,那么必须能够快速的启动一个拥有文件系统信息副本的新namenode,而这个过程需要以下几步:(1)将命名空间的副本映像导入内存
(2)重新编辑日志 (3)接收足够多来自datanode的数据块报告,从而重建起数据块与位置的对应关系。

上述实际上就是一个namenode的冷启动过程,但是在数据量足够大的情况下,这个冷启动可能需要30分钟以上的时间,这是无法忍受的。

Hadoop2.0开始,增加了对高可用性的支持。采用了双机热备份的方式。同时使用一对活动-
备用namenode,当活动namenode失效后,备用namenode可以迅速接管它的任务,这中间不会有任何的中断,以至于使得用户根本无法察觉。

为了实现这种双机热备份,HDFS架构需要作出以下几个改变:

  • 两个namenode之间要通过高可用共享存储来实现编辑日志的共享
  • datanode要同时向两个namenode发送数据块的报告信息
  • 客户端要使用特定机制来处理namenode的失效问题
  • 备用namenode要为活动namenode设置周期性的检查点,从中判断活动namenode是否失效

HDFS系统中运行着一个故障转移控制器,管理着将活动namenode转移为备用namenode的转换过程。同时,每一个namenode也运行着一个轻量级的故障转移控制器,主要目的就是监视宿主namenode是否失效,并在失效时实现迅速切换。

Hive

传统mysql无法处理大数据,而大数据文件系统HDFS不能使用SQL,Hive就是一种可以用类SQL语句对大数据文件系统中的结构化数据进行操作的工具

Hive的系统架构

hive的系统架构

  • 用户接口,包括CLI,JDBC/ODBC,WebUI
  • metastore,Hive将元数据存储在数据库中(metastore),目前只支持 mysql、derby(Derby引擎的缺点:一次只能打开一个会话。使用Mysql作为外置存储引擎,多用户同时访问)。Hive 中的元数据包括表名、列、分区及其属性、表的属性(是否为外部表等)、表数据所在目录等
  • Driver 解释器、编译器、优化器完成 HQL 查询语句从词法分析、语法分析、编译、优化以及查询计划(plan)的生成。生成的查询计划存储在 HDFS 中,并在随后有 MapReduce 调用执行
  • Hive的数据存储在HDFS中,大部分的查询由MapReduce完成(包含 * 的查询,比如select * from tb不会生成MapReduce任务)

Hive和普通数据库的异同

  1. 查询语言 。由于SQL被广泛的应用在数据仓库中,因此,专门针对Hive的特性设计了类SQL的查询语言HQL。熟悉SQL开发就很容易入手Hive开发。
  2. 数据仓库位置 。Hive是建立在Hadoop之上的,所有Hive的数据都是存储在HDFS中的。而数据库则可以将数据保存在块设备或者本地文件系统中。
  3. 数据格式 。 Hive中没有定义专门的数据格式,数据格式可以由用户指定,用户定义数据格式需要指定三个属性,列分隔符(通常为空格)、行分隔符(“\n”)以及读取文件数据的方法(Hive中默认有三个文件格式TextFile,SequenceFile以及RCFile)。由于在加载数据的过程中,不需要从用户数据格式到Hive定义的数据格式的转换,因此,Hive在加载的过程中不会对数据本身进行任何修改,而只是将数据内容复制或者移动到相应的HDFS目录中。
  4. 数据更新 。由于Hive是针对数据仓库应用设计的,而数据仓库的内容是读多写少的。因此, Hive中不支持对数据的改写和添加 ,所有的数据都是在加载的时候中确定好的。而数据库中的数据通常是需要经常进行修改的,因此可以使用 INSERT INTO … VALUES 添加数据,使用 UPDATE … SET修改数据。
  5. 索引 。Hive在加载数据的过程中不会对数据进行任何处理,甚至不会对数据进行扫描,因此也没有对数据中的某些Key建立索引。Hive要访问数据中满足条件的特定值时,需要暴力扫描整个数据,因此访问延迟较高。由于 MapReduce 的引入, Hive 可以并行访问数据,因此即使没有索引,对于大数据量的访问,Hive 仍然可以体现出优势。数据库中,通常会针对一个或者几个列建立索引,因此对于少量的特定条件的数据的访问,数据库可以有很高的效率,较低的延迟。由于数据的访问延迟较高,决定了 Hive 不适合在线数据查询。
  6. 执行 。Hive中大多数查询的执行是通过 Hadoop 提供的 MapReduce 来实现的(类似 select * from tbl的查询不需要MapReduce)。而数据库通常有自己的执行引擎。
  7. 执行延迟 。Hive 在查询数据的时候,由于没有索引,需要扫描整个表,因此延迟较高。另外一个导致 Hive 执行延迟高的因素是 MapReduce框架。由于MapReduce 本身具有较高的延迟,因此在利用MapReduce 执行Hive查询时,也会有较高的延迟。相对的,数据库的执行延迟较低。当然,这个低是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时 候,Hive的并行计算显然能体现出优势。
  8. 可扩展性 。由于Hive是建立在Hadoop之上的,因此Hive的可扩展性是和Hadoop的可扩展性是一致的。而数据库由于 ACID 语义的严格限制,扩展行非常有限。目前最先进的并行数据库 Oracle 在理论上的扩展能力也只有100台左右。
  9. 规模 。由于Hive建立在集群上并可以利用MapReduce进行并行计算,因此可以支持很大规模的数据;对应的,数据库可以支持的数据规模较小。

数据类型

在对Hive进行操作之前,首先要明白Hive数据类型有哪些。

  • tinyint/smallint/int/bigint
  • float/double
  • boolean
  • DECIMAL -用户可以指定范围和小数点位数
  • STRING -在特定的字符集中的一个字符串序列
  • VARCHAR -在特定的字符集中的一个有最大长度限制的字符串序列
  • CHAR -在特定的字符集中的一个指定长度的字符串序列
  • BINARY -一个二进制位序列
  • 结构体类型(Stuct): 使用点(.)来访问类型内部的元素。例如,有一列c,它是一个结构体类型{a INT; b INT},字段a可以使用表达式c.a来访问。
  • Map(key-value键值对):使用[‘元素名’]来访问元素。例如,有一个MapM,包含’group’->gid的映射,则gid的值可以使用M[‘group’]来访问。
  • 数组:数组中的元素是相同的类型。可以使用[n]来访问数组元素,n是数组下标,以0开始。例如有一个数组A,有元素[‘a’,’b’,’c’],则A[1]返回’b’。