目录
2.1、范式建模法(Third Normal Form 3NF)
9.5、export导出与import导入hive表数据(内部表操作)
12.4.3、每个MapReduce内部排序(sort by)局部排序
16.2.1、unix时间戳转日期函数:from_unixtime
16.2.2、获取当前unix时间戳函数:unix_timestamp
16.2.3、日期转unix时间戳函数:unix_timestamp
16.2.4、指定格式日期转unix时间戳函数:unix_timestamp
16.4.4、字符串连接并指定字符串分隔符:concat_ws
16.4.6、字符串截取函数:substr、substring
16.4.11、json解析函数:get_json_object
16.8、hive当中的lateral view 与 explode以及reflect和分析函数
16.8.1、使用explode函数将hive表中的Map和Array字段数据进行拆分
16.9.4、创建本地constellation.txt,导入数据
20.3.1、通过MultiDelimitSerDe解决多字符分割场景
一、数仓基础
1、数仓基本介绍
1.1、数仓基本概念
英文名称为Data Warehouse,可简写为DW或DWH。数据仓库的目的是构建面向分析的集成化数据环境,为企业提供决策支持(Decision Support)。它出于分析性报告和决策支持目的而创建。
数据仓库本身并不“生产”任何数据,同时自身也不需要“消费”任何的数据,数据来源于外部,并且开放给外部应用,这也是为什么叫“仓库”,而不叫“工厂”的原因。
通俗理解,收集大量数据,进行分析,为管理层提供解决方案和系统数据支持
通俗的讲,比如一个电商app,收集了大量的用户信息,购买信息,就会通过用户的购买行为去分析出,用户爱买什么想要买什么,什么样的人群会买什么样的商品,进行精准的定位,实现利益最大化。
数据仓库存放大量数据,低密度价值,成体积之后才能体现出价值
1.2、数仓的定义
数据仓库是面向主题的(Subject-Oriented )、集成的(Integrated)、稳定性的(Non-Volatile)和时变的(Time-Variant )数据集合,用以支持管理决策。
1.2.1、面向主题
数据仓库中的数据是按照一定的主题域进行组织。
主题是一个抽象的概念,是指用户使用数据仓库进行决策时所关心的重点方面,一个主题通常与多个操作型信息系统相关。
1.2.2、集成性
根据决策分析的要求,将分散于各处的源数据进行抽取、筛选、清理、综合等工作,最终集成到数据仓库中。
1.2.3、稳定性
数据的相对稳定性,数据仓库中的数据只进行新增,没有更新操作、删除操作处理。
反映历史变化,以查询分析为主。
1.2.4、时变性
数据仓库的数据一般都带有时间属性,随着时间的推移而发生变化,不断地生成主题的新快照
1.3、数据仓库与数据库的区别
数据库与数据仓库的区别实际讲的是 OLTP 与 OLAP 的区别。
OLTP: On-Line Transaction Processing 叫联机事务处理, 也可以称面向交易的处理系统,它是针对具体业务在数据库联机的日常操作,通常对少数记录进行查询、修改。用户较为关心操作的响应时间、数据的安全性、完整性和并发支持的用户数等问题。传统的数据库系统作为数据管理的主要手段,主要用于操作型处理 .
OLAP:On-Line Analytical Processing 叫联机分析处理,一般针对某些主题的历史数据进行分析,支持管理决策。
简而言之,数据库是面向事务的设计,数据仓库是面向主题设计的。
数据库一般存储在线交易数据,有很高的事务要求;数据仓库存储的一般是历史数据。
数据库设计是尽量避免冗余,一般采用符合范式的规则来设计,数据仓库在设计是有意引入冗余,采用反范式的方式来设计。
数据库是为捕获数据而设计,数据仓库是为分析数据而设计,它的两个基本的元素是维表和事实表。维是看问题的角度,比如时间,部门,维表放的就是这些东西的定义,事实表里放着要查询的数据,同时有维的ID。
功能 | 数据仓库 | 数据库 |
数据范围 | 存储历史的、完整的、反应历史变化的 | 当前状态数据 |
数据变化 | 可添加、无删除、无变更的、反应历史变化 | 支持频繁的增、删、改、查操作 |
应用场景 | 面向分析、支持战略决策 | 面向业务交易流程 |
设计理论 | 违范式、适当冗余 | 遵照范式(第一、二、三等范式)、避免冗余 |
处理量 | 非频繁、大批量、高吞吐、有延迟 | 频繁、小批次、高并发、低延迟 |
1.4、构建数仓常用手段
• 传统数仓建设更多的基于成熟的商业数据集成平台,比如Teradata、Oracle、Informatica等,技术体系比较成熟完善,但相对比较封闭,对实施者技术面要求也相对专业且单一,一般更多应用于银行、保险、电信等“有钱”行业.
• 基于大数据的数仓建设一般是基于非商业、开源的技术,常见的是基于hadoop生态构建,涉及技术较广泛、复杂,同时相对于商业产品,稳定性、服务支撑较弱,需要自己维护更多的技术框架。在大数据领域,常用的数据仓库构建手段很多基于hive,sparkSQL,impala等各种技术框架.
1.5、数仓分层
1.5.1、数仓分层描述
- 数据仓库更多代表的是一种对数据的管理和使用的方式,它是一整套包括了etl、调度、建模在内的完整的理论体系。现在所谓的大数据更多的是一种数据量级的增大和工具的上的更新。 两者并无冲突,相反,而是一种更好的结合。数据仓库在构建过程中通常都需要进行分层处理。业务不同,分层的技术处理手段也不同。
- 分层是数据仓库解决方案中,数据架构设计的一种数据逻辑结构 ,通过分层理念建立的数据仓库,它的可扩展性非常好,这样设计出来的模型架构,可以任意地增减、替换数据仓库中的各个组成部分。
从整体的逻辑划分来讲,数据仓库模型实际上就是这三层架构。
接入层:底层的数据源或者是操作数据层,一般在公司的话,统一都是称为ODS层
中间层:是做数据仓库同学需要花费更多精力的一层,这一层包括的内容是最多的、最复杂的。
应用层:对不同的应用提供对应的数据。该层主要是提供数据产品和数据分析使用的数据,比如我们经常说的报表数据
- 针对于这三层架构,这里给出比较典型的一个做数据仓库在实施的时候,具体的层次划分。
- ODS:
-
- Operation Data Store 原始数据层
- 不对数据进行过多处理,只做轻度的处理,保存原始数据基本结构
- DWD
-
- data warehouse detail 数据明细层
- 它主要是针对于接入层的数据进行数据的清洗和转换。还有就是一些维度的补充。
- 进行清洗(去空,去脏,取出明显不符合要求的数据,结构和粒度跟原始数据保持一致)
- DWS
-
- data warehouse summary 数据汇总层
- 它是在DWD明细层之上,也有公司叫DW层
- 它是按照一定的粒度进行了汇总聚合操作。它是单业务场景。
- DWM
-
- data warehouse market 数据集市层
- 它是在DWS数据汇总层之上,集市层它是多业务场景的。
- APP
-
- Application 应用层
- 这个是数据仓库的最后一层数据,为应用层数据,直接可以给业务人员使用。
TMP临时表:在做一些中间层表计算的时候,大量使用tmp临时表。
DIM维度层:基于ODS层和DWD层抽象出一些公共的维度,典型的公共维度主要包括城市信息、渠道信息、个人基础属性信息。
1.5.2、为什么要进行数仓分层
因为原始数据人看不懂,老板看不懂,决策者也看不懂,需要我们去梳理,去清洗,去进行价值提取
分层的主要原因是在管理数据的时候,能对数据有一个更加清晰的掌控,主要有下面几个原因:
- 空间换时间
-
- 通过建设多层次的数据模型供用户使用,避免用户直接使用底层操作型数据,可以更高效的访问数据。
- 把复杂问题简单化
-
- 将一个复杂的任务分解成多个步骤来完成,每一层只处理单一的步骤,比较简单和容易理解。而且便于维护数据的准确性,当数据出现问题之后,可以不用修复所有的数据,只需要从有问题的步骤开始修复。
- 便于处理业务的变化
-
- 随着业务的变化,只需要调整底层的数据,对应用层对业务的调整零感知。
- 清晰数据结构
-
- 每一个数据分层都有它的作用域,这样我们在使用表的时候能更方便地定位和理解。
- 数据血缘追踪
-
- 简单来说,我们最终给业务呈现的是一个能直接使用业务表,但是它的来源有很多,如果有一张来源表出问题了,我们希望能够快速准确地定位到问题,并清楚它的危害范围。
- 减少重复开发
-
- 规范数据分层,开发一些通用的中间层数据,能够减少极大的重复计算。
- 屏蔽原始数据的异常
-
- 屏蔽业务的影响,不必改一次业务就需要重新接入数据
1.6、获取数据
日志:flume等
- 落地到磁盘
- 前端埋点数据
- 移动端埋点
业务:导入导出工具 datax sqoop canal
- 数据库中的数据
爬虫:python直接写入到hdfs
- 爬取精品数据
- 什么是埋点
所谓“埋点”,是数据采集领域(尤其是用户行为数据采集领域)的术语,指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。将数据写入到日志文件(落盘)
1.7、同步策略
全量表:存储完整的数据。
增量表:存储新增加的数据。
新增及变化表:存储新增加的数据和变化的数据。
拉链表:对新增及变化表做定期合并。
- 实体表同步策略
比如用户,商品,商家
实体表数据量比较小,通常可以做每日全量,每天保存一份数据,即为每日全量
- 维度表同步策略
订单状态,审批状态,商品分类
比如撤销订单,审批失败这种作为每日全量进行同步
但是比如民族国家地区省事这些作为一份固定值
2、数据仓库建模
目前业界较为流行的数据仓库的建模方法非常多,这里主要介绍范式建模法,维度建模法,实体建模法等几种方法,每种方法其实从本质上讲就是从不同的角度看我们业务中的问题,不管从技术层面还是业务层面,其实代表的是哲学上的一种世界观。
2.1、范式建模法(Third Normal Form 3NF)
范式建模法是基于整个关系型数据库的理论基础之上发展而来的,其实是我们在构建数据模型常用的一个方法,主要解决关系型数据库得数据存储,利用的一种技术层面上的方法。目前,我们在关系型数据库中的建模方法,大部分采用的是三范式建模法。
从其表达的含义来看,一个符合第三范式的关系必须具有以下三个条件 :
(1)每个属性值唯一,不具有多义性 ;
(2)每个非主属性必须完全依赖于整个主键,而非主键的一部分 ;
(3)每个非主属性不能依赖于其他关系中的属性,因为这样的话,这种属性应该归到其他关系中去。
2.2、维度建模法
维度建模(dimensional modeling)是专门用于分析型数据库、数据仓库、数据集市建模的方法。维度建模法简单描述就是按照事实表、维度表来构建数仓、集市。
维度建模从分析决策的需求出发构建模型,为分析需求服务,因此它重点关注用户如何更快速地完成需求分析,同时具有较好的大规模复杂查询的相应性能。
2.2.1、维度表
维度表是你要对数据进行分析时所用的一个量,比如你要分析产品销售情况, 你可以选择按类别来进行分析,或按区域来分析。
通常来说维度表信息比较固定,且数据量小
一般是指对应一些业务状态,编号的解释表。也可以称之为码表。
地区表,订单状态,支付方式,审批状态,商品分类
2.2.2、事实表
表示对分析主题的度量。
事实表包含了与各维度表相关联的外键,并通过join方式与维度表关联。事实表的度量通常是数值类型,且记录数会不断增加,表规模迅速增长。
消费事实表:Prod_id(引用商品维度表), TimeKey(引用时间维度表), Place_id(引用地点维度表), Unit(销售量)。
总的说来,在数据仓库中不需要严格遵守规范化设计原则。因为数据仓库的主导功能就是面向分析,以查询为主,不涉及数据更新操作。事实表的设计是以能够正确记录历史信息为准则,维度表的设计是以能够以合适的角度来聚合主题内容为准则
一般是指一个现实存在的业务对象,比如用户,商品,商家,销售员等等
2.2.3、维度建模三种模式
基于事实表和维表就可以构建出多种多维模型,包括星形模型、雪花模型和星座模型。
维度建模法最被人广泛知晓的名字就是星型模式。
- 星型模式
星形模式(Star Schema)是最常用的维度建模方式。星型模式是以事实表为中心,所有的维度表直接连接在事实表上,像星星一样。
星形模式的维度建模由一个事实表和一组维表成,且具有以下特点:
a. 维表只和事实表关联,维表之间没有关联;
b. 每个维表主键为单列,且该主键放置在事实表中,作为两边连接的外键;
c. 以事实表为核心,维表围绕核心呈星形分布;
- 雪花模式
雪花模式是对星形模式的扩展。雪花模式的维度表可以拥有其他维度表的,虽然这种模型相比星型更规范一些,但是由于这种模型不太容易理解,维护成本比较高,而且性能方面需要关联多层维表,性能也比星型模型要低。所以一般不是很常用。
星座模式是星型模式延伸而来,星型模式是基于一张事实表的,而星座模式是基于多张事实表的,而且共享维度信息。
前面介绍的两种维度建模方法都是多维表对应单事实表,但在很多时候维度空间内的事实表不止一个,而一个维表也可能被多个事实表用到。在业务发展后期,绝大部分维度建模都采用的是星座模式。
2.3、实体建模法
实体建模法并不是数据仓库建模中常见的一个方法,它来源于哲学的一个流派。
从哲学的意义上说,客观世界应该是可以细分的,客观世界应该可以分成由一个个实体,以及实体与实体之间的关系组成。
那么我们在数据仓库的建模过程中完全可以引入这个抽象的方法,将整个业务也可以划分成一个个的实体,而每个实体之间的关系,以及针对这些关系的说明就是我们数据建模需要做的工作。
3、数据仓库架构
3.1、数据采集
数据采集层的任务就是把数据从各种数据源中采集和存储到数据存储上,期间有可能会做一些ETL操作。
数据源种类可以有多种:
日志:所占份额最大,存储在备份服务器上
业务数据库:如Mysql、Oracle
来自HTTP/FTP的数据:合作伙伴提供的接口
其他数据源:如Excel等需要手工录入的数据
3.2、数据存储与分析
HDFS是大数据环境下数据仓库/数据平台最完美的数据存储解决方案。
离线数据分析与计算,也就是对实时性要求不高的部分,Hive是不错的选择。
使用Hadoop框架自然而然也提供了MapReduce接口,如果真的很乐意开发Java,或者对SQL不熟,那么也可以使用MapReduce来做分析与计算。
Spark性能比MapReduce好很多,同时使用SparkSQL操作Hive。
3.3、数据共享
前面使用Hive、MR、Spark、SparkSQL分析和计算的结果,还是在HDFS上,但大多业务和应用不可能直接从HDFS上获取数据,那么就需要一个数据共享的地方,使得各业务和产品能方便的获取数据。
这里的数据共享,其实指的是前面数据分析与计算后的结果存放的地方,其实就是关系型数据库和NOSQL数据库。
3.4、数据应用
报表:报表所使用的数据,一般也是已经统计汇总好的,存放于数据共享层。
接口:接口的数据都是直接查询数据共享层即可得到。
即席查询:即席查询通常是现有的报表和数据共享层的数据并不能满足需求,需要从数据存储层直接查询。一般都是通过直接操作SQL得到。
4、理想的数仓架
增加以下内容
- 数据采集
-
- 采用Flume收集日志,采用Sqoop将RDBMS以及NoSQL中的数据同步到HDFS上
- 消息系统
-
- 可以加入Kafka分布式消息系统保证数据丢失
- 机器学习
-
- 使用了SparkMLlib提供的机器学习算法
- 数据可视化
-
- 提供可视化前端页面,方便运营等非开发人员直接查询
- 任务调度和监控
-
- 任务的统一调度和监控
5、技术选型
数据采集:flume kafka sqoop datax
数据存储:mysql hdfs hbase redis
数据计算:hive spark flink
数据查询:impala kylin
6、实例
6.1、用户行为日志
{"app":"xxxxx",//项目数据来源 app pc
"common": { //公共字段
"mid": "", // (String) 设备唯一标识
"uid": "", // (String) 用户标识
"vc": "1", // (String) versionCode,程序版本号
"vn": "1.0", // (String) versionName,程序版本名
"l": "zh", // (String) 系统语言
"sr": "", // (String) 渠道号,应用从哪个渠道来的。
"os": "7.1.1", // (String) Android系统版本
"ar": "CN", // (String) 区域
"md": "BBB100-1", // (String) 手机型号
"ba": "apple", // (String) 手机品牌
"sv": "13", // (String) sdkVersion
"g": "", // (String) gmail
"t": "1506047606608", // (String) 客户端日志产生时的时间
"nw": "WIFI", // (String) 网络模式
},
"event": [ //事件
{
"e_time": "1506047605364", //事件时间
"e_name": "display", //事件名称
"e_result": { //事件结果,以key-value形式自行定义
"goodsid": "1111",
"action": "2",
"extend1": "3",
"place": "4",
"category": "75"
}
}
]
}
- 页面
action 动作:曝光商品=1, 加载成功=2,加载失败=3 loading_time 加载时长:点击商品进入页面所用时间 loading_way 加载类型:1-读取缓存,2-从接口拉新数据 (加载成功才上报加载类型) watchtime 浏览当前页面时间 actionType 打开方式(连接,搜索) watchtype 加载类型:自动加载=1,用户下拽加载=2,底部加载=3(底部条触发点击底部提示条/点击返回顶部加载) type1 加载失败码:把加载失败状态码报回来(报空为加载成功,没有失败)
- 商品点击
事件标签:clickDisplay action 动作:曝光商品=1,点击商品=2, goodsid 商品ID(服务端下发的ID) order 顺序(第几条商品,第一条为0,第二条为1,如此类推) extend1 曝光类型:1 - 首次曝光 2-重复曝光 category 分类ID(服务端定义的分类ID)
- 广告
事件名称:advertisement 标签 含义 entry 入口:商品列表页=1 应用首页=2 商品详情页=3 在哪里看到广告的 action 动作:请求广告=1 取缓存广告=2 广告位展示=3 广告展示=4 广告点击=5 content 状态:成功=1 失败=2 detail 失败码(没有则上报空) source 广告来源: newstype Type: 1- 图文 2-图集 3-段子 4-GIF 5-视频 6-调查 7-纯文 8-视频+图文 9-GIF+图文 0-其他 show_style 内容样式:无图(纯文字)=6 一张大图=1 三站小图+文=4 一张小图=2 一张大图两张小图+文=3 图集+文 = 5 一张大图+文=11 GIF大图+文=12 视频(大图)+文 = 13
6.2、日志处理
Flume:过滤,拦截器,对数据进行过滤,多路数据归类
Kafka:使用不通topic去接收不同种类的数据,对数据进行分流
Hdfs:存储数据
Hive:接收清洗过的数据进行分层处理,提供业务支持
6.3、用户画像
- 女人
- 金融产品
- 头条
- 音乐
6.4、某电商数据建设整体架构
二、HIVE工具落地
2.1、Hive的概念
Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。其本质是将SQL转换为MapReduce的任务进行运算,底层由HDFS来提供数据的存储,说白了hive可以理解为一个将SQL转换为MapReduce的任务的工具,甚至更进一步可以说hive就是一个MapReduce的客户端
2.2、Hive与数据库的区别
- Hive 具有 SQL 数据库的外表,但应用场景完全不同。
- Hive 只适合用来做海量离线数据统计分析,也就是数据仓库。
2.3、Hive的优缺点
2.3.1 优点
- 操作接口采用类SQL语法,提供快速开发的能力(简单、容易上手)。
- 避免了去写MapReduce,减少开发人员的学习成本。
- Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
2.3.2 缺点
- Hive 不支持记录级别的增删改操作
- Hive 的查询延迟很严重
- hadoop jar xxxx.jar xxx.class /input /output
-
-
- 进行任务的划分,然后进行计算资源的申请
- map 0% reduce 0%
- map 10% reduce 0%
- 进行任务的划分,然后进行计算资源的申请
-
- Hive 不支持事务
2.4、Hive架构原理
2.4.1、用户接口:Client
- CLI(hive shell)、JDBC/ODBC(java访问hive)、WEBUI(浏览器访问hive)
2.4.2、元数据:Metastore
- 元数据包括:表名、表所属的数据库(默认是default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等;
- 默认存储在自带的derby数据库中,==推荐使用MySQL存储Metastore==
2.4.3、Hadoop集群
- 使用HDFS进行存储,使用MapReduce进行计算。
2.4.4、Driver:驱动器
- 解析器(SQL Parser)
-
- 将SQL字符串转换成抽象语法树AST
- 对AST进行语法分析,比如表是否存在、字段是否存在、SQL语义是否有误
- 编译器(Physical Plan):将AST编译生成逻辑执行计划
- 优化器(Query Optimizer):对逻辑执行计划进行优化
- 执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。对于Hive来说默认就是mapreduce任务
3、Hive安装部署
注意hive就是一个构建数据仓库的工具,只需要在一台服务器上安装就可以了,不需要在多台服务器上安装。
1、提前先安装好mysql服务和hadoop集群
详细见https://www.cnblogs.com/brianzhu/p/8575243.html
2、下载hive的安装包
http://archive.cloudera.com/cdh5/cdh/5/hive-1.1.0-cdh5.14.2.tar.gz
3、规划安装目录
/kkb/install
4、上传安装包到node03服务器中的/kkb/soft路径下
5、解压安装包到指定的规划目录
cd /kkb/soft/ tar -zxf hive-1.1.0-cdh5.14.2.tar.gz -C /kkb/install/
6、修改配置文件
进入到Hive的安装目录下的conf文件夹中
cd /kkb/install/hive-1.1.0-cdh5.14.2/conf/
mv hive-env.sh.template hive-env.sh
vim hive-env.sh
#配置HADOOP_HOME路径 export HADOOP_HOME=/kkb/install/hadoop-2.6.0-cdh5.14.2/ #配置HIVE_CONF_DIR路径 export HIVE_CONF_DIR=/kkb/install/hive-1.1.0-cdh5.14.2/conf
vim hive-site.xml
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://node03:3306/hive?createDatabaseIfNotExist=true&characterEncoding=latin1&useSSL=false</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123456</value>
</property>
<property>
<name>hive.cli.print.current.db</name>
<value>true</value>
</property>
<property>
<name>hive.cli.print.header</name>
<value>true</value>
</property>
<property>
<name>hive.server2.thrift.bind.host</name>
<value>node03.kaikeba.com</value>
</property>
</configuration>
修改hive的日志配置文件路径,便于我们排查hive出现的错误问题
node03执行以下命令,定义hive的日志文件存放路径
mkdir -p /kkb/install/hive-1.1.0-cdh5.14.2/logs/ cd /kkb/install/hive-1.1.0-cdh5.14.2/conf/ mv hive-log4j.properties.template hive-log4j.properties vim hive-log4j.properties #更改以下内容,设置我们的日志文件存放的路径 hive.log.dir=/kkb/install/hive-1.1.0-cdh5.14.2/logs/
ps: 需要将mysql的驱动包上传到hive的lib目录下
例如 mysql-connector-java-5.1.38.jar
4、Hive交互方式
先启动hadoop集群和mysql服务
4.1、Hive交互shell
cd /kkb/install/hive-1.1.0-cdh5.14.2 bin/hive
4.2、Hive JDBC服务
启动hiveserver2服务
- 前台启动
cd /kkb/install/hive-1.1.0-cdh5.14.2 bin/hive --service hiveserver2
- 后台启动
cd /kkb/install/hive-1.1.0-cdh5.14.2 nohup bin/hive --service hiveserver2 &
- beeline连接hiveserver2
重新开启一个会话窗口,然后使用beeline连接hive
cd /kkb/install/hive-1.1.0-cdh5.14.2 bin/beeline beeline> !connect jdbc:hive2://node03:10000
4.3、Hive的命令
hive -e sql语句
- 使用 –e 参数来直接执行hql的语句
cd /kkb/install/hive-1.1.0-cdh5.14.2/ bin/hive -e "show databases"
hive -f sql文件
- 使用 –f 参数执行包含hql语句的文件
node03执行以下命令准备hive执行脚本
cd /kkb/install/ vim hive.sql 文件内容如下 create database if not exists myhive; 通过以下命令来执行我们的hive脚本 cd /kkb/install/hive-1.1.0-cdh5.14.2/ bin/hive -f /kkb/install/hive.sql
5、Hive数据类型
5.1、基本数据类型
类型名称 | 描述 | 举例 |
boolean | true/false | true |
tinyint | 1字节的有符号整数 | 1 |
smallint | 2字节的有符号整数 | 1 |
int | 4字节的有符号整数 | 1 |
bigint | 8字节的有符号整数 | 1 |
float | 4字节单精度浮点数 | 1.0 |
double | 8字节单精度浮点数 | 1.0 |
string | 字符串(不设长度) | “abc” |
varchar | 字符串(1-65355长度,超长截断) | “abc” |
timestamp | 时间戳 | 1563157873 |
date | 日期 | 20190715 |
5.2、复合数据类型
类型名称 | 描述 | 举例 |
array | 一组有序的字段,字段类型必须相同 array(元素1,元素2) | Array(1,2,3) |
map | 一组无序的键值对 map(k1,v1,k2,v2) | Map(‘a’,1,'b',2) |
struct | 一组命名的字段,字段类型可以不同 struct(元素1,元素2) | Struct('a',1,2,0) |
- array字段的元素访问方式:
-
- 下标获取元素,下标从0开始
-
-
- 获取第一个元素
-
-
-
-
- array[0]
-
-
- map字段的元素访问方式
-
- 通过键获取值
-
-
- 获取a这个key对应的value
-
-
-
-
- map['a']
-
-
- struct字段的元素获取方式
-
- 定义一个字段c的类型为struct{a int;b string}
-
-
- 获取a和b的值
-
-
-
-
- 使用c.a 和c.b 获取其中的元素值
-
-
-
-
-
-
- 这里可以把这种类型看成是一个对象
-
-
-
create table complex( col1 array<int>, col2 map<string,int>, col3 struct<a:string,b:int,c:double> )
1、参数说明
创建表的时候可以指定每行数据的格式,如果使用的是复合数据类型,还需要指定复合数据类型中的元素分割符 ROW FORMAT DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char] [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char] 其中这里 FIELDS TERMINATED BY char 指定每一行记录中字段的分割符 COLLECTION ITEMS TERMINATED BY char 指定复合类型中多元素的分割符 MAP KEYS TERMINATED BY char 指定map集合类型中每一个key/value之间的分隔符 LINES TERMINATED BY char 指定每行记录的换行符,一般有默认 就是\n
2、Array类型
- array中的数据为相同类型,例如,假如array A中元素['a','b','c'],则A[1]的值为'b'
- 准备数据文件
-
- t_array.txt (字段空格分割)
1 zhangsan beijing,shanghai 2 lisi shanghai,tianjin
- 建表语法
create table t_array( id string, name string, locations array<string> ) row format delimited fields terminated by ' ' collection items terminated by ',';
- 加载数据
load data local inpath '/home/hadoop/t_array.txt' into table t_array;
- 查询数据
select id,name,locations[0],locations[1] from t_array;
3、Map类型
- map类型中存储key/value类型的数据,后期可以通过["指定key名称"]访问
- 准备数据文件
-
- t_map.txt (字段空格分割)
1 name:zhangsan#age:30 2 name:lisi#age:40
- 建表语法
create table t_map( id string, info map<string,string> ) row format delimited fields terminated by ' ' collection items terminated by '#' map keys terminated by ':';
- 加载数据
load data local inpath '/home/hadoop/t_map.txt' into table t_map;
- 查询数据
select id,info['name'],info['age'] from t_map;
4、Struct类型
- 可以存储不同类型的数据
-
- 例如c的类型为struct{a INT; b INT},我们可以通过c.a来访问域a
- 准备数据文件
-
- t_struct.txt (字段空格分割)
1 zhangsan:30:beijing 2 lisi:40:shanghai
- 建表语法
create table t_struct( id string, info struct<name:string, age:int,address:String> ) row format delimited fields terminated by ' ' collection items terminated by ':' ;
- 加载数据
load data local inpath '/home/hadoop/t_struct.txt' into table t_struct;
- 查询数据
select id,info.name,info.age,info.address from t_struct;
6、Hive的DDL操作
6.1、Hive的数据库DDL操作
6.1.1、创建数据库
hive > create database db_hive; # 或者 hive > create database if not exists db_hive;
数据库在HDFS上的默认存储路径是/user/hive/warehouse/*.db
6.1.2、显示所有数据库
hive> show databases;
6.1.3、查询数据库
hive> show databases like 'db_hive*';
6.1.4、查看数据库详情
hive> desc database db_hive;
6.1.5、显示数据库详细信息
hive> desc database extended db_hive;
6.1.6、切换当前数据库
hive > use db_hive;
6.1.7、删除数据库
#删除为空的数据库 hive> drop database db_hive; #如果删除的数据库不存在,最好采用if exists 判断数据库是否存在 hive> drop database if exists db_hive; #如果数据库中有表存在,这里需要使用cascade强制删除数据库 hive> drop database if exists db_hive cascade;
6.2、Hive的数据表DDL操作
6.2.1、建表语法介绍
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name data_type [COMMENT col_comment], ...)] [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] [ROW FORMAT row_format] row format delimited fields terminated by “分隔符” [STORED AS file_format] [LOCATION hdfs_path]
官网地址:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL
6.2.2、字段解释说明
- create table
-
- 创建一个指定名字的表
- EXTERNAL
-
- 创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION),指定表的数据保存在哪里
- COMMENT
-
- 为表和列添加注释
- PARTITIONED BY
-
- 创建分区表
- CLUSTERED BY
-
- 创建分桶表
- SORTED BY
-
- 按照字段排序(一般不常用)
- ROW FORMAT
-
- 指定每一行中字段的分隔符
-
-
- row format delimited fields terminated by ‘\t’
-
- STORED AS
-
- 指定存储文件类型
-
-
- 常用的存储文件类型:SEQUENCEFILE(二进制序列文件)、TEXTFILE(文本)、RCFILE(列式存储格式文件)
- 如果文件数据是纯文本,可以使用STORED AS TEXTFILE。如果数据需要压缩,使用 STORED AS SEQUENCEFILE
-
- LOCATION
-
- 指定表在HDFS上的存储位置。
6.2.3、创建内部表
- 1、直接建表
-
- 使用标准的建表语句
use myhive; create table stu(id int,name string); 可以通过insert into 向hive表当中插入数据,但是不建议工作当中这么做 insert into stu(id,name) values(1,"zhangsan"); select * from stu;
- 2、查询建表法
-
- 通过AS 查询语句完成建表:将子查询的结果存在新表里,有数据
create table if not exists myhive.stu1 as select id, name from stu;
- 3、like建表法
-
- 根据已经存在的表结构创建表
create table if not exists myhive.stu2 like stu;
- 4、查询表的类型
hive > desc formatted myhive.stu;
创建内部表并指定字段之间的分隔符,指定文件的存储格式,以及数据存放的位置
create table if not exists myhive.stu3(id int ,name string) row format delimited fields terminated by '\t' stored as textfile location '/user/stu2';
6.2.4、创建外部表
外部表因为是指定其他的hdfs路径的数据加载到表当中来,所以hive表会认为自己不完全独占这份数据,所以删除hive表的时候,数据仍然存放在hdfs当中,不会删掉
create external table myhive.teacher (t_id string,t_name string) row format delimited fields terminated by '\t';
- 创建外部表的时候需要加上external关键字
- location字段可以指定,也可以不指定
-
- 指定就是数据存放的具体目录
- 不指定就是使用默认目录 /user/hive/warehouse
向外部表当中加载数据:
我们前面已经看到过通过insert的方式向内部表当中插入数据,外部表也可以通过insert的方式进行插入数据,只不过insert的方式,我们一般都不推荐,实际工作当中我们都是使用load的方式来加载数据到内部表或者外部表
load数据可以从本地文件系统加载或者也可以从hdfs上面的数据进行加载
- 从本地文件系统加载数据到teacher表当中去,将我们附件当汇总的数据资料都上传到node03服务器的/kkb/install/hivedatas路径下面去
mkdir -p /kkb/install/hivedatas #将数据都上传到/kkb/install/hivedatas路径下,然后在hive客户端下执行以下操作 load data local inpath '/kkb/install/hivedatas/teacher.csv' into table myhive.teacher;
- 从hdfs上面加载文件到teacher表里面去(将teacher.csv文件上传到hdfs的/kkb/hdfsload/hivedatas路径下)
cd /kkb/install/hivedatas hdfs dfs -mkdir -p /kkb/hdfsload/hivedatas hdfs dfs -put teacher.csv /kkb/hdfsload/hivedatas # 在hive的客户端当中执行 load data inpath '/kkb/hdfsload/hivedatas' overwrite into table myhive.teacher;
6.2.5、内部表和外部表相互转换
- 1、内部表转换为外部表
#将stu内部表改为外部表 alter table stu set tblproperties('EXTERNAL'='TRUE');
- 2、外部表转换为内部表
#把emp外部表改为内部表 alter table teacher set tblproperties('EXTERNAL'='FALSE');
6.2.6、内部表和外部表的区别
- 1、建表语法的区别
-
- 外部表在创建的时候需要加上external关键字
- 2、删除表之后的区别
-
- 内部表删除后,表的元数据和真实数据都被删除了
- 外部表删除后,仅仅只是把该表的元数据删除了,真实数据还在,后期还是可以恢复出来
6.2.7、内部表和外部表的使用时机
内部表由于删除表的时候会同步删除HDFS的数据文件,所以确定如果一个表仅仅是你独占使用,其他人不使用的时候就可以创建内部表,如果一个表的文件数据,其他人也要使用,那么就创建外部表
一般外部表都是用在数据仓库的ODS层
内部表都是用在数据仓库的DW层
6.2.8、hive分区表
如果hive当中所有的数据都存入到一个文件夹下面,那么在使用MR计算程序的时候,读取一整个目录下面的所有文件来进行计算,就会变得特别慢,因为数据量太大了,实际工作当中一般都是计算前一天的数据,所以我们只需要将前一天的数据挑出来放到一个文件夹下面即可,专门去计算前一天的数据。这样就可以使用hive当中的分区表,通过分文件夹的形式,将每一天的数据都分成为一个文件夹,然后我们计算数据的时候,通过指定前一天的文件夹即可只计算前一天的数据。
在大数据中,最常用的一种思想就是分治,我们可以把大的文件切割划分成一个个的小的文件,这样每次操作一个小的文件就会很容易了,同样的道理,在hive当中也是支持这种思想的,就是我们可以把大的数据,按照每天,或者每小时进行切分成一个个的小的文件,这样去操作小的文件就会容易得多了
在文件系统上建立文件夹,把表的数据放在不同文件夹下面,加快查询速度。
6.2.8.1、创建分区表语法
hive (myhive)> create table score(s_id string,c_id string, s_score int) partitioned by (month string) row format delimited fields terminated by '\t';
6.2.8.2、创建一个表带多个分区
hive (myhive)> create table score2 (s_id string,c_id string, s_score int) partitioned by (year string,month string,day string) row format delimited fields terminated by '\t';
6.2.8.3、加载数据到分区表当中去
hive (myhive)>load data local inpath '/kkb/install/hivedatas/score.csv' into table score partition (month='201806');
6.2.8.4、加载数据到多分区表当中去
hive (myhive)> load data local inpath '/kkb/install/hivedatas/score.csv' into table score2 partition(year='2018',month='06',day='01');
6.2.8.5、查看分区
hive (myhive)> show partitions score;
6.2.8.6、添加一个分区
hive (myhive)> alter table score add partition(month='201805');
6.2.8.7、同时添加多个分区
hive (myhive)> alter table score add partition(month='201804') partition(month = '201803');
注意:添加分区之后就可以在hdfs文件系统当中看到表下面多了一个文件夹
6.2.8.8、删除分区
hive (myhive)> alter table score drop partition(month = '201806');
6.2.8.9、外部分区表综合练习
需求描述:现在有一个文件score.csv文件,里面有三个字段,分别是s_id string, c_id string,s_score int,字段都是使用 \t进行分割,存放在集群的这个目录下/scoredatas/day=20180607,这个文件每天都会生成,存放到对应的日期文件夹下面去,文件别人也需要公用,不能移动。需求,创建hive对应的表,并将数据加载到表中,进行数据统计分析,且删除表之后,数据不能删除
- 需求实现:
-
- 数据准备:
node03执行以下命令,将数据上传到hdfs上面去
将我们的score.csv上传到node03服务器的/kkb/install/hivedatas目录下,然后将score.csv文件上传到/kkb/install/hivedatas目录下去
cd /kkb/install/hivedatas/ hdfs dfs -mkdir -p /scoredatas/day=20180607 hdfs dfs -put score.csv /scoredatas/day=20180607/
-
- 创建外部分区表,并指定文件数据存放目录
hive (myhive)> create external table score4(s_id string, c_id string,s_score int) partitioned by (day string) row format delimited fields terminated by '\t' location '/scoredatas';
-
- 进行表的修复,说白了就是建立我们表与我们数据文件之间的一个关系映射
hive (myhive)> msck repair table score4;
修复成功之后即可看到数据已经全部加载到表当中去了
7、Hive分桶表
7.1、分桶表
- 分桶是相对分区进行更细粒度的划分。
- 分桶将整个数据内容按照某列属性值取hash值进行区分,具有相同hash值的数据进入到同一个文件中
-
- 比如按照name属性分为3个桶,就是对name属性值的hash值对3取摸,按照取模结果对数据分桶。
-
-
- 取模结果为0的数据记录存放到一个文件
- 取模结果为1的数据记录存放到一个文件
- 取模结果为2的数据记录存放到一个文件
- 取模结果为3的数据记录存放到一个文件
-
- 作用
-
- 1、取样sampling更高效。没有分区的话需要扫描整个数据集。
- 2、提升某些查询操作效率,例如map side join
7.2、案例演示
- 1、创建分桶表
-
- 在创建分桶表之前要执行的命令
-
-
- set hive.enforce.bucketing=true; 开启对分桶表的支持
- set mapreduce.job.reduces=4; 设置与桶相同的reduce个数(默认只有一个reduce)
-
# 进入hive客户端然后执行以下命令 use myhive; set mapreduce.job.reduces=4; set hive.enforce.bucketing=true; --分桶表 create table myhive.user_buckets_demo(id int, name string) clustered by(id) into 4 buckets row format delimited fields terminated by '\t'; --普通表 create table user_demo(id int, name string) row format delimited fields terminated by '\t';
-
- 2、准备数据文件 buckets.txt
#在linux当中执行以下命令 cd /kkb/install/hivedatas/ vim user_bucket.txt 1 laowang1 2 laowang2 3 laowang3 4 laowang4 5 laowang5 6 laowang6 7 laowang7 8 laowang8 9 laowang9 10 laowang10
-
- 3、加载数据到普通表 user_demo 中
load data local inpath '/kkb/install/hivedatas/user_bucket.txt' overwrite into table user_demo;
#在hive客户端当中加载数据 load data local inpath '/kkb/install/hivedatas/user_bucket.txt' into table user_demo;
-
- 4、加载数据到桶表user_buckets_demo中
insert into table user_buckets_demo select * from user_demo;
-
- 5、hdfs上查看表的数据目录
- 6、抽样查询桶表的数据
-
- tablesample抽样语句,语法:tablesample(bucket x out of y)
-
-
- x表示从第几个桶开始取数据
- y表示桶数的倍数,一共需要从 桶数/y 个桶中取数据
-
select * from user_buckets_demo tablesample(bucket 1 out of 2) -- 需要的总桶数=4/2=2个 -- 先从第1个桶中取出数据 -- 再从第1+2=3个桶中取出数据
8、Hive修改表结构
修改表结构语法
alter table old_table_name rename to new_table_name;
8.1、修改表的名称
hive> alter table stu3 rename to stu4;
8.2、表的信息
hive> desc stu4; hive> desc formatted stu4;
8.3、增加/修改/替换列信息
- 增加列
hive> alter table stu4 add columns(address string);
- 修改列
hive> alter table stu4 change column address address_id int;
9、Hive数据导入
9.1、通过load方式加载数据
hive> load data [local] inpath 'dataPath' overwrite | into table student [partition (partcol1=val1,…)]; hive (myhive)> load data local inpath '/kkb/install/hivedatas/score.csv' overwrite into table score3 partition(month='201806');
9.2、通过查询方式加载数据
hive (myhive)> create table score5 like score; hive (myhive)> insert overwrite table score5 partition(month = '201806') select s_id,c_id,s_score from score;
9.3、查询语句中创建表并加载数据(as select)
hive (myhive)> create table score6 as select * from score;
9.4、创建表时通过location指定加载数据路径
1)创建表,并指定在hdfs上的位置
hive (myhive)> create external table score7 (s_id string,c_id string,s_score int) row format delimited fields terminated by '\t' location '/myscore7';
2)上传数据到hdfs上,我们也可以直接在hive客户端下面通过dfs命令来进行操作hdfs的数据
hive (myhive)> dfs -mkdir -p /myscore7; hive (myhive)> dfs -put /kkb/install/hivedatas/score.csv /myscore7;
3)查询数据
hive (myhive)> select * from score7;
9.5、export导出与import导入hive表数据(内部表操作)
hive (myhive)> create table teacher2 like teacher; hive (myhive)> export table teacher to '/kkb/teacher'; hive (myhive)> import table teacher2 from '/kkb/teacher';
10、Hive数据导出
10.1、insert导出
1、将查询的结果导出到本地
insert overwrite local directory '/kkb/install/hivedatas/stu' select * from stu;
2、将查询的结果格式化导出到本地
insert overwrite local directory '/kkb/install/hivedatas/stu2' row format delimited fields terminated by ',' select * from stu;
3、将查询的结果导出到HDFS上(没有local)
insert overwrite directory '/kkb/hivedatas/stu' row format delimited fields terminated by ',' select * from stu;
10.2、Hive shell 命令导出
基本语法:
- hive -e "sql语句" > file
- hive -f sql文件 > file
bin/hive -e 'select * from myhive.stu;' > /kkb/install/hivedatas/student1.txt
10.3、export导出到HDFS上
export table myhive.stu to '/kkb/install/hivedatas/stuexport';
11、Hive的静态分区和动态分区
11.1、静态分区
- 表的分区字段的值需要开发人员手动给定
-
- 1、创建分区表
use myhive; create table order_partition( order_number string, order_price double, order_time string ) partitioned BY(month string) row format delimited fields terminated by '\t';
-
- 2、准备数据
cd /kkb/install/hivedatas vim order.txt 10001 100 2019-03-02 10002 200 2019-03-02 10003 300 2019-03-02 10004 400 2019-03-03 10005 500 2019-03-03 10006 600 2019-03-03 10007 700 2019-03-04 10008 800 2019-03-04 10009 900 2019-03-04
-
- 3、加载数据到分区表
load data local inpath '/kkb/install/hivedatas/order.txt' overwrite into table order_partition partition(month='2019-03');
-
- 4、查询结果数据
select * from order_partition where month='2019-03'; 结果为: 10001 100.0 2019-03-02 2019-03 10002 200.0 2019-03-02 2019-03 10003 300.0 2019-03-02 2019-03 10004 400.0 2019-03-03 2019-03 10005 500.0 2019-03-03 2019-03 10006 600.0 2019-03-03 2019-03 10007 700.0 2019-03-04 2019-03 10008 800.0 2019-03-04 2019-03 10009 900.0 2019-03-04 2019-03
11.2、动态分区
按照需求实现把数据自动导入到表的不同分区中,==不需要手动指定==
- 需求:按照不同部门作为分区导数据到目标表
1、创建表
--创建普通表 create table t_order( order_number string, order_price double, order_time string )row format delimited fields terminated by '\t'; --创建目标分区表 create table order_dynamic_partition( order_number string, order_price double )partitioned BY(order_time string) row format delimited fields terminated by '\t';
2、准备数据 order_created.txt内容如下
cd /kkb/install/hivedatas vim order_partition.txt 10001 100 2019-03-02 10002 200 2019-03-02 10003 300 2019-03-02 10004 400 2019-03-03 10005 500 2019-03-03 10006 600 2019-03-03 10007 700 2019-03-04 10008 800 2019-03-04 10009 900 2019-03-04
3、向普通表t_order加载数据
load data local inpath '/kkb/install/hivedatas/order_partition.txt' overwrite into table t_order;
4、动态加载数据到分区表中
要想进行动态分区,需要设置参数 //开启动态分区功能 hive> set hive.exec.dynamic.partition=true; //设置hive为非严格模式 hive> set hive.exec.dynamic.partition.mode=nonstrict; hive> insert into table order_dynamic_partition partition(order_time) select order_number,order_price,order_time from t_order;
5、查看分区
bin/hive> show partitions order_dynamic_partition;
12、Hive的基本查询语法
12.1、基本查询
注意
-
- SQL 语言大小写不敏感
- SQL 可以写在一行或者多行
- 关键字不能被缩写也不能分行
- 各子句一般要分行写
- 使用缩进提高语句的可读性
12.1.1、全表和特定查询
全表查询
select * from stu;
查询特定列
select id,name from stu;
12.1.2、列起别名
- 重命名一个列
-
- 紧跟列名,也可以在列名和别名之间加入关键字 ‘as’
- 案例实操
select id,name as stuName from stu;
12.1.3、常用函数
1.求总行数(count)
select count(*) cnt from score;
2、求分数的最大值(max)
select max(s_score) from score;
3、求分数的最小值(min)
select min(s_score) from score;
4、求分数的总和(sum)
select sum(s_score) from score;
5、求分数的平均值(avg)
select avg(s_score) from score;
12.1.4、limit语句
典型的查询会返回多行数据。limit子句用于限制返回的行数。
select * from score limit 5;
12.1.5、where语句
- 1、使用 where 子句,将不满足条件的行过滤掉
- 2、where 子句紧随from子句
- 3、案例实操
select * from score where s_score > 60;
12.1.6、算术运算符
运算符 | 描述 |
A+B | A和B 相加 |
A-B | A减去B |
A*B | A和B 相乘 |
A/B | A除以B |
A%B | A对B取余 |
A&B | A和B按位取与 |
A|B | A和B按位取或 |
A^B | A和B按位取异或 |
~A | A按位取反 |
12.1.7、比较运算
操作符 | 支持的数据类型 | 描述 |
A=B | 基本数据类型 | 如果A等于B则返回true,反之返回false |
A<=>B | 基本数据类型 | 如果A和B都为NULL,则返回true,其他的和等号(=)操作符的结果一致,如果任一为NULL则结果为NULL |
A<>B, A!=B | 基本数据类型 | A或者B为NULL则返回NULL;如果A不等于B,则返回true,反之返回false |
A<B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A小于B,则返回true,反之返回false |
A<=B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A小于等于B,则返回true,反之返回false |
A>B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A大于B,则返回true,反之返回false |
A>=B | 基本数据类型 | A或者B为NULL,则返回NULL;如果A大于等于B,则返回true,反之返回false |
A [NOT] BETWEEN B AND C | 基本数据类型 | 如果A,B或者C任一为NULL,则结果为NULL。如果A的值大于等于B而且小于或等于C,则结果为true,反之为false。如果使用NOT关键字则可达到相反的效果。 |
A IS NULL | 所有数据类型 | 如果A等于NULL,则返回true,反之返回false |
A IS NOT NULL | 所有数据类型 | 如果A不等于NULL,则返回true,反之返回false |
IN(数值1, 数值2) | 所有数据类型 | 使用 IN运算显示列表中的值 |
A [NOT] LIKE B | STRING 类型 | B是一个SQL下的简单正则表达式,如果A与其匹配的话,则返回true;反之返回false。B的表达式说明如下:‘x%’表示A必须以字母‘x’开头,‘%x’表示A必须以字母’x’结尾,而‘%x%’表示A包含有字母’x’,可以位于开头,结尾或者字符串中间。如果使用NOT关键字则可达到相反的效果。like不是正则,而是通配符 |
A RLIKE B, A REGEXP B | STRING 类型 | B是一个正则表达式,如果A与其匹配,则返回true;反之返回false。匹配使用的是JDK中的正则表达式接口实现的,因为正则也依据其中的规则。例如,正则表达式必须和整个字符串A相匹配,而不是只需与其字符串匹配。 |
12.1.8、逻辑运算符
操作符 | 操作 | 描述 |
A AND B | 逻辑并 | 如果A和B都是true则为true,否则false |
A OR B | 逻辑或 | 如果A或B或两者都是true则为true,否则false |
NOT A | 逻辑否 | 如果A为false则为true,否则false |
12.2、分组
12.2.1、group by语句
Group By 语句通常会和==聚合函数==一起使用,按照一个或者多个列队结果进行分组,然后对每个组执行聚合操作。
- 案例实操:
-
- (1)计算每个学生的平均分数
select s_id,avg(s_score) from score group by s_id;
-
- (2)计算每个学生最高的分数
select s_id,max(s_score) from score group by s_id;
12.2.2、having语句
having 与 where 不同点
- where针对表中的列发挥作用,查询数据;having针对查询结果中的列发挥作用,筛选数据
- where后面不能写分组函数,而having后面可以使用分组函数
- having只用于group by分组统计语句
案例实操
- 求每个学生的平均分数
select s_id,avg(s_score) from score group by s_id;
- 求每个学生平均分数大于60的人
select s_id,avg(s_score) as avgScore from score group by s_id having avgScore > 60;
12.3、join语句
12.3.1、等值join
- Hive支持通常的SQL JOIN语句,但是只支持等值连接,不支持非等值连接。
- 案例实操
-
- 根据学生和成绩表,查询学生姓名对应的成绩
12.3.2、表的别名
- 好处
-
- 使用别名可以简化查询。
- 使用表名前缀可以提高执行效率。
- 案例实操
-
- 合并老师与课程表
#hive当中创建course表并加载数据 create table course (c_id string,c_name string,t_id string) row format delimited fields terminated by '\t'; load data local inpath '/kkb/install/hivedatas/course.csv' overwrite into table course; select * from teacher t join course c on t.t_id = c.t_id;
12.3.3、内连接inner join
- 内连接:只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来。
-
- join默认是inner join
- 案例实操
select * from teacher t inner join course c on t.t_id = c.t_id;
12.3.4、左外连接left outer join
- 左外连接:join操作符左边表中符合where子句的所有记录将会被返回。
- 案例实操
-
- 查询老师对应的课程
select * from teacher t left outer join course c on t.t_id = c.t_id;
12.3.5、右外连接right outer join
- 右外连接:join操作符右边表中符合where子句的所有记录将会被返回。
- 案例实操
select * from teacher t right outer join course c on t.t_id = c.t_id;
12.3.6、满外连接full outer join
- 满外连接:将会返回==所有表中==符合where语句条件的所有记录。如果任一表的指定字段没有符合条件的值的话,那么就使用null值替代。
- 案例实操
select * from teacher t full outer join course c on t.t_id = c.t_id;
12.3.7、多表连接
- 多个表使用join进行连接
- 注意:连接 n个表,至少需要n-1个连接条件。例如:连接三个表,至少需要两个连接条件。
- 案例实操
-
- 多表连接查询,查询老师对应的课程,以及对应的分数,对应的学生
select * from teacher t left join course c on t.t_id = c.t_id left join score s on c.c_id = s.c_id left join stu on s.s_id = stu.id;
12.4、排序
12.4.1、order by 全局排序
- order by 说明
-
- 全局排序,只有一个reduce
- 使用 ORDER BY 子句排序
-
-
- asc ( ascend)
-
-
-
-
- 升序 (默认)
-
-
-
-
- desc (descend)
-
-
-
-
- 降序
-
-
-
- order by 子句在select语句的结尾
- 案例实操
-
- 查询学生的成绩,并按照分数降序排列
select * from score s order by s_score desc ;
12.4.2、按照别名排序
- 按照学生分数的平均值排序
select s_id,avg(s_score) avgscore from score group by s_id order by avgscore desc;
12.4.3、每个MapReduce内部排序(sort by)局部排序
- sort by:每个reducer内部进行排序,对全局结果集来说不是排序。
-
- 1、设置reduce个数
set mapreduce.job.reduces=3;
-
- 2、查看reduce的个数
set mapreduce.job.reduces;
-
- 3、查询成绩按照成绩降序排列
select * from score s sort by s.s_score;
-
- 4、将查询结果导入到文件中(按照成绩降序排列)
insert overwrite local directory '/kkb/install/hivedatas/sort' select * from score s sort by s.s_score;
12.4.4、distribute by 分区排序
- distribute by:类似MR中partition,采集hash算法,在map端将查询的结果中hash值相同的结果分发到对应的reduce文件中。结合sort by使用。
- 注意
-
- Hive要求 distribute by 语句要写在 sort by 语句之前。
- 案例实操
-
- 先按照学生 sid 进行分区,再按照学生成绩进行排序
-
-
- 设置reduce的个数
-
set mapreduce.job.reduces=3;
-
-
- 通过distribute by 进行数据的分区,,将不同的sid 划分到对应的reduce当中去
-
insert overwrite local directory '/kkb/install/hivedatas/distribute' select * from score distribute by s_id sort by s_score;
12.4.5、cluster by
- 当distribute by和sort by字段相同时,可以使用cluster by方式
- 除了distribute by 的功能外,还会对该字段进行排序,所以cluster by = distribute by + sort by
--以下两种写法等价 insert overwrite local directory '/kkb/install/hivedatas/distribute_sort' select * from score distribute by s_score sort by s_score; insert overwrite local directory '/kkb/install/hivedatas/cluster' select * from score cluster by s_score;
13、hive客户端jdbc操作
13.1、启动hiveserver2的服务端
node03执行以下命令启动hiveserver2的服务端
cd /kkb/install/hive-1.1.0-cdh5.14.2/ nohup bin/hive --service hiveserver2 2>&1 &
13.2、引入依赖
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.1.0-cdh5.14.2</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>1.1.0-cdh5.14.2</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-cli</artifactId>
<version>1.1.0-cdh5.14.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0-cdh5.14.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<!-- <verbal>true</verbal>-->
</configuration>
</plugin>
</plugins>
</build>
13.3、代码开发
import java.sql.*;
public class HiveJDBC {
private static String url="jdbc:hive2://192.168.52.120:10000/myhive";
public static void main(String[] args) throws Exception {
Class.forName("org.apache.hive.jdbc.HiveDriver");
//获取数据库连接
Connection connection = DriverManager.getConnection(url, "hadoop","");
//定义查询的sql语句
String sql="select * from stu";
try {
PreparedStatement ps = connection.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()){
//获取id字段值
int id = rs.getInt(1);
//获取deptid字段
String name = rs.getString(2);
System.out.println(id+"\t"+name);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
14、hive的可视化工具dbeaver介绍以及使用
14.1、dbeaver的基本介绍
dbeaver是一个图形化的界面工具,专门用于与各种数据库的集成,通过dbeaver我们可以与各种数据库进行集成通过图形化界面的方式来操作我们的数据库与数据库表,类似于我们的sqlyog或者navicate
14.2、dbeaver的下载安装
https://github.com/dbeaver/dbeaver/releases
14.3、dbeaver的安装与使用
使用的版本是6.15这个版本,下载zip的压缩包,直接解压就可以使用,然后双击dbeaver.exe即可启动
- 第一步:双击dbeaver.exe然后启动dbeaver图形化界面
- 第二步:配置我们的主机名与端口号
15、Hive的参数传递
15.1、hive命令行
语法结构
hive [-hiveconf x=y]* [<-i filename>]* [<-f filename>|<-e query-string>] [-S]
说明:
1、 -i 从文件初始化HQL。
2、 -e从命令行执行指定的HQL
3、 -f 执行HQL脚本
4、 -v 输出执行的HQL语句到控制台
5、 -p <port> connect to Hive Server on port number
6、 -hiveconf x=y Use this to set hive/hadoop configuration variables. 设置hive运行时候的参数配置
15.2、hive参数配置方式
Hive参数大全:
https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties
开发Hive应用时,不可避免地需要设定Hive的参数。设定Hive的参数可以调优HQL代码的执行效率,或帮助定位问题。然而实践中经常遇到的一个问题是,为什么设定的参数没有起作用?这通常是错误的设定方式导致的。
对于一般参数,有以下三种设定方式:
配置文件 hive-site.xml 命令行参数 启动hive客户端的时候可以设置参数 参数声明 进入客户端以后设置的一些参数 set
配置文件:Hive的配置文件包括
用户自定义配置文件:$HIVE_CONF_DIR/hive-site.xml
默认配置文件:$HIVE_CONF_DIR/hive-default.xml
用户自定义配置会覆盖默认配置。
另外,Hive也会读入Hadoop的配置,因为Hive是作为Hadoop的客户端启动的,Hive的配置会覆盖Hadoop的配置。配置文件的设定对本机启动的所有Hive进程都有效。
命令行参数:启动Hive(客户端或Server方式)时,可以在命令行添加-hiveconf param=value来设定参数,例如:
bin/hive -hiveconf hive.root.logger=INFO,console
这一设定对本次启动的Session(对于Server方式启动,则是所有请求的Sessions)有效。
参数声明:可以在HQL中使用SET关键字设定参数,例如:
set mapred.reduce.tasks=100;
这一设定的作用域也是session级的。
上述三种设定方式的优先级依次递增。即参数声明覆盖命令行参数,命令行参数覆盖配置文件设定。注意某些系统级的参数,例如log4j相关的设定,必须用前两种方式设定,因为那些参数的读取在Session建立以前已经完成了。
参数声明 > 命令行参数 > 配置文件参数(hive)
15.3、使用变量传递参数
实际工作当中,我们一般都是将hive的hql语法开发完成之后,就写入到一个脚本里面去,然后定时的通过命令 hive -f 去执行hive的语法即可,然后通过定义变量来传递参数到hive的脚本当中去,那么我们接下来就来看看如何使用hive来传递参数。
hive0.9以及之前的版本是不支持传参的 hive1.0版本之后支持 hive -f 传递参数
在hive当中我们一般可以使用hivevar或者hiveconf来进行参数的传递
hiveconf使用说明
hiveconf用于定义HIVE执行上下文的属性(配置参数),可覆盖覆盖hive-site.xml(hive-default.xml)中的参数值,如用户执行目录、日志打印级别、执行队列等。例如我们可以使用hiveconf来覆盖我们的hive属性配置,
hiveconf变量取值必须要使用hiveconf作为前缀参数,具体格式如下:
${hiveconf:key} bin/hive --hiveconf "mapred.job.queue.name=root.default"
hivevar使用说明
hivevar用于定义HIVE运行时的变量替换,类似于JAVA中的“PreparedStatement”,与${key}配合使用或者与 ${hivevar:key}
对于hivevar取值可以不使用前缀hivevar,具体格式如下:
使用前缀: ${hivevar:key} 不使用前缀: ${key} --hivevar name=zhangsan ${hivevar:name} 也可以这样取值 ${name}
define使用说明
define与hivevar用途完全一样,还有一种简写“-d bin/hive --hiveconf "mapred.job.queue.name=root.default" -d my="201809" --database mydb 执行SQL select * from mydb where concat(year, month) = ${my} limit 10;
hiveconf与hivevar使用实战
需求:hive当中执行以下hql语句,并将参数全部都传递进去
select * from student left join score on student.s_id = score.s_id where score.month = '201807' and score.s_score > 80 and score.c_id = 03;
- 第一步:创建student表并加载数据
hive (myhive)> create external table student (s_id string,s_name string,s_birth string , s_sex string ) row format delimited fields terminated by '\t'; hive (myhive)> load data local inpath '/kkb/install/hivedatas/student.csv' overwrite into table student;
- 第二步:定义hive脚本
开发hql脚本,并使用hiveconf和hivevar进行参数穿肚
node03执行以下命令定义hql脚本
cd /kkb/instal/hivedatas vim hivevariable.hql use myhive; select * from student left join score on student.s_id = score.s_id where score.month = ${hiveconf:month} and score.s_score > ${hivevar:s_score} and score.c_id = ${c_id};
- 第三步:调用hive脚本并传递参数
node03执行以下命令并
[root@node03 hive-1.1.0-cdh5.14.2]# bin/hive --hiveconf month=201807 --hivevar s_score=80 --hivevar c_id=03 -f /kkb/install/hivedatas/hivevariable.hql
16、Hive的常用函数
系统内置函数
1.查看系统自带的函数 hive> show functions; 2.显示自带的函数的用法 hive> desc function upper; 3.详细显示自带的函数的用法 hive> desc function extended upper;
16.1、数值计算
16.1.1、取整函数:round
语法: round(double a)
返回值: BIGINT
说明: 返回double类型的整数值部分 (遵循四舍五入)
hive> select round(3.1415926) from tableName; 3 hive> select round(3.5) from tableName; 4 hive> create table tableName as select round(9542.158) from tableName;
16.1.2、指定精度取整函数:round
语法: round(double a, int d)
返回值: DOUBLE
说明: 返回指定精度d的double类型
hive> select round(3.1415926,4) from tableName; 3.1416
16.1.3、向下取整函数:floor
语法: floor(double a)
返回值: BIGINT
说明: 返回等于或者小于该double变量的最大的整数
hive> select floor(3.1415926) from tableName; 3 hive> select floor(25) from tableName; 25
16.1.4、向上取整函数:ceil
语法: ceil(double a)
返回值: BIGINT
说明: 返回等于或者大于该double变量的最小的整数
hive> select ceil(3.1415926) from tableName; 4 hive> select ceil(46) from tableName; 46
16.1.5、向上取整函数:ceiling
语法: ceiling(double a)
返回值: BIGINT
说明: 与ceil功能相同
hive> select ceiling(3.1415926) from tableName; 4 hive> select ceiling(46) from tableName; 46
16.1.6、取随机数函数:rand
语法: rand(),rand(int seed)
返回值: double
说明: 返回一个0到1范围内的随机数。如果指定种子seed,则会等到一个稳定的随机数序列
hive> select rand() from tableName; 0.5577432776034763 hive> select rand() from tableName; 0.6638336467363424 hive> select rand(100) from tableName; 0.7220096548596434 hive> select rand(100) from tableName; 0.7220096548596434
16.2、日期函数
16.2.1、unix时间戳转日期函数:from_unixtime
语法: from_unixtime(bigint unixtime[, string format])
返回值: string
说明: 转化UNIX时间戳(从1970-01-01 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式
hive> select from_unixtime(1323308943,'yyyyMMdd') from tableName; 20111208
16.2.2、获取当前unix时间戳函数:unix_timestamp
语法: unix_timestamp()
返回值: bigint
说明: 获得当前时区的UNIX时间戳
hive> select unix_timestamp() from tableName; 1323309615
16.2.3、日期转unix时间戳函数:unix_timestamp
语法: unix_timestamp(string date)
返回值: bigint
说明: 转换格式为"yyyy-MM-dd HH:mm:ss"的日期到UNIX时间戳。如果转化失败,则返回0。
hive> select unix_timestamp('2011-12-07 13:01:03') from tableName; 1323234063
16.2.4、指定格式日期转unix时间戳函数:unix_timestamp
语法: unix_timestamp(string date, string pattern)
返回值: bigint
说明: 转换pattern格式的日期到UNIX时间戳。如果转化失败,则返回0。
hive> select unix_timestamp('20111207 13:01:03','yyyyMMdd HH:mm:ss') from tableName; 1323234063
16.2.5、日期时间转日期函数:to_date
语法: to_date(string timestamp)
返回值: string
说明: 返回日期时间字段中的日期部分。
hive> select to_date('2011-12-08 10:03:01') from tableName; 2011-12-08
16.2.6、日期转年函数:year
语法: year(string date)
返回值: int
说明: 返回日期中的年。
hive> select year('2011-12-08 10:03:01') from tableName; 2011 hive> select year('2012-12-08') from tableName; 2012
16.2.7、日期转月函数:month
语法: month (string date)
返回值: int
说明: 返回日期中的月份。
hive> select month('2011-12-08 10:03:01') from tableName; 12 hive> select month('2011-08-08') from tableName; 8
16.2.8、日期转天函数:day
语法: day (string date)
返回值: int
说明: 返回日期中的天。
hive> select day('2011-12-08 10:03:01') from tableName; 8 hive> select day('2011-12-24') from tableName; 24
16.2.9、日期转小时函数:hour
语法: hour (string date)
返回值: int
说明: 返回日期中的小时。
hive> select hour('2011-12-08 10:03:01') from tableName; 10
16.2.10、日期转分钟函数:minute
语法: minute (string date)
返回值: int
说明: 返回日期中的分钟。
hive> select minute('2011-12-08 10:03:01') from tableName; 3 hive> select second('2011-12-08 10:03:01') from tableName; 1
16.2.11、日期转周函数:weekofyear
语法: weekofyear (string date)
返回值: int
说明: 返回日期在当前的周数。
16.2.12、日期比较函数:datediff
语法: datediff(string enddate, string startdate)
返回值: int
说明: 返回结束日期减去开始日期的天数。
hive> select datediff('2012-12-08','2012-05-09') from tableName; 213
16.2.13、日期增加函数:date_add
语法: date_add(string startdate, int days)
返回值: string
说明: 返回开始日期startdate增加days天后的日期。
hive> select date_add('2012-12-08',10) from tableName; 2012-12-18
16.2.14、日期减少函数:date_sub
语法: date_sub (string startdate, int days)
返回值: string
说明: 返回开始日期startdate减少days天后的日期。
hive> select date_sub('2012-12-08',10) from tableName; 2012-11-28
16.3、条件函数
16.3.1、if函数:if
语法: if(boolean testCondition, T valueTrue, T valueFalseOrNull)
返回值: T
说明: 当条件testCondition为TRUE时,返回valueTrue;否则返回valueFalseOrNull
hive> select if(1=2,100,200) from tableName; 200 hive> select if(1=1,100,200) from tableName; 100
16.3.2、非空查找函数:coalesce
语法: COALESCE(T v1, T v2, …)
返回值: T
说明: 返回参数中的第一个非空值;如果所有值都为NULL,那么返回NULL
hive> select COALESCE(null,'100','50') from tableName; 100
16.3.3、条件判断函数:case
语法: CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END
返回值: T
说明:如果a等于b,那么返回c;如果a等于d,那么返回e;否则返回f
hive> Select case 100 when 50 then 'tom' when 100 then 'mary' else 'tim' end from tableName; mary hive> Select case 200 when 50 then 'tom' when 100 then 'mary' else 'tim' end from tableName; tim
16.3.4、条件判断函数:case
语法: CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END
返回值: T
说明:如果a为TRUE,则返回b;如果c为TRUE,则返回d;否则返回e
hive> select case when 1=2 then 'tom' when 2=2 then 'mary' else 'tim' end from tableName; mary hive> select case when 1=1 then 'tom' when 2=2 then 'mary' else 'tim' end from tableName; tom
case...when...例子
#创建表 create table employee( empid int, deptid int, sex string, salary double )row format delimited fields terminated by ' '; # employee.txt 数据文件 1 10 female 5500.0 2 10 male 4500.0 3 20 female 1900.0 4 20 male 4800.0 5 30 female 6500.0 6 30 female 14500.0 7 30 male 44500.0 8 40 male 6500.0 9 40 male 7500.0 #表结构 # desc employee; empid int deptid int sex string salary double #表记录 select * from employee; 1 10 female 5500.0 2 10 male 4500.0 3 20 female 1900.0 4 20 male 4800.0 5 40 female 6500.0 6 40 female 14500.0 7 40 male 44500.0 8 50 male 6500.0 9 50 male 7500.0 ################### 将员工按照薪资待遇划分等级 薪水小于五千的,打上低等收入的标签,收入在5000到10000打上中等收入标签,收入大于10000打上高等收入标签 ################### ################### 将员工按照性别打上标识 ################### select *, case when salary < 5000 then "低等收入" when salary>= 5000 and salary < 10000 then "中等收入" when salary > 10000 then "高等收入" end as level, case sex when "female" then 1 when "male" then 0 end as flag from employee; #统计结果 1 10 female 5500.0 中等收入 1 2 10 male 4500.0 低等收入 0 3 20 female 1900.0 低等收入 1 4 20 male 4800.0 低等收入 0 5 40 female 6500.0 中等收入 1 6 40 female 14500.0 高等收入 1 7 40 male 44500.0 高等收入 0 8 50 male 6500.0 中等收入 0 9 50 male 7500.0 中等收入 0
hive什么情况下可避免进行mapreduce
hive 为了执行效率考虑,简单的查询,就是只是select,不带count,sum,group by这样的,都不走map/reduce,直接读取hdfs目录中的文件进行filter过滤。==也就是所谓的本地模式==。
- 1、直接查询表的数据 不会进行Mapreduce
select * from employee;
- 2、查询语句中的过滤条件只是分区字段的情况下 不会进行Mapreduce。
select * from order_partition where month='2019-03';
- 此外,如果设置属性 set hive.exec.mode.local.auto=true; hive还是会尝试使用本地模式。
16.4、字符串函数
16.4.1、字符串长度函数:length
语法: length(string A)
返回值: int
说明:返回字符串A的长度
hive> select length('abcedfg') from tableName;
16.4.2、字符串反转函数:reverse
语法: reverse(string A)
返回值: string
说明:返回字符串A的反转结果
hive> select reverse('abcedfg') from tableName; gfdecba
16.4.3、字符串连接函数:concat
语法: concat(string A, string B…)
返回值: string
说明:返回输入字符串连接后的结果,支持任意个输入字符串
hive> select concat('abc','def','gh') from tableName; abcdefgh
16.4.4、字符串连接并指定字符串分隔符:concat_ws
语法: concat_ws(string SEP, string A, string B…)
返回值: string
说明:返回输入字符串连接后的结果,SEP表示各个字符串间的分隔符
hive> select concat_ws(',','abc','def','gh')from tableName; abc,def,gh
16.4.5、字符串截取函数:substr
语法: substr(string A, int start),substring(string A, int start)
返回值: string
说明:返回字符串A从start位置到结尾的字符串
hive> select substr('abcde',3) from tableName; cde hive> select substring('abcde',3) from tableName; cde hive> select substr('abcde',-1) from tableName; (和ORACLE相同) e
16.4.6、字符串截取函数:substr、substring
语法: substr(string A, int start, int len),substring(string A, int start, int len)
返回值: string
说明:返回字符串A从start位置开始,长度为len的字符串
hive> select substr('abcde',3,2) from tableName; cd hive> select substring('abcde',3,2) from tableName; cd hive>select substring('abcde',-2,2) from tableName; de
16.4.7、字符串转大写函数:upper、ucase
语法: upper(string A) ucase(string A)
返回值: string
说明:返回字符串A的大写格式
hive> select upper('abSEd') from tableName; ABSED hive> select ucase('abSEd') from tableName; ABSED
16.4.8、字符串转小写函数:lower、lcase
语法: lower(string A) lcase(string A)
返回值: string
说明:返回字符串A的小写格式
hive> select lower('abSEd') from tableName; absed hive> select lcase('abSEd') from tableName; absed
16.4.9、去空格函数:trim
语法: trim(string A)
返回值: string
说明:去除字符串两边的空格
hive> select trim(' abc ') from tableName; abc
16.4.10、url解析函数:parse_url
语法:parse_url(string urlString, string partToExtract [, string keyToExtract])
返回值: string
说明:返回URL中指定的部分。partToExtract的有效值为:HOST, PATH,QUERY, REF, PROTOCOL, AUTHORITY, FILE, and USERINFO.
hive> select parse_url ('https://www.tableName.com/path1/p.php?k1=v1&k2=v2#Ref1', 'HOST') from tableName; www.tableName.com hive> select parse_url ('https://www.tableName.com/path1/p.php?k1=v1&k2=v2#Ref1', 'QUERY', 'k1') from tableName; v1
16.4.11、json解析函数:get_json_object
语法: get_json_object(string json_string, string path)
返回值: string
说明:解析json的字符串json_string,返回path指定的内容。如果输入的json字符串无效,那么返回NULL。
hive> select get_json_object('{"store":{"fruit":\[{"weight":8,"type":"apple"},{"weight":9,"type":"pear"}], "bicycle":{"price":19.95,"color":"red"} },"email":"amy@only_for_json_udf_test.net","owner":"amy"}','$.owner') from tableName;
16.4.12、重复字符串函数:repeat
语法: repeat(string str, int n)
返回值: string
说明:返回重复n次后的str字符串
hive> select repeat('abc',5) from tableName; abcabcabcabcabc
16.4.13、分割字符串函数:split
语法: split(string str, string pat)
返回值: array
说明: 按照pat字符串分割str,会返回分割后的字符串数组
hive> select split('abtcdtef','t') from tableName; ["ab","cd","ef"]
16.5、集合函数统计
16.5.1、个数统计函数:count
语法: count(*), count(expr), count(DISTINCT expr[, expr_.])
返回值:Int
说明: count(*)统计检索出的行的个数,包括NULL值的行;count(expr)返回指定字段的非空值的个数;count(DISTINCTexpr[, expr_.])返回指定字段的不同的非空值的个数
hive> select count(*) from tableName; 20 hive> select count(distinct t) from tableName; 10
16.5.2、总和统计函数:sum
语法: sum(col), sum(DISTINCT col)
返回值: double
说明: sum(col)统计结果集中col的相加的结果;sum(DISTINCT col)统计结果中col不同值相加的结果
hive> select sum(t) from tableName; 100 hive> select sum(distinct t) from tableName; 70
16.5.3、平均值统计函数:avg
语法: avg(col), avg(DISTINCT col)
返回值: double
说明: avg(col)统计结果集中col的平均值;avg(DISTINCT col)统计结果中col不同值相加的平均值
hive> select avg(t) from tableName; 50 hive> select avg (distinct t) from tableName; 30
16.5.4、最小值统计函数:mix
语法: min(col)
返回值: double
说明: 统计结果集中col字段的最小值
hive> select min(t) from tableName; 20
16.5.5、最大值统计函数:max
语法: maxcol)
返回值: double
说明: 统计结果集中col字段的最大值
hive> select max(t) from tableName; 120
16.6、复合类型构建函数
16.6.1、map类型构建:map
语法: map (key1, value1, key2, value2, …)
说明:根据输入的key和value对构建map类型
create table score_map(name string, score map<string,int>) row format delimited fields terminated by '\t' collection items terminated by ',' map keys terminated by ':'; 创建数据内容如下并加载数据 cd /kkb/install/hivedatas/ vim score_map.txt zhangsan 数学:80,语文:89,英语:95 lisi 语文:60,数学:80,英语:99 加载数据到hive表当中去 load data local inpath '/kkb/install/hivedatas/score_map.txt' overwrite into table score_map; map结构数据访问: 获取所有的value: select name,map_values(score) from score_map; 获取所有的key: select name,map_keys(score) from score_map; 按照key来进行获取value值 select name,score["数学"] from score_map; 查看map元素个数 select name,size(score) from score_map;
16.6.2、struct类型构建:struck
语法: struct(val1, val2, val3, …)
说明:根据输入的参数构建结构体struct类型,似于C语言中的结构体,内部数据通过X.X来获取,假设我们的数据格式是这样的,电影ABC,有1254人评价过,打分为7.4分
创建struct表 hive> create table movie_score( name string, info struct<number:int,score:float> )row format delimited fields terminated by "\t" collection items terminated by ":"; 加载数据 cd /kkb/install/hivedatas/ vim struct.txt ABC 1254:7.4 DEF 256:4.9 XYZ 456:5.4 加载数据 load data local inpath '/kkb/install/hivedatas/struct.txt' overwrite into table movie_score; hive当中查询数据 hive> select * from movie_score; hive> select info.number,info.score from movie_score; OK 1254 7.4 256 4.9 456 5.4
16.6.3、array类型构建:array
语法: array(val1, val2, …)
说明:根据输入的参数构建数组array类型
hive> create table person(name string,work_locations array<string>) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' COLLECTION ITEMS TERMINATED BY ','; 加载数据到person表当中去 cd /kkb/install/hivedatas/ vim person.txt 数据内容格式如下 biansutao beijing,shanghai,tianjin,hangzhou linan changchu,chengdu,wuhan 加载数据 hive > load data local inpath '/kkb/install/hivedatas/person.txt' overwrite into table person; 查询所有数据数据 hive > select * from person; 按照下表索引进行查询 hive > select work_locations[0] from person; 查询所有集合数据 hive > select work_locations from person; 查询元素个数 hive > select size(work_locations) from person;
16.7、复杂类型长度统计函数
16.7.1、map类型长度函数:size(map<k,v>)
语法: size(Map<k .V>)
返回值: int
说明: 返回map类型的长度
hive> select size(t) from map_table2; 2
16.7.2、array类型长度函数:size(arry<T>)
语法: size(Array<T>)
返回值: int
说明: 返回array类型的长度
hive> select size(t) from arr_table2; 4
16.7.3、类型转换函数
类型转换函数: cast
语法: cast(expr as <type>)
返回值: Expected "=" to follow "type"
说明: 返回转换后的数据类型
hive> select cast('1' as bigint) from tableName; 1
16.8、hive当中的lateral view 与 explode以及reflect和分析函数
16.8.1、使用explode函数将hive表中的Map和Array字段数据进行拆分
lateral view用于和split、explode等UDTF一起使用的,能将一行数据拆分成多行数据,在此基础上可以对拆分的数据进行聚合,lateral view首先为原始表的每行调用UDTF,UDTF会把一行拆分成一行或者多行,lateral view在把结果组合,产生一个支持别名表的虚拟表。其中explode还可以用于将hive一列中复杂的array或者map结构拆分成多行
需求:现在有数据格式如下 zhangsan child1,child2,child3,child4 k1:v1,k2:v2 lisi child5,child6,child7,child8 k3:v3,k4:v4 字段之间使用\t分割,需求将所有的child进行拆开成为一列 +----------+--+ | mychild | +----------+--+ | child1 | | child2 | | child3 | | child4 | | child5 | | child6 | | child7 | | child8 | +----------+--+ 将map的key和value也进行拆开,成为如下结果 +-----------+-------------+--+ | mymapkey | mymapvalue | +-----------+-------------+--+ | k1 | v1 | | k2 | v2 | | k3 | v3 | | k4 | v4 | +-----------+-------------+--+
- 第一步:创建hive数据库
创建hive数据库
hive (default)> create database hive_explode; hive (default)> use hive_explode;
- 第二步:创建hive表,然后使用explode拆分map和array
hive (hive_explode)> create table hive_explode.t3(name string,children array<string>,address Map<string,string>) row format delimited fields terminated by '\t' collection items terminated by ',' map keys terminated by ':' stored as textFile;
- 第三步:加载数据
node03执行以下命令创建表数据文件
cd /kkb/install/hivedatas/ vim maparray 数据内容格式如下 zhangsan child1,child2,child3,child4 k1:v1,k2:v2 lisi child5,child6,child7,child8 k3:v3,k4:v4
hive表当中加载数据
hive (hive_explode)> load data local inpath '/kkb/install/hivedatas/maparray' into table hive_explode.t3;
- 第四步:使用explode将hive当中数据拆开
将array当中的数据拆分开
hive (hive_explode)> SELECT explode(children) AS myChild FROM hive_explode.t3;
将map当中的数据拆分开
hive (hive_explode)> SELECT explode(address) AS (myMapKey, myMapValue) FROM hive_explode.t3;
16.8.2、使用explode拆分json字符串
需求:现在有一些数据格式如下:
a:shandong,b:beijing,c:hebei|1,2,3,4,5,6,7,8,9|[{"source":"7fresh","monthSales":4900,"userCount":1900,"score":"9.9"},{"source":"jd","monthSales":2090,"userCount":78981,"score":"9.8"},{"source":"jdmart","monthSales":6987,"userCount":1600,"score":"9.0"}]
其中字段与字段之间的分隔符是 |
我们要解析得到所有的monthSales对应的值为以下这一列(行转列)
4900 2090 6987
- 第一步:创建hive表
hive (hive_explode)> create table hive_explode.explode_lateral_view (area string, goods_id string, sale_info string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' STORED AS textfile;
- 第二步:准备数据并加载数据
准备数据如下
cd /kkb/install/hivedatas vim explode_json a:shandong,b:beijing,c:hebei|1,2,3,4,5,6,7,8,9|[{"source":"7fresh","monthSales":4900,"userCount":1900,"score":"9.9"},{"source":"jd","monthSales":2090,"userCount":78981,"score":"9.8"},{"source":"jdmart","monthSales":6987,"userCount":1600,"score":"9.0"}]
加载数据到hive表当中去
hive (hive_explode)> load data local inpath '/kkb/install/hivedatas/explode_json' overwrite into table hive_explode.explode_lateral_view;
- 第三步:使用explode拆分Array
hive (hive_explode)> select explode(split(goods_id,',')) as goods_id from hive_explode.explode_lateral_view;
- 第四步:使用explode拆解Map
hive (hive_explode)> select explode(split(area,',')) as area from hive_explode.explode_lateral_view;
- 第五步:拆解json字段
hive (hive_explode)> select explode(split(regexp_replace(regexp_replace(sale_info,'\\[\\{',''),'}]',''),'},\\{')) as sale_info from hive_explode.explode_lateral_view;
然后我们想用get_json_object来获取key为monthSales的数据:
hive (hive_explode)> select get_json_object(explode(split(regexp_replace(regexp_replace(sale_info,'\\[\\{',''),'}]',''),'},\\{')),'$.monthSales') as sale_info from hive_explode.explode_lateral_view; 然后出现异常FAILED: SemanticException [Error 10081]: UDTF's are not supported outside the SELECT clause, nor nested in expressions UDTF explode不能写在别的函数内 如果你这么写,想查两个字段,select explode(split(area,',')) as area,good_id from explode_lateral_view; 会报错FAILED: SemanticException 1:40 Only a single expression in the SELECT clause is supported with UDTF's. Error encountered near token 'good_id' 使用UDTF的时候,只支持一个字段,这时候就需要LATERAL VIEW出场了
16.8.3、配合LATERAL VIEW使用
配合lateral view查询多个字段
hive (hive_explode)> select goods_id2,sale_info from explode_lateral_view LATERAL VIEW explode(split(goods_id,','))goods as goods_id2;
其中LATERAL VIEW explode(split(goods_id,','))goods相当于一个虚拟表,与原表explode_lateral_view笛卡尔积关联。
也可以多重使用
hive (hive_explode)> select goods_id2,sale_info,area2 from explode_lateral_view LATERAL VIEW explode(split(goods_id,','))goods as goods_id2 LATERAL VIEW explode(split(area,','))area as area2;
也是三个表笛卡尔积的结果
最终,我们可以通过下面的句子,把这个json格式的一行数据,完全转换成二维表的方式展现
hive (hive_explode)> select get_json_object(concat('{',sale_info_1,'}'),'$.source') as source, get_json_object(concat('{',sale_info_1,'}'),'$.monthSales') as monthSales, get_json_object(concat('{',sale_info_1,'}'),'$.userCount') as monthSales, get_json_object(concat('{',sale_info_1,'}'),'$.score') as monthSales from explode_lateral_view LATERAL VIEW explode(split(regexp_replace(regexp_replace(sale_info,'\\[\\{',''),'}]',''),'},\\{'))sale_info as sale_info_1;
总结:
Lateral View通常和UDTF一起出现,为了解决UDTF不允许在select字段的问题。
Multiple Lateral View可以实现类似笛卡尔乘积。
Outer关键字可以把不输出的UDTF的空结果,输出成NULL,防止丢失数据。
16.9、列转行
16.9.1、相关函数介绍
CONCAT(string A/col, string B/col…):返回输入字符串连接后的结果,支持任意个输入字符串;
CONCAT_WS(separator, str1, str2,...):它是一个特殊形式的 CONCAT()。第一个参数剩余参数间的分隔符。分隔符可以是与剩余参数一样的字符串。如果分隔符是 NULL,返回值也将为 NULL。这个函数会跳过分隔符参数后的任何 NULL 和空字符串。分隔符将被加到被连接的字符串之间;
COLLECT_SET(col):函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生array类型字段。
16.9.2、数据准备
name | constellation | blood_type |
孙悟空 | 白羊座 | A |
老王 | 射手座 | A |
宋宋 | 白羊座 | B |
猪八戒 | 白羊座 | A |
冰冰 | 射手座 | A |
16.9.3、需求
把星座和血型一样的人归类到一起。结果如下:
射手座,A 老王|冰冰 白羊座,A 孙悟空|猪八戒 白羊座,B 宋宋
16.9.4、创建本地constellation.txt,导入数据
node03服务器执行以下命令创建文件,注意数据使用\t进行分割
cd /kkb/install/hivedatas vim constellation.txt 孙悟空 白羊座 A 老王 射手座 A 宋宋 白羊座 B 猪八戒 白羊座 A 凤姐 射手座 A
16.9.5、创建hive表并导入数据
hive (hive_explode)> create table person_info( name string, constellation string, blood_type string) row format delimited fields terminated by "\t"; hive (hive_explode)> load data local inpath '/kkb/install/hivedatas/constellation.txt' into table person_info;
16.9.6、按需求查询数据
hive (hive_explode)> select t1.base, concat_ws('|', collect_set(t1.name)) name from (select name, concat(constellation, "," , blood_type) base from person_info) t1 group by t1.base;
16.10、行转列
16.10.1、函数说明
EXPLODE(col):将hive一列中复杂的array或者map结构拆分成多行。
LATERAL VIEW
用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias
解释:用于和split, explode等UDTF一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。
16.10.2、数据准备
数据内容如下,字段之间都是使用\t进行分割
cd /kkb/install/hivedatas vim movie.txt 《疑犯追踪》 悬疑,动作,科幻,剧情 《Lie to me》 悬疑,警匪,动作,心理,剧情 《战狼2》 战争,动作,灾难
16.10.3、需求
将电影分类中的数组数据展开。结果如下:
《疑犯追踪》 悬疑 《疑犯追踪》 动作 《疑犯追踪》 科幻 《疑犯追踪》 剧情 《Lie to me》 悬疑 《Lie to me》 警匪 《Lie to me》 动作 《Lie to me》 心理 《Lie to me》 剧情 《战狼2》 战争 《战狼2》 动作 《战狼2》 灾难
16.10.4、创建hive表并导入数据
hive (hive_explode)> create table movie_info(movie string, category array<string>) row format delimited fields terminated by "\t" collection items terminated by ","; load data local inpath "/kkb/install/hivedatas/movie.txt" into table movie_info;
16.10.5、按需求查询数据
hive (hive_explode)> select movie, category_name from movie_info lateral view explode(category) table_tmp as category_name;
16.11、reflect函数
reflect函数可以支持在sql中调用java中的自带函数,秒杀一切udf函数。
使用java.lang.Math当中的Max求两列中最大值
创建hive表
hive (hive_explode)> create table test_udf(col1 int,col2 int) row format delimited fields terminated by ',';
准备数据并加载数据
cd /kkb/install/hivedatas vim test_udf 1,2 4,3 6,4 7,5 5,6
加载数据
hive (hive_explode)> load data local inpath '/kkb/install/hivedatas/test_udf' overwrite into table test_udf;
使用java.lang.Math当中的Max求两列当中的最大值
hive (hive_explode)> select reflect("java.lang.Math","max",col1,col2) from test_udf;
不同记录执行不同的java内置函数
创建hive表
hive (hive_explode)> create table test_udf2(class_name string,method_name string,col1 int , col2 int) row format delimited fields terminated by ',';
准备数据
cd /export/servers/hivedatas vim test_udf2 java.lang.Math,min,1,2 java.lang.Math,max,2,3
加载数据
hive (hive_explode)> load data local inpath '/kkb/install/hivedatas/test_udf2' overwrite into table test_udf2;
执行查询
hive (hive_explode)> select reflect(class_name,method_name,col1,col2) from test_udf2;
判断是否为数字
使用apache commons中的函数,commons下的jar已经包含在hadoop的classpath中,所以可以直接使用。
使用方式如下:
hive (hive_explode)> select reflect("org.apache.commons.lang.math.NumberUtils","isNumber","123");
16.12、hive当中的分析函数-分组求topN
16.12.1、分析函数的作用介绍
对于一些比较复杂的数据求取过程,我们可能就要用到分析函数,分析函数主要用于分组求topN,或者求取百分比,或者进行数据的切片等等,我们都可以使用分析函数来解决
16.12.2、常用的分析函数介绍
1、ROW_NUMBER():
从1开始,按照顺序,生成分组内记录的序列,比如,按照pv降序排列,生成分组内每天的pv名次,ROW_NUMBER()的应用场景非常多,再比如,获取分组内排序第一的记录;获取一个session中的第一条refer等。
2、RANK() :
生成数据项在分组中的排名,排名相等会在名次中留下空位
3、DENSE_RANK() :
生成数据项在分组中的排名,排名相等会在名次中不会留下空位
4、CUME_DIST :
小于等于当前值的行数/分组内总行数。比如,统计小于等于当前薪水的人数,所占总人数的比例
5、PERCENT_RANK :
分组内当前行的RANK值/分组内总行数
6、NTILE(n) :
用于将分组数据按照顺序切分成n片,返回当前切片值,如果切片不均匀,默认增加第一个切片的分布。NTILE不支持ROWS BETWEEN,比如 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)。
16.12.3、需求描述
现有数据内容格式如下,分别对应三个字段,cookieid,createtime ,pv,求取每个cookie访问pv前三名的数据记录,其实就是分组求topN,求取每组当中的前三个值
cookie1,2015-04-10,1 cookie1,2015-04-11,5 cookie1,2015-04-12,7 cookie1,2015-04-13,3 cookie1,2015-04-14,2 cookie1,2015-04-15,4 cookie1,2015-04-16,4 cookie2,2015-04-10,2 cookie2,2015-04-11,3 cookie2,2015-04-12,5 cookie2,2015-04-13,6 cookie2,2015-04-14,3 cookie2,2015-04-15,9 cookie2,2015-04-16,7
- 第一步:创建数据库表
在hive当中创建数据库表
CREATE EXTERNAL TABLE cookie_pv ( cookieid string, createtime string, pv INT ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ;
- 第二步:准备数据并加载
node03执行以下命令,创建数据,并加载到hive表当中去
cd /kkb/install/hivedatas vim cookiepv.txt cookie1,2015-04-10,1 cookie1,2015-04-11,5 cookie1,2015-04-12,7 cookie1,2015-04-13,3 cookie1,2015-04-14,2 cookie1,2015-04-15,4 cookie1,2015-04-16,4 cookie2,2015-04-10,2 cookie2,2015-04-11,3 cookie2,2015-04-12,5 cookie2,2015-04-13,6 cookie2,2015-04-14,3 cookie2,2015-04-15,9 cookie2,2015-04-16,7
加载数据到hive表当中去
load data local inpath '/kkb/install/hivedatas/cookiepv.txt' overwrite into table cookie_pv
- 第三步:使用分析函数来求取每个cookie访问PV的前三条记录
SELECT cookieid, createtime, pv, RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1, DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2, ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3 FROM cookie_pv WHERE rn1 <= 3 ;
16.13、hive自定义函数
16.13.1、自定义函数的基本介绍
1)Hive 自带了一些函数,比如:max/min等,但是数量有限,自己可以通过自定义UDF来方便的扩展。
2)当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。
3)根据用户自定义函数类别分为以下三种:
(1)UDF(User-Defined-Function)
一进一出
(2)UDAF(User-Defined Aggregation Function)
聚集函数,多进一出
类似于:count/max/min
(3)UDTF(User-Defined Table-Generating Functions)
一进多出
如lateral view explode()
4)官方文档地址
https://cwiki.apache.org/confluence/display/Hive/HivePlugins
5)编程步骤:
(1)继承org.apache.hadoop.hive.ql.UDF
(2)需要实现evaluate函数;evaluate函数支持重载;
6)注意事项
(1)UDF必须要有返回类型,可以返回null,但是返回类型不能为void;
(2)UDF中常用Text/LongWritable等类型,不推荐使用java类型;
16.13.2、自定义函数开发
- 第一步:创建maven java 工程,并导入jar包
<repositories> <repository> <id>cloudera</id> <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.6.0-cdh5.14.2</version> </dependency> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.1.0-cdh5.14.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.2</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*/RSA</exclude> </excludes> </filter> </filters> </configuration> </execution> </executions> </plugin> </plugins> </build>
- 第二步:开发java类继承UDF,并重载evaluate 方法
public class MyUDF extends UDF { public Text evaluate(final Text s) { if (null == s) { return null; } //**返回大写字母 return new Text(s.toString().toUpperCase()); } }
- 第三步:将我们的项目打包,并上传到hive的lib目录下
使用maven的package进行打包,将我们打包好的jar包上传到node03服务器的/kkb/install/hive-1.1.0-cdh5.14.2/lib 这个路径下
- 第四步:添加我们的jar包
重命名我们的jar包名称
cd /kkb/install/hive-1.1.0-cdh5.14.2/lib mv original-day_hive_udf-1.0-SNAPSHOT.jar udf.jar
hive的客户端添加我们的jar包
0: jdbc:hive2://node03:10000> add jar /kkb/install/hive-1.1.0-cdh5.14.2/lib/udf.jar;
- 第五步:设置函数与我们的自定义函数关联
0: jdbc:hive2://node03:10000> create temporary function tolowercase as 'com.kkb.udf.MyUDF';
- 第六步:使用自定义函数
0: jdbc:hive2://node03:10000>select tolowercase('abc');
16.13.3、hive当中如何创建永久函数
在hive当中添加临时函数,需要我们每次进入hive客户端的时候都需要添加以下,退出hive客户端临时函数就会失效,那么我们也可以创建永久函数来让其不会失效
创建永久函数
1、指定数据库,将我们的函数创建到指定的数据库下面 0: jdbc:hive2://node03:10000>use myhive; 2、使用add jar添加我们的jar包到hive当中来 0: jdbc:hive2://node03:10000>add jar /kkb/install/hive-1.1.0-cdh5.14.2/lib/udf.jar; 3、查看我们添加的所有的jar包 0: jdbc:hive2://node03:10000>list jars; 4、创建永久函数,与我们的函数进行关联 0: jdbc:hive2://node03:10000>create function myuppercase as 'com.kkb.udf.MyUDF'; 5、查看我们的永久函数 0: jdbc:hive2://node03:10000>show functions like 'my*'; 6、使用永久函数 0: jdbc:hive2://node03:10000>select myhive.myuppercase('helloworld'); 7、删除永久函数 0: jdbc:hive2://node03:10000>drop function myhive.myuppercase; 8、查看函数 show functions like 'my*';
17、hive表的数据压缩
17.1、数据的压缩说明
- 压缩模式评价
-
- 可使用以下三种标准对压缩方式进行评价
-
-
- 1、压缩比:压缩比越高,压缩后文件越小,所以压缩比越高越好
- 2、压缩时间:越快越好
- 3、已经压缩的格式文件是否可以再分割:可以分割的格式允许单一文件由多个Mapper程序处理,可以更好的并行化
-
- 常见压缩格式
压缩方式 | 压缩比 | 压缩速度 | 解压缩速度 | 是否可分割 |
gzip | 13.4% | 21 MB/s | 118 MB/s | 否 |
bzip2 | 13.2% | 2.4MB/s | 9.5MB/s | 是 |
lzo | 20.5% | 135 MB/s | 410 MB/s | 是 |
snappy | 22.2% | 172 MB/s | 409 MB/s | 否 |
- Hadoop编码/解码器方式
压缩格式 | 对应的编码/解码器 |
DEFLATE | org.apache.hadoop.io.compress.DefaultCodec |
Gzip | org.apache.hadoop.io.compress.GzipCodec |
BZip2 | org.apache.hadoop.io.compress.BZip2Codec |
LZO | com.hadoop.compress.lzo.LzopCodec |
Snappy | org.apache.hadoop.io.compress.SnappyCodec |
- 压缩性能的比较
压缩算法 | 原始文件大小 | 压缩文件大小 | 压缩速度 | 解压速度 |
gzip | 8.3GB | 1.8GB | 17.5MB/s | 58MB/s |
bzip2 | 8.3GB | 1.1GB | 2.4MB/s | 9.5MB/s |
LZO | 8.3GB | 2.9GB | 49.3MB/s | 74.6MB/s |
http://google.github.io/snappy/
On a single core of a Core i7 processor in 64-bit mode, Snappy compresses at about 250 MB/sec or more and decompresses at about 500 MB/sec or more.
17.2、压缩配置参数
要在Hadoop中启用压缩,可以配置如下参数(mapred-site.xml文件中):
参数 | 默认值 | 阶段 | 建议 |
io.compression.codecs (在core-site.xml中配置) | org.apache.hadoop.io.compress.DefaultCodec, org.apache.hadoop.io.compress.GzipCodec, org.apache.hadoop.io.compress.BZip2Codec, org.apache.hadoop.io.compress.Lz4Codec | 输入压缩 | Hadoop使用文件扩展名判断是否支持某种编解码器 |
mapreduce.map.output.compress | false | mapper输出 | 这个参数设为true启用压缩 |
mapreduce.map.output.compress.codec | org.apache.hadoop.io.compress.DefaultCodec | mapper输出 | 使用LZO、LZ4或snappy编解码器在此阶段压缩数据 |
mapreduce.output.fileoutputformat.compress | false | reducer输出 | 这个参数设为true启用压缩 |
mapreduce.output.fileoutputformat.compress.codec | org.apache.hadoop.io.compress. DefaultCodec | reducer输出 | 使用标准工具或者编解码器,如gzip和bzip2 |
mapreduce.output.fileoutputformat.compress.type | RECORD | reducer输出 | SequenceFile输出使用的压缩类型:NONE和BLOCK |
17.3、开启map输出阶段压缩
开启map输出阶段压缩可以减少job中map和Reduce task间数据传输量。具体配置如下:
案例实操:
1)开启hive中间传输数据压缩功能 hive (default)>set hive.exec.compress.intermediate=true; 2)开启mapreduce中map输出压缩功能 hive (default)>set mapreduce.map.output.compress=true; 3)设置mapreduce中map输出数据的压缩方式 hive (default)>set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec; 4)执行查询语句 select count(1) from score;
17.4、开启reduce输出阶段压缩
当Hive将输出写入到表中时,输出内容同样可以进行压缩。属性hive.exec.compress.output控制着这个功能。用户可能需要保持默认设置文件中的默认值false,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true,来开启输出结果压缩功能。
案例实操:
1)开启hive最终输出数据压缩功能 hive (default)>set hive.exec.compress.output=true; 2)开启mapreduce最终输出数据压缩 hive (default)>set mapreduce.output.fileoutputformat.compress=true; 3)设置mapreduce最终数据输出压缩方式 hive (default)> set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec; 4)设置mapreduce最终数据输出压缩为块压缩 hive (default)>set mapreduce.output.fileoutputformat.compress.type=BLOCK; 5)测试一下输出结果是否是压缩文件 insert overwrite local directory '/kkb/install/hivedatas/snappy' select * from score distribute by s_id sort by s_id desc;
18、hive表文件存储格式
Hive支持的存储数的格式主要有:TEXTFILE(行式存储) 、SEQUENCEFILE(行式存储)、ORC(列式存储)、PARQUET(列式存储)。
18.1、列式存储和行式存储
上图左边为逻辑表,右边第一个为行式存储,第二个为列式存储。
行存储的特点: 查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。select *
列存储的特点: 因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。 select 某些字段效率更高
TEXTFILE和SEQUENCEFILE的存储格式都是基于行存储的;
ORC和PARQUET是基于列式存储的。
18.2、TEXTFILE格式
默认格式,数据不做压缩,磁盘开销大,数据解析开销大。可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。
18.3、ORC格式
Orc (Optimized Row Columnar)是hive 0.11版里引入的新的存储格式。
可以看到每个Orc文件由1个或多个stripe组成,每个stripe250MB大小,这个Stripe实际相当于RowGroup概念,不过大小由4MB->250MB,这样能提升顺序读的吞吐率。每个Stripe里有三部分组成,分别是Index Data,Row Data,Stripe Footer:
一个orc文件可以分为若干个Stripe
一个stripe可以分为三个部分
indexData:某些列的索引数据
rowData :真正的数据存储
StripFooter:stripe的元数据信息
1)Index Data:一个轻量级的index,默认是每隔1W行做一个索引。这里做的索引只是记录某行的各字段在Row Data中的offset。
2)Row Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个Stream来存储。
3)Stripe Footer:存的是各个stripe的元数据信息
每个文件有一个File Footer,这里面存的是每个Stripe的行数,每个Column的数据类型信息等;每个文件的尾部是一个PostScript,这里面记录了整个文件的压缩类型以及FileFooter的长度信息等。在读取文件时,会seek到文件尾部读PostScript,从里面解析到File Footer长度,再读FileFooter,从里面解析到各个Stripe信息,再读各个Stripe,即从后往前读。
18.4、PARQUET格式
Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目。
Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。
通常情况下,在存储Parquet数据的时候会按照Block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。Parquet文件的格式如下图所示。
上图展示了一个Parquet文件的内容,一个文件中可以存储多个行组,文件的首位都是该文件的Magic Code,用于校验它是否是一个Parquet文件,Footer length记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的Schema信息。除了文件中每一个行组的元数据,每一页的开始都会存储该页的元数据,在Parquet中,有三种类型的页:数据页、字典页和索引页。数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引,目前Parquet中还不支持索引页。
18.5、主流文件存储格式对比试验
从存储文件的压缩比和查询速度两个角度对比。
存储文件的压缩比测试:
- 1)TEXTFILE
-
- (1)创建表,存储数据格式为TEXTFILE
use myhive; create table log_text ( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string )ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE ;
-
- (2)向表中加载数据
load data local inpath '/kkb/install/hivedatas/log.data' into table log_text ;
-
- (3)查看表中数据大小,大小为18.1M
dfs -du -h /user/hive/warehouse/myhive.db/log_text; 18.1 M /user/hive/warehouse/log_text/log.data
- 2)ORC
-
- (1)创建表,存储数据格式为ORC
create table log_orc( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS orc ;
-
- (2)向表中加载数据
insert into table log_orc select * from log_text ;
-
- (3)查看表中数据大小
dfs -du -h /user/hive/warehouse/myhive.db/log_orc; 2.8 M /user/hive/warehouse/log_orc/123456_0
orc这种存储格式,默认使用了zlib压缩方式来对数据进行压缩,所以数据会变成了2.8M,非常小
- 3)PARQUET
-
- (1)创建表,存储数据格式为parquet
create table log_parquet( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS PARQUET ;
-
- (2)向表中加载数据
insert into table log_parquet select * from log_text ;
-
- (3)查看表中数据大小
dfs -du -h /user/hive/warehouse/myhive.db/log_parquet; 13.1 M /user/hive/warehouse/log_parquet/123456_0
存储文件的压缩比总结:
ORC > Parquet > textFile
存储文件的查询速度测试:
1)TextFile hive (default)> select count(*) from log_text; _c0 100000 Time taken: 21.54 seconds, Fetched: 1 row(s) 2)ORC hive (default)> select count(*) from log_orc; _c0 100000 Time taken: 20.867 seconds, Fetched: 1 row(s) 3)Parquet hive (default)> select count(*) from log_parquet; _c0 100000 Time taken: 22.922 seconds, Fetched: 1 row(s) 存储文件的查询速度总结: ORC > TextFile > Parquet
19、存储和压缩的结合
官网:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC
ORC存储方式的压缩:
Key | Default | Notes |
orc.compress | ZLIB | high level compression (one of NONE, ZLIB, SNAPPY) |
orc.compress.size | 262,144 | number of bytes in each compression chunk |
orc.stripe.size | 67,108,864 | number of bytes in each stripe |
orc.row.index.stride | 10,000 | number of rows between index entries (must be >= 1000) |
orc.create.index | true | whether to create row indexes |
orc.bloom.filter.columns | "" | comma separated list of column names for which bloom filter should be created |
orc.bloom.filter.fpp | 0.05 | false positive probability for bloom filter (must >0.0 and <1.0) |
- 1)创建一个非压缩格式的ORC存储方式
-
- (1)建表语句
create table log_orc_none( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS orc tblproperties ("orc.compress"="NONE");
-
- (2)插入数据
insert into table log_orc_none select * from log_text ;
-
- (3)查看插入后数据
dfs -du -h /user/hive/warehouse/myhive.db/log_orc_none; 7.7 M /user/hive/warehouse/log_orc_none/123456_0
- 2)创建一个SNAPPY压缩的ORC存储方式
-
- (1)建表语句
create table log_orc_snappy( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS orc tblproperties ("orc.compress"="SNAPPY");
-
- (2)插入数据
insert into table log_orc_snappy select * from log_text ;
-
- (3)查看插入后数据
dfs -du -h /user/hive/warehouse/myhive.db/log_orc_snappy ; 3.8 M /user/hive/warehouse/log_orc_snappy/123456_0
-
- 3)上一节中默认创建的ORC存储方式,导入数据后的大小为
2.8 M /user/hive/warehouse/log_orc/123456_0
比Snappy压缩的还小。原因是orc存储文件默认采用ZLIB压缩。比snappy压缩的小。
-
- 4)存储方式和压缩总结:
在实际的项目开发当中,hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy。
20、hive的SerDe
20.1、hive的SerDe是什么
Serde是 Serializer/Deserializer的简写。hive使用Serde进行行对象的序列与反序列化。最后实现把文件内容映射到 hive 表中的字段数据类型。
为了更好的阐述使用 SerDe 的场景,我们需要了解一下 Hive 是如何读数据的(类似于 HDFS 中数据的读写操作):
HDFS files –> InputFileFormat –> <key, value> –> Deserializer –> Row object
Row object –> Serializer –> <key, value> –> OutputFileFormat –> HDFS files
20.2、hive的SerDe类型
- Hive 中内置org.apache.hadoop.hive.serde2 库,内部封装了很多不同的SerDe类型。
- hive创建表时, 通过自定义的SerDe或使用Hive内置的SerDe类型指定数据的序列化和反序列化方式。
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name data_type [COMMENT col_comment], ...)] [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] [ROW FORMAT row_format] [STORED AS file_format] [LOCATION hdfs_path]
- 如上创建表语句, 使用row format 参数说明SerDe的类型。
- 你可以创建表时使用用户自定义的Serde或者native Serde, 如果 ROW FORMAT没有指定或者指定了 ROW FORMAT DELIMITED就会使用native Serde。
- Avro (Hive 0.9.1 and later)
- ORC (Hive 0.11 and later)
- RegEx
- Thrift
- Parquet (Hive 0.13 and later)
- CSV (Hive 0.14 and later)
- MultiDelimitSerDe
20.3、企业实战
20.3.1、通过MultiDelimitSerDe解决多字符分割场景
1、创建表
use myhive; create table t1 (id String, name string) row format serde 'org.apache.hadoop.hive.contrib.serde2.MultiDelimitSerDe' WITH SERDEPROPERTIES ("field.delim"="##");
2、准备数据 t1.txt
cd /kkb/install/hivedatas vim t1.txt 1##xiaoming 2##xiaowang 3##xiaozhang
3、加载数据
load data local inpath '/kkb/install/hivedatas/t1.txt' into table t1;
4、查询数据
0: jdbc:hive2://node1:10000> select * from t1; +--------+------------+--+ | t1.id | t1.name | +--------+------------+--+ | 1 | xiaoming | | 2 | xiaowang | | 3 | xiaozhang | +--------+------------+--+
20.3.2、通过RegexSerDe解决多字符分割场景
1、创建表
create table t2(id int, name string) row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe' WITH SERDEPROPERTIES ("input.regex" = "^(.*)\\#\\#(.*)$");
2、准备数据 t1.txt
1##xiaoming 2##xiaowang 3##xiaozhang
3、加载数据
load data local inpath '/kkb/install/hivedatas/t1.txt' into table t2;
4、查询数据
0: jdbc:hive2://node1:10000> select * from t2; +--------+------------+--+ | t2.id | t2.name | +--------+------------+--+ | 1 | xiaoming | | 2 | xiaowang | | 3 | xiaozhang | +--------+------------+--+
21、hive的企业级调优
21.1、Fetch抓取
- Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算
-
- 例如:select * from score;
- 在这种情况下,Hive可以简单地读取employee对应的存储目录下的文件,然后输出查询结果到控制台
- 在hive-default.xml.template文件中 hive.fetch.task.conversion默认是more,老版本hive默认是minimal,该属性修改为more以后,在全局查找、字段查找、limit查找等都不走mapreduce。
- 案例实操
-
- 把 hive.fetch.task.conversion设置成none,然后执行查询语句,都会执行mapreduce程序
set hive.fetch.task.conversion=none; select * from score; select s_id from score; select s_id from score limit 3;
-
- 把hive.fetch.task.conversion设置成more,然后执行查询语句,如下查询方式都不会执行mapreduce程序。
set hive.fetch.task.conversion=more; select * from score; select s_id from score; select s_id from score limit 3;
21.2、本地模式
- 在Hive客户端测试时,默认情况下是启用hadoop的job模式,把任务提交到集群中运行,这样会导致计算非常缓慢;
- Hive可以通过本地模式在单台机器上处理任务。对于小数据集,执行时间可以明显被缩短。
- 案例实操
--开启本地模式,并执行查询语句 set hive.exec.mode.local.auto=true; //开启本地mr --设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式, --默认为134217728,即128M set hive.exec.mode.local.auto.inputbytes.max=50000000; --设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式, --默认为4 set hive.exec.mode.local.auto.input.files.max=5; --执行查询的sql语句 select * from student cluster by s_id; --关闭本地运行模式 set hive.exec.mode.local.auto=false; select * from student cluster by s_id;
21.3、表的优化
- 1 小表、大表 join
-
- 将key相对分散,并且数据量小的表放在join的左边,这样可以有效减少内存溢出错误发生的几率;再进一步,可以使用map join让小的维度表(1000条以下的记录条数)先进内存。在map端完成reduce。
select count(distinct s_id) from score; select count(s_id) from score group by s_id; 在map端进行聚合,效率更高
-
- 实际测试发现:新版的hive已经对小表 join 大表和大表 join 小表进行了优化。小表放在左边和右边已经没有明显区别。
- 多个表关联时,最好分拆成小段,避免大sql(无法控制中间Job)
- 2 大表 join 大表
-
- 1.空 key 过滤
-
-
- 有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的reducer上,从而导致内存不够。
- 此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤。
- 测试环境准备:
-
use myhive; create table ori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; create table nullidtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; create table jointable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; load data local inpath '/kkb/install/hivedatas/hive_big_table/*' into table ori; load data local inpath '/kkb/install/hivedatas/hive_have_null_id/*' into table nullidtable;
-
-
- 过滤空key与不过滤空key的结果比较
-
不过滤: INSERT OVERWRITE TABLE jointable SELECT a.* FROM nullidtable a JOIN ori b ON a.id = b.id; 结果: No rows affected (152.135 seconds) 过滤: INSERT OVERWRITE TABLE jointable SELECT a.* FROM (SELECT * FROM nullidtable WHERE id IS NOT NULL ) a JOIN ori b ON a.id = b.id; 结果: No rows affected (141.585 seconds)
-
- 2、空 key 转换
-
-
- 有时虽然某个 key 为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在 join 的结果中,此时我们可以表 a 中 key 为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的 reducer 上。
不随机分布:
- 有时虽然某个 key 为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在 join 的结果中,此时我们可以表 a 中 key 为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的 reducer 上。
-
set hive.exec.reducers.bytes.per.reducer=32123456; set mapreduce.job.reduces=7; INSERT OVERWRITE TABLE jointable SELECT a.* FROM nullidtable a LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN 'hive' ELSE a.id END = b.id; No rows affected (41.668 seconds)
- 结果:这样的后果就是所有为null值的id全部都变成了相同的字符串,及其容易造成数据的倾斜(所有的key相同,相同key的数据会到同一个reduce当中去)
为了解决这种情况,我们可以通过hive的rand函数,随机的给每一个为空的id赋上一个随机值,这样就不会造成数据倾斜
随机分布:
set hive.exec.reducers.bytes.per.reducer=32123456; set mapreduce.job.reduces=7; INSERT OVERWRITE TABLE jointable SELECT a.* FROM nullidtable a LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN concat('hive', rand()) ELSE a.id END = b.id; No rows affected (42.594 seconds)
- 3、大表join小表与小表join大表实测
-
- 需求:测试大表JOIN小表和小表JOIN大表的效率 (新的版本当中已经没有区别了,旧的版本当中需要使用小表)
-
-
- (1)建大表、小表和JOIN后表的语句
-
create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; create table smalltable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; create table jointable2(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';
-
-
- (2)分别向大表和小表中导入数据
-
hive (default)> load data local inpath '/kkb/install/hivedatas/big_data' into table bigtable; hive (default)>load data local inpath '/kkb/install/hivedatas/small_data' into table smalltable;
-
-
- (3)map join
-
-
-
-
- 如果不指定MapJoin 或者不符合 MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join。容易发生数据倾斜。可以用 MapJoin 把小表全部加载到内存在map端进行join,避免reducer处理。
-
-
-
-
-
-
- 1、开启MapJoin参数设置
-
-
-
--默认为true set hive.auto.convert.join = true;
-
-
-
-
- 2、大表小表的阈值设置(默认25M一下认为是小表)
-
-
-
set hive.mapjoin.smalltable.filesize=26214400;
-
-
-
-
- 3、MapJoin工作机制
-
-
-
首先是Task A,它是一个Local Task(在客户端本地执行的Task),负责扫描小表b的数据,将其转换成一个HashTable的数据结构,并写入本地的文件中,之后将该文件加载到DistributeCache中。
接下来是Task B,该任务是一个没有Reduce的MR,启动MapTasks扫描大表a,在Map阶段,根据a的每一条记录去和DistributeCache中b表对应的HashTable关联,并直接输出结果。
由于MapJoin没有Reduce,所以由Map直接输出结果文件,有多少个Map Task,就有多少个结果文件。
案例实操:
(1)开启Mapjoin功能
set hive.auto.convert.join = true; 默认为true
2)执行小表JOIN大表语句
INSERT OVERWRITE TABLE jointable2 SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url FROM smalltable s JOIN bigtable b ON s.id = b.id; Time taken: 31.814 seconds
(3)执行大表JOIN小表语句
INSERT OVERWRITE TABLE jointable2 SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url FROM bigtable b JOIN smalltable s ON s.id = b.id; Time taken: 28.46 seconds
-
-
- (4)group by
-
-
-
-
- 默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。
- 并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。
- 开启Map端聚合参数设置
-
-
--是否在Map端进行聚合,默认为True set hive.map.aggr = true; --在Map端进行聚合操作的条目数目 set hive.groupby.mapaggr.checkinterval = 100000; --有数据倾斜的时候进行负载均衡(默认是false) set hive.groupby.skewindata = true; 当选项设定为 true,生成的查询计划会有两个MR Job。第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
-
-
- (5)count(distinct)
-
-
-
-
- 数据量小的时候无所谓,数据量大的情况下,由于count distinct 操作需要用一个reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般count distinct使用先group by 再count的方式替换
- 环境准备:
-
-
create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; load data local inpath '/kkb/install/hivedatas/data/100万条大表数据(id除以10取整)/bigtable' into table bigtable; --每个reduce任务处理的数据量 默认256000000(256M) set hive.exec.reducers.bytes.per.reducer=32123456; select count(distinct ip ) from log_text; 转换成 set hive.exec.reducers.bytes.per.reducer=32123456; select count(ip) from (select ip from log_text group by ip) t; 虽然会多用一个Job来完成,但在数据量大的情况下,这个绝对是值得的。
-
-
- (6)笛卡尔积
-
-
-
-
- 尽量避免笛卡尔积,即避免join的时候不加on条件,或者无效的on条件
- Hive只能使用1个reducer来完成笛卡尔积。
-
-
21.4、使用分区剪裁、列剪裁
- 尽可能早地过滤掉尽可能多的数据量,避免大量数据流入外层SQL。
- 列剪裁
-
- 只获取需要的列的数据,减少数据输入。
- 分区裁剪
-
- 分区在hive实质上是目录,分区裁剪可以方便直接地过滤掉大部分数据。
- 尽量使用分区过滤,少用select *
环境准备:
create table ori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t'; load data local inpath '/home/admin/softwares/data/加递增id的原始数据/ori' into table ori; load data local inpath '/home/admin/softwares/data/100万条大表数据(id除以10取整)/bigtable' into table bigtable;
先关联再Where:
SELECT a.id FROM bigtable a LEFT JOIN ori b ON a.id = b.id WHERE b.id <= 10;
正确的写法是写在ON后面:先Where再关联
SELECT a.id FROM ori a LEFT JOIN bigtable b ON (a.id <= 10 AND a.id = b.id);
或者直接写成子查询:
SELECT a.id FROM bigtable a RIGHT JOIN (SELECT id FROM ori WHERE id <= 10 ) b ON a.id = b.id;
21.5、并行执行
把一个sql语句中没有相互依赖的阶段并行去运行。提高集群资源利用率
--开启并行执行 set hive.exec.parallel=true; --同一个sql允许最大并行度,默认为8。 set hive.exec.parallel.thread.number=16;
21.6、严格模式
- Hive提供了一个严格模式,可以防止用户执行那些可能意想不到的不好的影响的查询。
- 通过设置属性hive.mapred.mode值为默认是非严格模式**nonstrict** 。开启严格模式需要修改hive.mapred.mode值为**strict**,开启严格模式可以禁止3种类型的查询。
--设置非严格模式(默认) set hive.mapred.mode=nonstrict; --设置严格模式 set hive.mapred.mode=strict;
(1)对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行
--设置严格模式下 执行sql语句报错; 非严格模式下是可以的 select * from order_partition; 异常信息:Error: Error while compiling statement: FAILED: SemanticException [Error 10041]: No partition predicate found for Alias "order_partition" Table "order_partition"
(2)对于使用了order by语句的查询,要求必须使用limit语句
--设置严格模式下 执行sql语句报错; 非严格模式下是可以的 select * from order_partition where month='2019-03' order by order_price; 异常信息:Error: Error while compiling statement: FAILED: SemanticException 1:61 In strict mode, if ORDER BY is specified, LIMIT must also be specified. Error encountered near token 'order_price'
(3)限制笛卡尔积的查询
- 严格模式下,避免出现笛卡尔积的查询
21.7、JVM重用
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。
Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。JVM重用可以使得JVM实例在同一个job中重新使用N次。N的值可以在Hadoop的mapred-site.xml文件中进行配置。通常在10-20之间,具体多少需要根据具体业务场景测试得出。
<property> <name>mapreduce.job.jvm.numtasks</name> <value>10</value> <description>How many tasks to run per jvm. If set to -1, there is no limit. </description> </property>
我们也可以在hive当中通过
set mapred.job.reuse.jvm.num.tasks=10;
这个设置来设置我们的jvm重用
这个功能的缺点是,开启JVM重用将一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job中有某几个reduce task执行的时间要比其他Reduce task消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。
21.8、推测执行
在分布式集群环境下,因为程序Bug(包括Hadoop本身的bug),负载不均衡或者资源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速度可能明显慢于其他任务(比如一个作业的某个任务进度只有50%,而其他所有任务已经运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生,Hadoop采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
设置开启推测执行参数:Hadoop的mapred-site.xml文件中进行配置
<property> <name>mapreduce.map.speculative</name> <value>true</value> <description>If true, then multiple instances of some map tasks may be executed in parallel.</description> </property> <property> <name>mapreduce.reduce.speculative</name> <value>true</value> <description>If true, then multiple instances of some reduce tasks may be executed in parallel.</description> </property>
不过hive本身也提供了配置项来控制reduce-side的推测执行:
<property> <name>hive.mapred.reduce.tasks.speculative.execution</name> <value>true</value> <description>Whether speculative execution for reducers should be turned on. </description> </property>
关于调优这些推测执行变量,还很难给一个具体的建议。如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大大。(直接关闭)
21.9、压缩
Hive表中间数据压缩
#设置为true为激活中间数据压缩功能,默认是false,没有开启 set hive.exec.compress.intermediate=true; #设置中间数据的压缩算法 set mapred.map.output.compression.codec= org.apache.hadoop.io.compress.SnappyCodec;
Hive表最终输出结果压缩
set hive.exec.compress.output=true; set mapred.output.compression.codec= org.apache.hadoop.io.compress.SnappyCodec;
21.10、使用explain(执行计划)
21.11、数据倾斜
由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点
数据倾斜的现象: 在执行任务的时候,任务进度长时间维持在99%左右,查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。 单一reduce的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。最长时长远大于平均时长。
数据倾斜的情况:
数据倾斜的原因:
1)、key分布不均匀 2)、业务数据本身的特性 3)、建表时考虑不周 4)、某些SQL语句本身就有数据倾斜
- 数据倾斜的解决方案
-
- map端聚合
--Map 端部分聚合,相当于Combiner hive.map.aggr = true; --有数据倾斜的时候进行负载均衡 hive.groupby.skewindata=true; --有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。
-
- sql语句调节
-
-
- 如何join
-
关于驱动表的取,用join key分布最均匀的表作为驱动表 做好列裁剪和filter操作,以达到两表做join的时候,数据量相对变小的效果。
-
-
- 大小表join
-
使用map join让小的维度表(1000条以下的记录条数) 先进内存。在map端完成reduce.
-
-
- 大表Join大表
-
把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。
-
-
- count distinct大量相同特殊值
-
count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
-
-
- group by维度过小
-
采用sum() group by的方式来替换count(distinct)完成计算。
-
-
- 特殊情况特殊处理
-
在业务逻辑优化效果的不大情况下,一些时候是可以将倾斜的数据单独拿出来处理。最后union回去
- 典型的业务场景
-
- 空值产生的数据倾斜
-
-
- 场景
-
如日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和 用户表中的user_id 关联,会碰到数据倾斜的问题。
-
-
- 解决方法
-
--user_id为空的不参与关联 select * from log a join users b on a.user_id is not null and a.user_id = b.user_id union all select * from log a where a.user_id is null; --赋与空值分新的key值 select * from log a left outer join users b on case when a.user_id is null then concat(‘hive’,rand()) else a.user_id end = b.user_id;
-
- 不同数据类型关联产生数据倾斜
-
-
- 场景
-
用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。
-
-
- 解决办法
-
-
-
-
- 把数字类型转换成字符串类型
-
-
select * from users a left outer join logs b on a.usr_id = cast(b.user_id as string);
21.11.1、设置合理的map数
1) 通常情况下,作业会通过input的目录产生一个或者多个map任务。
主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。 举例: a) 假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块),从而产生7个map数。 b) 假设input目录下有3个文件a,b,c大小分别为10m,20m,150m,那么hadoop会分隔成4个块(10m,20m,128m,22m),从而产生4个map数。即,如果文件大于块大小(128m),那么会拆分,如果小于块大小,则把该文件当成一个块。
2) 是不是map数越多越好?
答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。
3) 是不是保证每个map处理接近128m的文件块,就高枕无忧了?
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。 针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;
21.11.2、小文件合并
- 在map执行前合并小文件,减少map数:
- CombineHiveInputFormat 具有对小文件进行合并的功能(系统默认的格式)
set mapred.max.split.size=112345600; 最大的文件切片数据量 set mapred.min.split.size.per.node=112345600; 最大每个节点处理的数据量 set mapred.min.split.size.per.rack=112345600; 最大每个机架处理的数据量 set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 表示对小文件进行合并
这个参数表示执行前进行小文件合并,前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的),进行合并。
21.11.3、复杂文件增加map数
- 当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
- 增加map的方法为
-
- 根据 computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))公式
- 调整maxSize最大值。让maxSize最大值低于blocksize就可以增加map的个数。
mapreduce.input.fileinputformat.split.minsize=1 默认值为1 mapreduce.input.fileinputformat.split.maxsize=Long.MAXValue 默认值Long.MAXValue因此,默认情况下,切片大小=blocksize maxsize(切片最大值): 参数如果调到比blocksize小,则会让切片变小,而且就等于配置的这个参数的值。 minsize(切片最小值): 参数调的比blockSize大,则可以让切片变得比blocksize还大。 例如 --设置maxsize大小为10M,也就是说一个fileSplit的大小为10M set mapreduce.input.fileinputformat.split.maxsize=10485760;
21.11.4、设置合理的reduceshu
1、调整reduce个数方法一
- 1)每个Reduce处理的数据量默认是256MB
set hive.exec.reducers.bytes.per.reducer=256000000;
- 2) 每个任务最大的reduce数,默认为1009
set hive.exec.reducers.max=1009;
- 3) 计算reducer数的公式
N=min(参数2,总输入数据量/参数1)
2、调整reduce个数方法二
--设置每一个job中reduce个数 set mapreduce.job.reduces=3;
3、reduce个数并不是越多越好
- 过多的启动和初始化reduce也会消耗时间和资源;
- 同时过多的reduce会生成很多个文件,也有可能出现小文件问题