问题:公司的经营状况?
主题一:财务
现金流
指标1.1:净现金流入/流出量
指标1.2:现金转换周期
预算执行状况
指标2.1:预算内成本控制
指标2.2:预算与实际支出的差异
主题二:产品
指标1:产品销售量
指标2:产品利润率
指标3:新产品开发周期
主题三:客户
老客户的流失率
指标1.1:重复购买率
指标1.2:客户满意度调查结果
新客户的留存率、转化率
指标2.1:新客户留存率
指标2.2:潜在客户到实际客户的转化率
主题四:员工
指标1:员工满意度
指标2:员工流失率
指标3:员工绩效评估
主题五:资产
指标1:资产负债率
指标2:固定资产周转率
指标3:存货周转率
问题 => 主题(拆分) => 指标 => HQL
基于元数据和表数据对数据进行结构化治理的机制(数仓)
"基于元数据"的核心:获取Hive MetaStore指定Mysql存储的元数据并根据其解析HQL生成执行计划
"基于表数据"的核心:获取表数据并通过执行计划进行数据处理和结果存储和加载。
UI
:客户端提交查询。
Compiler
:借助MetaStore获取存储在Mysql中的元数据信息解析查询块或表达式,生成执行计划。
Execution engine
:管理计划的不同阶段之间的依赖关系(协调各操作的执行顺序),并在适当的大数据组件上执行这些阶段。
6.1步:
1.将执行计划提交到Hadoop作为一个MapReduce作业.
2.RM将作业分解为多个任务,并且分配给不同的集群节点去执行。
3.启动Map任务(读取输入数据,处理,输出结果)
4.启动Reduce任务(可选)(读取Map|Shffle端输出,处理,输出结果) => 决定了是MapOnly引擎还是MapReduce引擎
6.2步:
完成了一个Job,将数据加载到Hive表上。
6.3步:
Hive与HDFS进行交互,包括读取数据文件以进行查询,或将查询结果写入到HDFS。
7,8,9步:
一旦执行引擎完成了查询任务,结果需要被传回UI
体现了将查询结果有执行引擎处理并通过Hive驱动程序返回给用户界面的过程。
将数据从各种数据源中提取出来,经过一系列处理和转换,最后导入到Hive委托存储的地方(例如:HDFS,Mysql等)
Extract
从外部数据源读取不同数据格式(结构化,半结构化,非结构化)数据通过UI提交到Hive系统
ODS 近源层
Extract后提取到的保持原始格式的数据被提取到ODS层,并在ODS层内经过一定的清洗和结构化。
==================进入MapReduce框架=====================
Transform(+宽表)
列裁剪:使用 INSERT INTO ... SELECT 语句创建新表或视图。
数据清洗:移除无用或错误数据。
数据补全:修正残缺或错误数据。如 mlae 视为 male。 province city
数据变形:改变数据的格式。如 {province:Jiangsu, city:wuxi} 转为 Jiangsu | wuxi。
无法修正错误:删除记录或视为缺失后估算。如年龄 -3。
数据集大小对处理要求的影响
大数据集:尽量近似和估算
小数据集:尽量补全
DWD 明细层
存储原始或未加工的数据
数据分析示例
事实->维度->维度建模
事实:如 “我在12:13分在小A包子店吃了一个鲜肉包。”
维度建模:基于不同客体分析,如对用户,则有特征,身份,喜好...
对鲜肉包,则有销量,销量占比,材料,烹饪方式...
DWT 轻聚层
对数据进行不同维度和不同量级的聚合,便于对数据进行即时查询
DWS 汇总层
数据被进一步聚合和汇总
DM 数据集市
数据仓库架构中的业务数据仓库子集,通常针对特定业务部门或功能领域。
Load
1. 将数据从DM层或者直接从源头加载到HDFS中。
2. 将物理计划传递给执行引擎,如MapReduce。
3. 获取处理后的结果或将处理后的结果写入HDFS。
关于"层"的概念:
1. "层"是逻辑概念,表示一个数据处理|存储环节,实际上存储在不同的服务器中。
2. "层"是基于数仓的概念,不依赖于任何引擎。
库、表的存储位置:
/user/hive/warehouse
【即配置hive.metastore.warehouse.dir
的文件夹路径地址】
默认数据库default | 非default数据库 | |
---|---|---|
数据库位置 | /user/hive/warehouse/数据库名字.db | |
表位置 | /user/hive/warehouse/表名 | /user/hive/warehouse/数据库名字.db/表名 |
服务
Hive MetaStore:指定Derby|Mysql存储元数据信息,并与HDFS相关联。
开放API面向客户端供它们访问元数据信息。
存储
database_params:存放元数据参数配置
dbs:存放metadata元数据
内部表:hive_+含义
外部表:hive_ext_+含义
CTE|TEMPORARY TABLE|VIEW:含义|hive_tmp_+含义|hive_view_+含义
分区表|分桶表:hive_par_+含义
A.区别
内部表
1.HDFS中的数据由Hive管理,在Hive中删除表会同步删除数据。
2.在Hive中创建内部表之后,在HDFS中会自动创建内部映射路径:/hive312/warehouse/DB_NAME.db/TABLE_NAME
3.内部表在创建的时候,通常路径中还没有数据。
外部表
1.HDFS中的数据不由Hive管理,在Hive中删除表不会同步删除数据,要删数据必须去HDFS中手动删除。
2.在Hive中创建外部表之后,在HDFS中不会有内部映射路径,需要自行创建并指定location。(location为数据文件的上一级目录)
3.外部表在创建的时候,通常路径中已有数据。
B.选择
只在Hive中使用;频繁更新数据并希望这些操作自动管理 —— 内部表
数据由多个应用共享,例如Hive和Spark;数据不经常变化或数据更新由外部过程管理 —— 外部表
分区字段名=分区字段值
基于一个或多个分区键进行分区,通常以日期进行分区。
分区表为内部表。
分区字段可以是原始表中的字段,也可以是通过表中的一个或多个字段计算出来的表达式。(日期 -> 年)。
若非原始字段,其本身也可以作为原始字段使用所有的SQL语句。
1.WHERE … | ON …
2.GROUP BY … , DISTRIBUTE BY … (确定Reducer) , cluster by …(确定Reducer并在内部排序)
3.如果数据量大,由于分区本身(文件夹)的大小限制,会选择缩小分区字段的维度。
1.静态分区(eg:客户按分区给数据)
需要为每个加载的数据集指定一个具体的分区(分区字段名=字段值)
适用于持续增长的新数据,例如对按月分区的数据,每个月增加一次分区即可。
2.动态分区(eg:项目初期,客户一次性给了大量数据)
适合处理包含大量分区的情况
1.hive进行查询时就是文件流读文件,即使读一条数据也需要加载整个文件。
所以分区表将文件切割成更小的粒度,当需要针对局部数据进行检索、聚合等处理时,便于快速筛选出符合特定条件的数据,提高效率。
2.如果需要进行分组聚合的字段是分组字段,即直接对该分区作聚合,则直接设置combiner,无需设置reducer。
GROUP BY year(order_time),month(order_time),weekofmonth(order_time)
=>从左到右分区从主到次
一级分区(year=2012)
二级分区(month=3)
三级分区(weekofmonth=2)
选择桶中的一个或多个列作为桶的依据,根据这些列的值对数据进行散列,然后根据散列值将数据分配到不同的桶中。
分桶的字段必须是表中已存在的字段,分桶数通常为2^n
默认采用原始字段值的hashcode%分桶数来决定该行数据会被拆分到几个桶。
便于进行抽样,通过了解数据的分布状态来对数据进行全量分析。或者作为一部分数据集运行查询测试。
提升有join操作时的MR效率
主要由采样率决定 : 10% -> 桶数定义为10
数据量越大,分桶越多。分桶越多,并行度越高。
分区倾斜严重,分桶能缓解。
配置: 优先级 重要程度
set config_item=value; 临时配置 1 ✔✔✔(个性化 执行优化)
set hive.server2.logging.operation.level=verbose|NONE; 开启|关闭日志()
hive-site.xml 自定义配置 2 ✔(服务 通用优化配置)
取hive-default.xml的头结构并且按hive_install.md进行个性化配置。
hive-default.xml 默认配置(模板) 3
优化:
Cost based Optimizer
1.通过估算不同查询策略的成本来选择执行计划,根据数据库中数据的变化动态调整计划。
2.在执行多表连接的查询时,会找出最优的连接顺序和连接算法.
HiveServer2 Beeline | HiveServer1 CLI | |
---|---|---|
Connection | beeline -u |
hive -h |
Query | beeline -e |
hive -e |
注意:beeline -u
输入的所有SQL语句前面不能有空白符。
beeline -u
刚开始连接上的时候会因为Hadoop的安全模式或者服务的延迟 导致开始可能会连接不上。
hive -e
适用于编写处理Hive的脚本,因为可以做临时连接(添加|删除分区…)
!table List Tables
!column List Columns
!sh ls Run Shell CMD
dfs -ls ; Run DFS CMD
!run Run SQL File
!dbinfo Check Supports
基础类型([]为较Mysql多出的)
字符:char,varchar,[string]✔
整数:tinyint,smallint,int✔,bigint
小数:float,double,numeric(m,n),decimal(m,n)✔
布尔:[boolean]✔
日期:date✔(YYYY-mm-dd 必须完整),timestamp(ms为单位,也可解析具体到秒的日期和时间信息:2015-9-7 1:52:22)✔
复杂类型(支持泛型)
数组:array => 用于处理同类型数据,对原始数据做结构化映射的(先升维才方便降维)
键值对:map => 用于处理有稳定映射关系的数据
结构体:struct => 用于处理无稳定映射关系的数据
一般来说,键值对or结构体的键尽量简单,值可以有一定的复杂度。
嵌套复杂类型
数组结构体
array> =>
[
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 35}
]
特殊:
01 => string(若设置为int,会自动去除前导零)
2015-01 => date ❌(不完整) timestamp ❌(没有具体到秒) string ✔
show databases;
create datebase if not exists yb12211;
use yb12211;
describe database yb12211;
alter database yb12211 set owner user dayongd;
drop datebase if exists yb12211;
mkdir -p EXTERNAL_TABLE_DIR
hdfs dfs -put FILE_NAME EXTERNAL_TABLE_DIR
create [external] table if not exists TABLE_NAME(
id int,
name string,
time bigint,
isPartyMember boolean,
expectSalary decimal(10,2),
hobby array<string>,
scores map<string,int>,
address struct<province:string,city:string>
)
comment 'TABLE_USAGE'
PARTITIONED BY (partition_key STRING)
--- row format delimited
| fields terminated by ',' 字段分隔方式
| collection items terminated by ';' 集合项分隔方式
| map keys terminated by ':' 键值对分隔方式
| lines terminated by '\n' 行间分隔方式(还可能有'\r\n','\t',' '...)
|
--- row format serde 'CLASS_FULL_PATH'
CSV(123,张三,1698376651212,true,26238.45)
serde 'org.apache.hadoop.hive.serde2.OpenCSVSerde'
with serdeproperties(
separatorChar=',',
quoteChar='"',(用于包围数据字段,避免数据内的符号被视为分隔符)
escapeChar='\\'(规避处理转义符)
)
regex
serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
with serdeproperties(
'input.regex'='^(\\d+),(.*?),(\\d+),(true|false),(\\d+).(\\d+\.?\\d+?)$'
)
json
serde 'org.apache.hive.hcatalog.data.JsonSerDe'
hbase
serde 'org.apache.hadoop.hive.hbase.HBaseSerde'
stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
with sederproperties(
"hbase.columns.mapping"=":key,info:name,info:sex,info:age"
)
tblproperties("hbase.table.name"="test_serde");
stored as FILE_FORMAT(textfile|orc|parquet|sequencefile|JSONFile|CSV) 指定存储格式,文件的存储格式改变,可能要相应设置InputFormat和OutputFormat。
location 'EXTERNAL_TABLE_DIR'
tblproperties(
'skip.header.line.count'='1'
'skip.footer.line.count'='2'
'related_sql_table' = 'my_sql_table' 将Hive表与另一个数据库的表`my_sql_table`相关联
);
load data [local] inpath 'FILEDIR' [overwrite] into table DB_NAME.TABLE_NAME;
delimited:以特定分隔符分隔的数据,可处理复杂数据
"123 Main St,Anytown,12345"
"math:90,science:85,english:95"
Serde:不得已才用
OpenCSVSerde:可以很好地处理包含引号和逗号的复杂 CSV 数据
不支持数据中有复杂的数据结构
"Jane Doe","[email protected]","555-1234","Street 123, Some City"
记忆:CSV格式要求
表头(可选)
CSV文件通常由多行组成,每行代表一个数据记录,字段内容可以包含在引号中(尤其是当字段值中包含分隔符时)
字段之间通常用,进行分隔,行之间通常用换行符进行分隔
RegexSerDe:适用于非标准或更复杂的文本数据格式和数据行中有较多无用数据的情况,通常面向日志
"[INFO] 2023-11-15 09:00:00 - Task started"
JsonSerDe:处理 json 格式的数据
{"id": 2, "name": "Bob", "role": "Analyst", "salary": 50000}
记忆:JSON格式要求
键值对形式 + 字符串需用双引号括起来 + 逗号分隔
ROW FORMAT
便于对不同类型的数据进行建表,在采集数据到ODS的过程中发挥作用
LOAD
上传的文件类型不重要
LOCAL:有LOCAL表示文件位于Linux本地,执行后为拷贝数据
没有LOCAL表示文件位于HDFS中,执行后为直接移动数据
OVERWRITE:有OVERWRITE表示覆盖
没有OVERWRITE表示追加
LOAD DATA和LOCATION的区别
LOAD DATA:在hive312的目录下,将数据从文件系统(本地|HDFS)直接加载到Hive表中
LOCAL INPATH 复制
INPATH 移动
LOCATION:用于指定表的数据实际存储在哪个文件系统路径上
LOCAL INPATH 选择复制数据的原因是为了在本地文件系统下留存一份数据副本
INPATH 选择移动数据的原因是在HDFS中移动数据只设计元数据的更改,并且不涉及网络传输,效率更高。
split("...",'SEPERATOR') 分隔
regexp_replace("...",'REGEX','REPLACE') 正则替换
explode(...) 列转行
INSERT INTO TABLE_NAME(FIELD1,FIELD2...) SELECT ...
建表高阶语句(CTAS —— CREATE TABLE AS SELECT)
CTAS
CTAS 常用于筛选某表中的数据出来并作为新表的内容
CTAS 不能创建 分区表、分桶表、外部表
eg:筛选2015年之前的数据作为新表
create table hive_ext_test_before2015 as
select * from hive_ext_regex_test1w
where year(order_time) <= 2015;
CTAS WITH CTE(WITH 作为数据来源)
eg:筛选2015年之前的全部数据和2015年之后男性中订单数超过5条记录或订单总金额超过50000的数据
CREATE TABLE hive_test_before2015_and_male_over5or5w_after2015 AS
WITH
before2015 AS (
SELECT * FROM hive_ext_regex_test1w
WHERE YEAR(order_time)<=2015
),
agg_male_over5or5w AS (
SELECT user_id
FROM hive_ext_regex_test1w
WHERE YEAR(order_time)>2015 AND user_gender = '男'
GROUP BY user_id
HAVING COUNT(*) >= 5 OR SUM(order_amount) >= 50000
),
male_over5or5w AS (
SELECT * FROM hive_ext_regex_test1w A
INNER JOIN agg_male_over5or5w B
ON A.user_id = B.user_id AND YEAR(A.order_time)>2015
)
SELECT * FROM before2015
UNION ALL
SELECT * FROM male_over5or5w;
CTL 完全复制表结构
CREATE TABLE employee_like LIKE employee;
-- 有时还需要添加内容
INSERT INTO employee_like SELECT * FROM employee;
创建临时表
临时表只对当前session有效,session退出后自动删除
表空间位于/tmp/hive-
如果创建的临时表和已创建的常规表表名相同,优先使用临时表
CREATE TEMPORARY TABLE TABLE_NAME(...) | AS SELECT ... ✔ | LIKE ...
创建视图
视图本质是一条复杂的公用的查询语句,实际上并不存储数据,查询视图实际上是在查询背后的基础表。
DROP VIEW IF EXISTS hive_view_test2020
CREATE VIEW IF NOT EXISTS hive_view_test2020 AS SELECT * FROM hive_ext_regex_test1w
WHERE year(order_time)=2020;
View :简化复杂查询
TEMPORARY TABLE : 短期数据处理 复杂查询的中间结果 预聚合数据存储
CTE : 单词查询使用。
DROP TABLE IF EXISTS employee [WITH PERGE];
With PERGE直接删除(可选),否则会放到 .Trash目录
TRUNCATE TABLE employee;
清空表数据
修改表是可以在创建表之后进行追加描述的。
ALTER TABLE employee RENAME TO new_employee;(修改表名,常用于数据备份)
ALTER TABLE c_employee SET TBLPROPERTIES ('key'='value');
常见的表属性
'comment':用于提供关于表的描述性注释。
'serde':指定用于序列化和反序列化数据的序列化/反序列化器。
'location':指定表的物理存储位置。
'inputformat':指定输入数据的格式。
'outputformat':指定输出数据的格式。
'external':指示表是否为外部表(外部表的数据存储在外部位置,不受表删除操作的影响)。
...(可设置的表属性一般可通过DESCRIBE FORMATTED TABLE_NAME查看)
key不一定已存在:可以新建属性,也可以对已有属性进行修改。
ALTER TABLE employee_internal SET SERDEPROPERTIES ('field.delim' = '$');
ALTER TABLE c_employee SET FILEFORMAT RCFILE; -- 修正表文件格式
通常用于拉链表的情况
-- 修改表的列操作
ALTER TABLE employee_internal CHANGE old_name new_name STRING; -- 修改列名
ALTER TABLE c_employee ADD COLUMNS (work string); -- 添加列
ALTER TABLE c_employee REPLACE COLUMNS (name string); -- 替换
show tables; 查看所有表
SHOW TABLES LIKE 'TABLE_NAME' 快速检查是否存在某表
desc inner_tab_employee; 查看表结构(字段名称、类型等)
desc formatted inner_tab_employee; 获取表的详细信息(包括分区信息、字段信息、表属性、存储信息等)
show create table inner_tab_employee; 获取创建表的完整SQL语句
定义:partitioned by(PAR_FIELD_NAME DATA_TYPE, …)
数据:
drop table if exists hive_internal_par_ext_regex_test1w;
create table if not exists hive_internal_par_ext_regex_test1w(
user_id int,
user_gender string,
order_time timestamp,
order_amount decimal(10,2)
)
partitioned by (year int)
row format delimited
fields terminated by ','
stored as textfile;
静态分区
1. 简单文本处理:
– 从文件中筛选2012年的数据
cat test1w.log | awk ‘/2012-/{print $0}’>test2012.log
– 将该数据上传到该表的2012年的分区下
load data local inpath ‘/root/hive/data/course/test2012.log’
overwrite into table hive_internal_par_ext_regex_test1w partition(year=2012);
2. 复杂数据处理:
INSERT OVERWRITE TABLE sales PARTITION(year=2023, month=3)
SELECT …
FROM another_table
WHERE …
动态分区 优先级
SET hive.exec.dynamic.partition = true; – 1.会话 ✔
SET hive.exec.dynamic.partition.mode = nonstrict;
hive-site.xml – 2.个性化配置
hive-default.xml – 3.为所有配置项提供默认配置
INSERT OVERWRITE TABLE hive_internal_par_ext_regex_test1w PARTITION (year)
SELECT *, year(order_time) FROM hive_ext_regex_test1w where year(order_time)>=2014;
(RegexSerDe不适用)
(分区字段除了可以从源表中获取,也可以从计算字段中获取 PARTITION(`year`) <=> `year(order_time)` )
虚拟分区的创建:只能使用动态分区的语法,并且需要指定值。
动态分区和静态分区的区别:
静态分区:
分区键值必须在插入数据时明确指定
可以向一个分区内导入数据
动态分区:
分区键值在插入数据时动态指定
可以向多个分区内导入数据
查看分区信息
SHOW PARTITIONS TABLE_NAME;
手动添加分区
ALTER TABLE hive_internal_par_ext_regex_test1w ADD PARTITION(year=2021);
可以将符合数据结构的文件直接放到分区的路径下,从而实现分区数据的添加。
手动删除分区
ALTER TABLE hive_internal_par_ext_regex_test1w DROP PARTITION(year=2021);
eg:设置一个定时任务,定时删除三年前同月份的分区,并添加当年新月份的数据(一个月结束之后才能获取到该月完整的分区数据)
dt=$(date -d '-1 month' '+%F')
del_year=$(date -d "$dt -3 year" '+%Y')
del_month=$(date -d "$dt -3 year" '+%m')
add_year=$(date -d '+%Y')
add_month=$(date -d '+%m')
hive -e "ALTER TABLE hive_internal_par_ext_regex_test1w DROP PARTITION(year=$del_year,month=$del_month);"
if [ $? -eq 0 ];then
echo "SUCCESS TO DROP PREVIOUS PARTITION"
else
echo "FAIL TO DROP PREVIOUS PARTITION"
fi
hive -e "ALTER TABLE hive_internal_par_ext_regex_test1w ADD PARTITION(year=$add_year,month=$add_month);"
if [ $? -eq 0 ];then
echo "SUCCESS TO ADD NEW PARTITION"
else
echo "FAIL TO ADD NEW PARTITION"
fi
clustered by (FIELD_NAME) INTO 2 BUCKETS
create table if not exists hive_internal_par_cluster_ext_regex_test1w(
user_id int,
user_gender string,
order_time timestamp,
order_amount decimal(10,2)
)
partitioned by (year int)
clustered by (order_time) into 4 buckets
row format delimited
fields terminated by ','
stored as textfile;
INSERT OVERWRITE TABLE hive_internal_par_cluster_ext_regex_test1w PARTITION(year)
SELECT *, year FROM hive_ext_regex_test1w where year(order_time)>=2014;
分桶抽样
随机抽样基于随机列
SELECT * FROM TABLE_NAME TABLESAMPLE(BUCKET 3 OUT OF 4 ON rand()) s;
随机抽样基于指定列(使用分桶列更高效)
SELECT * FROM TABLE_NAME TABLESAMPLE(BUCKET 3 OUT OF 32 ON id) s;
** 基于随机列和指定列的区别:
基于随机列的抽样提供了数据的随机和均匀代表性,适合于需要估计整个数据集的参数时。
基于指定列的抽样则适用于对数据集的特定部分感兴趣的情况,例如分析特定区域的销售数据或特定时间段的用户行为。
** 如何理解`BUCKET x OUT OF y`?
即将原本的桶分成y份后的顺序的第x个。
假设Num Buckets=4,x=2,y=8,明显桶的份数不够8份,因此将4个桶切分为8份,并且获取到第二份的数据。
![bucket抽样](./assets/bucket%E6%8A%BD%E6%A0%B7.png)
bucket的顺序是A1,B1 , C1 , D1 , A2 , B2 , C2 , D2
假设y<Num Buckets,那么直接假设合并即可。
未抽样数据: 抽样数据:
2014,945 2014,234
2015,931 2015,210
2016,858 1/4 2016,205
2017,909 ===> 2017,220 ===> 对每个分区数据进行抽样
2018,911 2018,224
2019,928 2019,224
临时表
如何判断该表是否为临时表?
通过命名看出来,如果有_tmp_的命名则是临时表;
SHOW CREATE TABLE TABLE_NAME;
使用环境:
Hive UI
Tableau(数据呈现),Zeppelin(交互)