三十分钟学会Hive

ROS2实战:打造智能巡检机器人的关键技术与实现 本文详细介绍了基于ROS2和Navigation2构建智能巡检机器人的完整实战流程。通过设计主从协作的事件驱动架构,实现了机器人在已知地图中的自动导航、定点语音播报和环境图像采集功能。文章涵盖了从环境搭建、仿真配置到核心控制节点、语音服务与图像记录模块的代码实现,为开发者打造实用的自动巡检机器人提供了清晰的技术指南。 阅读详情

Hive的概念与运用

Hive 是一个构建在Hadoop 之上的数据分析工具(Hive 没有存储数据的能力,只有使用数据的能力),底层由 HDFS 来提供数据存储,可以将结构化的数据文件映射为一张数据库表,并且提供类似 SQL 的查询功能,本质就是将 HQL 转化成 MapReduce 程序。

Hive和传统数据库的区别

在这里插入图片描述

Hive和传统数据库的区别在于数据存储和处理⽅式不同
1.Hive是基于Hadoop的分布式数据存储和处理平台,适合处理⼤规模数据集,⽽传统的关系型数据库则着重于⾼度规范化的数据模式和事务处理。
2.Hive使⽤类SQL语⾔HQL进⾏查询和数据操作,可以将查询转换为MapReduce任务进⾏处理。同时,Hive⽀持⾃定义函数和UDF,能够⽅便地添加⾃定义逻辑来处理数据。
3.传统数据库的查询语⾔通常使⽤SQL,数据模式被⾼度规范化以确保数据的⼀致性和可靠性。传统数据库还⽀持ACID(原⼦性、⼀致性、隔离性和持久性)事务,可以确保事务的安全性和完整性。

Hive 优缺点

优点:

  1. 操作接口采用类似 SQL 的语法,提供快速开发的能力(简单、容易上手)。
  2. 免去了写 MapReduce 的过程,减少开发人员的学习成本。
  3. Hive 的执行延迟比较高,因此 Hive 常用于离线数据分析,对实时性要求不高的场合。
  4. Hive 优势在于处理大数据,对于处理小数据没有优势,因为 Hive 的执行延迟比较高。
  5. Hive 支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
  6. 集群可自由拓展并且具有良好的容错性,节点出现问题 SQL 仍可完成执行。

缺点:

  1. Hive 的 HQL 表达能力有限,当逻辑需求特别复杂的时候,还是要借助 MapReduce。
    迭代式算法无法表达

    数据挖掘方面不擅长

  2. Hive 操作默认基于 MapReduce 引擎,而 MapReduce 引擎与其它的引擎(如 Spark 引擎)相比,特点就是慢、延迟高、不适合交互式查询,所以 Hive 也有这个缺点(这里讲的是默认引擎,Hive 是可以更改成其他引擎的)。

  3. Hive 自动生成的 MapReduce 作业,通常情况下不够智能化。

  4. Hive 调优比较困难,粒度较粗。

Hive架构

在这里插入图片描述

Client

Hive 允许 Client 连接的方式有三个 CLI(Hive Shell)、JDBC/ODBC(Java 访问 Hive)、WEBUI(浏览器访问 Hive)。

MetaStroe

通常用来存放:元数据,数据的数据。

元数据包括:表名、表结构信息、表所属的数据库(默认是 default 库)、表的拥有者(权限信息)、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等。

元数据的存放一般需要借助于其他的数据载体(Derby 或 MySQL),默认存放在自带的 Derby 数据库(单用户局限性)中,推荐使用 MySQL 进行存储。连接数据库需要提供:uri、username、password、driver。

元数据服务的作用是:客户端连接 MetaStroe 服务,MetaStroe 服务再去连接 MySQL 数据库来存取元数据。

Driver

在这里插入图片描述

解析器(SQL Parser):将 SQL 字符串转换成抽象语法树 AST,这一步一般都用第三方工具库完成,比如 ANTLR;对AST 进行语法分析,分析查询语法和查询计划,检查 SQL 语义是否有误。

编译器(Compiler):获取元数据,检查表是否存在、字段是否存在,然后将 AST 编译生成逻辑执行计划。

优化器(Query Optimizer):对逻辑执行计划进行优化。

执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。最后根据优化后的物理执行计划生成底层代码进行执行,对于 Hive 来说,就是 MR/Spark。

Hive 工作原理

当创建表的时候,需要指定 HDFS 文件路径,表和其文件路径会保存到 MetaStore,从而建立表和数据的映射关系。当数据加载入表时,根据映射获取到对应的 HDFS 路径,将数据导入。
用户输入 SQL 后,Hive 会将其转换成 MapReduce 或者 Spark 任务,提交到 YARN 上执行,执行成功将返回结果。

在这里插入图片描述

Hive 交互方式

hive
beeline -u jdbc:hive2://node01:10000 -n root
使用 -e 参数来直接执行 HQL 的语句。
bin/beeline -u "jdbc:hive2://node01:10000/default" hive -e "SHOW DATABASES;"
使用 -f 参数通过指定文本文件来执行 HQL 的语句。
bin/beeline -u "jdbc:hive2://node01:10000/default" hive -f "default.sql"

Hive 基础

数据库

创建数据库

创建一个数据库,数据库在 HDFS 上默认的存储路径是 /hive/warehouse/*.db 。

避免要创建的数据库已经存在错误,可以使用 IF NOT EXISTS 选项来进行判断。(标准写法)

CREATE DATABASE IF NOT EXISTS crm;

指定数据库创建的位置(数据库在 HDFS 上的存储路径)。

CREATE DATABASE IF NOT EXISTS school location '/hive/school.db';
修改数据库

用户可以使用 ALTER DATABASE 命令为某个数据库的 DBPROPERTIES 设置键-值对属性值,来描述这个数据库的属性信息。数据库的其他元数据信息都是不可更改的,包括数据库名和数据库所在的目录位置。

ALTER DATABASE school SET DBPROPERTIES('createtime'='20220803');
数据库详情

显示所有数据库。

SHOW DATABASES;

可以通过 like 进行过滤。

SHOW DATABASES LIKE 's*';

查看某个数据库的详情。

DESC DATABASE school;
DESCRIBE DATABASE school;

切换数据库。

USE school;
删除数据库
DROP DATABASE IF EXISTS school;

如果数据库不为空,使用 CASCADE 命令进行强制删除。

DROP DATABASE IF EXISTS school CASCADE;

数据表

CREATE DATABASE IF NOT EXISTS test;
CREATE TABLE IF NOT EXISTS test.t_user (
id int,
username string,
password string,
gender string,
age int
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n';
LOAD DATA INPATH '/yjx/user.txt' INTO TABLE test.t_user;

简单的查询不会执行 MapReduce,复杂的查询会调用 MapReduce 模板生成 MapReduce 任务。

表详情

显示所有数据表。

SHOW TABLES;

可以通过 like 进行过滤。

SHOW TABLES LIKE 't*';

查看某个数据表的详情。

DESC t_person;
DESC FORMATTED t_person;
DESCRIBE FORMATTED t_person;

重命名

内部表会同时修改文件目录,外部表因为目录是共享的,所以不会修改目录名称。

ALTER TABLE old_table_name RENAME TO new_table_name;

修改列

-- 添加列
ALTER TABLE table_name ADD COLUMNS (new_col INT);
-- 一次增加一个列(默认添加为最后一列)
ALTER TABLE table_name ADD COLUMNS (new_col INT);
-- 可以一次增加多个列
ALTER TABLE table_name ADD COLUMNS (c1 INT, c2 STRING);
-- 添加一列并增加列字段注释
ALTER TABLE table_name ADD COLUMNS (new_col INT COMMENT 'a comment');
-- 更新列
ALTER TABLE table_name CHANGE old_col new_col STRING;
-- 将列 a 的名称更改为 a1
ALTER TABLE table_name CHANGE a a1 INT;
-- 将列 a1 的名称更改为 a2,将其数据类型更改为字符串,并将其放在列 b 之后
ALTER TABLE table_name CHANGE a1 a2 STRING AFTER b;
-- 将 c 列的名称改为 c1,并将其作为第一列
ALTER TABLE table_name CHANGE c c1 INT FIRST;

清空表

TRUNCATE TABLE table_name;

注意:清空表只能删除内部表的数据(HDFS 文件),不能删除外部表中的数据。
在 SQL 中,TRUNCATE 属于 DDL 语句,主要功能是彻底删除数据,使其不能进行回滚。

删除表

DROP TABLE table_name;

注意:删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。

内外部表

内部表和外部表的区别
  1. 存储位置:内部表的数据存储在 Hive 的默认文件系统中,外部表的数据存储在外部文件系统中。
  2. Hive 内部表的管理既包含逻辑以及语法上的,也包含实际物理意义上的,即创建 Hive 内部表时,数据将真实存在于表所在的目录内,删除内部表时,物理数据和文件也⼀并删除。默认创建的是内部表。 外部表(external table)则不然,其管理仅仅只是在逻辑和语法意义上的,即新建表仅仅是指向⼀个外部目录。同样,删除时也并不物理删除外部⽬录,仅仅是将引⽤和定义删除 。
  3. 数据管理:对于内部表,当 DROP TABLE 时,表的元数据和数据都会被删除。⽽对于外部表,DROP TABLE 仅会删除表的元数据,数据仍然存在于外部⽂件系统中。
  4. 数据加载:对于内部表,数据可以通过 LOAD DATA 或 INSERT INTO 命令加载,且Hive 会负责将数据直接写⼊表的默认⽂件系统中。⽽对于外部表,数据必须通过外部⽂件系统中的⽂件进⾏加载,并且不能使⽤ INSERT INTO 命令。外部表的创建需要使⽤EXTERNAL关键字。
  5. 数据更新:对于内部表,可以使⽤ UPDATE 和 DELETE 命令来更新和删除数据。⽽对于外部表,由于数据存储在外部⽂件系统中,⽆法直接更新或删除其中的数据。

总的来说,内部表和外部表在数据存储、管理和更新⽅⾯有很⼤的差别,选择哪种类型的表应根据具体的场景需求⽽定。

内部表

即创建 Hive 内部表时,数据将真实存在于表所在的目录内,删除内部表时,物理数据和文件也一并删除。默认创建的是内部表。Hive 内部表的管理既包含逻辑以及语法上的,也包含实际物理意义
上的。

-- 创建表
CREATE TABLE IF NOT EXISTS test.t_user (
id int,
username string,
password string,
gender string,
age int
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n';
-- 载入数据
LOAD DATA INPATH '/yjx/user/user.txt' INTO TABLE test.t_user;

执行 LOAD DATA 语句后 user.txt 文件将会被移动到 t_user 表所在路径下(/hive/warehouse/test.db/t_user/user.txt )。此时如果使用 DROP 语句删除 t_user 后,其数据和表的元数据都会被删除。

外部表

外部表(external table)则不然,其管理仅仅只是在逻辑和语法意义上的,即新建表仅仅是指向一个外部目录而已。同样,删除时也并不物理删除外部目录,而仅仅是将引用和定义删除。数据的创建和删除完全由自己控制,Hive 不管理这些数据。

方案一:创建时指定数据位置*

数据的位置可以在创建时指定,在 Hive 的数据仓库 /hive/warehouse/test.db 目录下不会创建 t_user 目录。

CREATE EXTERNAL TABLE IF NOT EXISTS test.t_user (
id int,
username string,
password string,
gender string,
age int
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
-- 可以指定到某个目录(该目录下的文件都会被扫描到),也可以指定到某个具体的文件
LOCATION '/yjx/user';

提示:如果 /yjx/user 目录不存在 Hive 会帮我们自动创建,我们只需要将 t_user 表所需的数据上传至 /yjx/user 目录即可。或者数据已经存在于 HDFS 某个目录,Hive 创建外部表时直接指定数据位置即可。

方案二:先建表再导入

也可以先单独建立外部表,建立时不指定数据位置,然后通过 LOAD DATA 命令载入数据。

CREATE EXTERNAL TABLE IF NOT EXISTS test.t_user2 (
id int,
username string,
password string,
gender string,
age int
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n';

在这种情况下,Hive 的数据仓库 /hive/warehouse/test.db 目录下会创建 t_user2 目录。
然后通过 LOAD DATA 命令载入数据。

LOAD DATA INPATH '/yjx/user/user.txt' INTO TABLE test.t_user2;

注意:在这种情况下,执行 LOAD DATA 语句后 user.txt 文件将会被移动到 t_user2 表所在路径下(/hive/warehouse/test.db/t_user2/user.txt )。唯一的区别是,外部表此时如果使用 DROP 语句删除t_user2 后,只会删除元数据,也就是说 t_user2 目录和 user.txt 数据并不会被删除。

推荐使用方案一,数据不会被随意移动,更易于团队使用。

导出数据

1通过 SQL 操作
将查询结果导出到本地

首先在 HiveServer2 的节点上创建一个存储导出数据的目录。

mkdir -p /root/user
-- 将查询结果导出到本地
INSERT OVERWRITE LOCAL DIRECTORY '/root/user' SELECT * FROM t_user;

如果需要按指定的格式将数据导出到本地。

-- 按指定的格式将数据导出到本地
INSERT OVERWRITE LOCAL DIRECTORY '/root/person'
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY '-'
MAP KEYS TERMINATED BY ':'
LINES TERMINATED BY '\n'
SELECT * FROM t_person;
将查询结果输出到 HDFS

首先在 HDFS 上创建一个存储导出数据的目录。

hdfs dfs -mkdir -p /yjx/export/user
-- 将查询结果导出到 HDFS
INSERT OVERWRITE DIRECTORY '/yjx/export/user'
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
SELECT * FROM t_user;
2通过 HDFS 操作

首先在 HDFS 上创建一个存储导出数据的目录。

hdfs dfs -mkdir -p /yjx/export/person

使用 HDFS 命令拷贝文件到其他目录。

hdfs dfs -cp /hive/warehouse/t_person/* /yjx/export/person
将元数据和数据同时导出

首先在 HDFS 上创建一个存储导出数据的目录。

hdfs dfs -mkdir -p /yjx/export/person
-- 将表结构和数据同时导出
EXPORT TABLE t_person TO '/yjx/export/person';

注意:时间不同步,会导致导入导出失败。

基本查询

内置运算符与函数
-- 查看系统自带函数
SHOW FUNCTIONS;
-- 显示某个函数的用法
DESC FUNCTION UPPER;
-- 详细显示某个函数的用法
DESC FUNCTION EXTENDED UPPER;

sort by、order by、distrbute by、cluster by 的区别

  1. (局部排序)SORT BY:和 ORDER BY 类似,⽤于对结果进⾏排序,但是它不保证结果的全局排序,只保证在各个 reducer 中排序。

  2. (全局排序)ORDER BY:⽤于对结果集进⾏排序,可以排序⼀个或多个列,并且可以指定升序或降序排列

  3. (分区排序)DISTRIBUTE BY:⽤于将数据分发到指定的 reducer,但是不保证每个 reducer中的数据是有序的。如果需要排序再使⽤ ORDER BY 或 SORT BY。

  4. (分组排序)CLUSTER BY:⽤于对表按照指定的列进⾏分组并且排序,与 GROUP BY 相似。但是CLUSTER BY 是在 Map 端完成的,可以⼤⼤减少数据在 Reduce 端的交换量,提⾼计算效率。

Hive 高级

分区/分桶

在这里插入图片描述

单独的一个表;
对表进行分区;
对表进行分区,再分桶;
对表直接分桶。

分区

使用分区技术,可以避免 Hive 全表扫描,提升查询效率;同时能够减少数据冗余进而提高特定(指定分区)查询分析的效率。

注意,在逻辑上分区表与未分区表没有区别,在物理上分区表会将数据按照分区键的键值存储在表目录的子目录中,目录名为“分区键=键值”。你可以把建立分区想象成建了个文件夹,把一些相似(或者说相同类型)的数据存放到文件夹中。

使用分区表时,尽量利用分区字段进行查询,如果不使用分区字段查询,就会全部扫描,这样就失去了分区的意义。

静态分区

分区表类型分为静态分区和动态分区。区别在于前者是我们手动指定的,后者是通过数据来判断分区的。根据分区的深度又分为单分区与多分区。

创建静态分区表语法(静态分区和动态分区的建表语句是一样的):

-- 单分区:创建分区表 PARTITIONED BY (分区字段名 分区字段类型)
-- 多分区:创建分区表 PARTITIONED BY (分区字段名 分区字段类型, 分区字段名2 分区字段类型2)
CREATE TABLE IF NOT EXISTS t_student (
sno int,
sname string
) PARTITIONED BY (grade int)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

注意: PARTITIONED BY () 括号中指定的分区字段名不能和表中的字段名一样。

需要注意的是,分区查询会将分区中所有的数据都查询出来,所以如果文件中的数据本身已经出问题了,那么查询的结果也会出问题

添加分区(也可以在载入数据时添加分区):

ALTER TABLE t_student ADD IF NOT EXISTS PARTITION (grade=1);

查看分区:

SHOW PARTITIONS t_student;

删除分区:

-- ALTER TABLE 表名 DROP PARTITION(分区字段名=键值)

多分区:

-- 单分区:创建分区表 PARTITIONED BY (分区字段名 分区字段类型)
-- 多分区:创建分区表 PARTITIONED BY (分区字段名 分区字段类型, 分区字段名2 分区字段类型2)
CREATE TABLE IF NOT EXISTS t_teacher (
tno int,
tname string
) PARTITIONED BY (grade int, clazz int)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

注意:前后两个分区的关系为父子关系,也就是 grade 文件夹下面有多个 clazz 子文件夹

动态分区*

静态分区与动态分区的主要区别在于静态分区是手动指定,而动态分区是通过数据来判断分区的。详细来说,静态分区的列是在编译时期通过用户传递来决定的;动态分区只有在 SQL 执行时才能决定。

开启动态分区首先要在 Hive 会话中设置以下参数:

-- 开启动态分区支持(默认 true)
SET hive.exec.dynamic.partition=true;
-- 是否允许所有分区都是动态的,strict 要求至少包含一个静态分区列,nonstrict 则无此要求(默认 strict)
SET hive.exec.dynamic.partition.mode=nonstrict;

其余参数的详细配置如下:

-- 每个 Mapper 或 Reducer 可以创建的最大动态分区个数(默认为 100)
-- 比如:源数据中包含了一年的数据,如果按天分区,即 day 字段有 365 个值,那么该参数就需要设置成大于 365,如果
使用默认值 100,则会报错
hive.exec.max.dynamic.partition.pernode=100;
-- 一个动态分区创建可以创建的最大动态分区个数(默认为 1000)
hive.exec.max.dynamic.partitions=1000;
-- 全局可以创建的最大文件个数(默认为 100000)
hive.exec.max.created.files=100000;
-- 当有空分区产生时,是否抛出异常(默认为 false)
hive.error.on.empty.partition=false;
-- 是否开启严格模式 strict(严格模式)和 nostrict(非严格模式,默认)
hive.mapred.mode=nostrict;
严格模式

开启严格模式,主要是为了禁止某些查询(这些查询可能会造成意想不到的坏结果),目前主要禁止三种类型的查询:
分区表查询时,必须在 WHERE 语句后指定分区字段,否则不允许执行。因为在查询分区表时,如果不指定分区查询,会进行全表扫描。而分区表通常有非常大的数据量,全表扫描非常消耗资源。

ORDER BY 查询必须带有 LIMIT 语句,否则不允许执行。因为 ORDER BY 会进行全局排序,这个过程会将处理的结果分配到一个 Reduce 中进行处理,处理时间长且影响性能。

笛卡尔积查询(多使用 JOIN 和 ON 语句查询)。数据量非常大时,笛卡尔积查询会出现不可控的情况,因此严格模式下也不允许执行。

分区表载入数据
-- 从本地载入
LOAD DATA LOCAL INPATH '/root/teacher.txt' INTO TABLE t_teacher_d;
-- 从 HDFS 载入
LOAD DATA INPATH '/yjx/teacher.txt' INTO TABLE t_teacher_d;
-- 通过查询载入
INSERT INTO OVERWRITE TABLE t_teacher_d PARTITION (grade, clazz) SELECT * FROM t_teacher;
分桶

分桶是将数据集分解为更容易管理的若干部分的另一种技术,也就是更为细粒度的数据范围划分,将数据按照字段划分到多个文件中去。
注意:分区针对的是数据的存储路径,分桶针对的是数据文件。

使用分桶表时,尽量利用分桶字段进行查询,如果不使用分桶字段查询,就会全部扫描,这样就失去了分桶的意义。

分桶原理

Hive 采用对列值哈希,然后除以桶的个数求余的方式决定该条记录要存放在哪个桶中。
计算公式: bucket num = hash_function(bucketing_column) mod num_buckets 。

分桶优势

方便抽样:使抽样(Sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

提高 JOIN 查询效率:获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理某些查询的时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side Join)高效的实现。比如 JOIN 操作。对于 JOIN 操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行 JOIN 操作就可以,可以大大较少 JOIN 的数据量。

分桶实践

开启分桶功能首先要在 Hive 会话中设置以下参数:

-- 开启分桶功能,默认为 false
SET hive.enforce.bucketing=true;
-- 设置 Reduce 的个数,默认是 -1,-1 时会通过计算得到 Reduce 个数,一般 Reduce 的数量与表中的 BUCKETS 数量
一致
-- 有些时候环境无法满足时,通常设置为接近可用主机的数量即可
SET mapred.reduce.tasks=-1;

分桶语法:

CREATE TABLE 表名(字段1 类型1,字段2,类型2 )
CLUSTERED BY (表内字段)
SORTED BY (表内字段)
INTO 分桶数 BUCKETS

动态分区和静态分区的区别

静态分区是指增加数据是需要⼿动指定具体的分区⽬录

静态分区的列实在编译时期,通过⽤户传递列名来决定的

静态分区不管有没有数据都将会创建该分区

动态分区增加数据时不⽤⼿动指定分区⽬录,⽽是由系统通过数据来进⾏判断。

动态分区实在SQL执⾏的时候确定的。

动态分区是有结果集将创建分区,否则不创建。

动态分区虽然⽅便快捷,但创建太多分区时可能会占⽤⼤量资源

Hive 的分区分桶

HIve的分区可以分为静态分区和动态分区。静态分区的规则需要⽤户⾃定义,动态分区由系统通过⽤户数据判断。

Hive分桶主要是采⽤对列值哈希,然后除以桶的个数求余的⽅式决定该条记录要存放在哪个桶中。

分区和分桶的区别主要有以下⼏点:

  1. 分区是按列的值进⾏划分,⽽分桶是按列的哈希值进⾏划分。
  2. 分区是针对表或分区进⾏划分,⽽分桶是针对表或分区中的某个列进⾏划分。
  3. 分区可以提⾼查询效率,⽽分桶可以提⾼查询效率和数据均匀性。

分区和分桶可以根据具体的业务需求进⾏选择:

  1. 如果需要提⾼查询效率,可以使⽤分区或分桶。
  2. 如果需要提⾼数据均匀性,可以使⽤分桶。
  3. 如果需要按业务属性进⾏数据管理,可以使⽤分区。

分区和分桶的注意事项:

  1. 分区和分桶会增加数据存储的成本。
  2. 分区和分桶会影响数据的插⼊和删除操作。

数据抽样

块抽样(Block Sampling)

使用 TABLESAMPLE(n percent) 函数根据 Hive 表数据的大小按比例抽取数据,并保存到新的 Hive 表中。

-- 该方式允许 Hive 随机抽取 N 行数据,数据总量的百分比(n百分比)或 N 字节的数据
SELECT * FROM <Table_Name> TABLESAMPLE(N PERCENT|ByteLengthLiteral|N ROWS) s;

TABLESAMPLE(nM) :指定抽样数据的大小,单位为 M。

TABLESAMPLE(n ROWS) :指定抽样数据的行数,其中 n 代表每个 Map 任务均取 n 行数据,Map 数量可通过 Hive 表的简单查询语句确认(关键词:number of mappers: x)。

TABLESAMPLE(n PERCENT) :按百分比抽样数据,如果数据不超过 128M 还是全量数据。

缺点:不随机。该方法实际上是按照文件中的顺序返回数据,对分区表,从头开始抽取,可能造成只有前面几个分区的数据。
优点:速度快。

分桶抽样 (Smapling Bucketized Table)

Hive 中分桶其实就是根据某一个字段 Hash 取模,放入指定数据的桶中,比如将表 Table_1 按照 ID 分成 100 个桶,其算法是 hash(id) % 100,这样 hash(id) % 100 = 0 的数据会被放到第一个桶中,hash(id) % 100 = 1 的记录被放到第二个桶中。

-- 其中 x 是要抽样的桶编号,桶编号从 1 开始,colname 表示抽样的列,y 表示桶的数量
TABLESAMPLE(BUCKET x OUT OF y [ON colname])

优点 : 随机且速度最快(不走 MR)。

随机抽样(Random() Sampling)

使用 rand() 函数进行随机抽样,Limit 关键字限制抽样返回的数据,其中 RAND() 函数前的 DISTRIBUTE 和 SORT 关键字可以保证数据在 Mapper 和 Reducer 阶段是随机分布的。

-- 使用RAND()函数和LIMIT关键字来获取样例数据,使用DISTRIBUTE和SORT关键字来保证数据随机分散到Mapper和Reducer
-- SORT BY 提供了单个 Reducer 内的排序功能,但不保证整体有序
SELECT * FROM <Table_Name> DISTRIBUTE BY RAND() SORT BY RAND() LIMIT <N rows to sample>;
-- ORDER BY RAND() 语句可以获得同样的效果,但是性能会有所降低
SELECT * FROM <Table_Name> WHERE col=xxx ORDER BY RAND() LIMIT <N rows to sample>;

优点:提供真正的随机抽样。
缺点 : 速度慢。

事务

  1. 不支持 BEGIN、COMMIT、ROLLBACK,所有操作自动提交;
  2. 事务表仅支持 ORC 文件格式;
  3. 表参数 transactional 必须为 true;
  4. 外部表不能成为 ACID 表,不允许从非 ACID 会话读取/写入 ACID 表(例如会话开启了事务,才可以查询事务表,否则查询就会报错);
  5. 默认事务关闭,需要额外配置
# 开启 hive 并发
SET hive.support.concurrency=true;
# 配置事务管理类
SET hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
# 如果事务表配合分区分桶一起使用建议开启以下参数
# 开启分桶功能
SET hive.enforce.bucketing=true;
# 启用自动压缩
SET hive.compactor.initiator.on=true;
# 这里的压缩线程数必须大于 0,理想状态和分桶数一致
SET hive.compactor.worker.threads=2;
# 是否允许所有分区都是动态的,strict 要求至少包含一个静态分区列,nonstrict 则无此要求(默认 strict)
SET hive.exec.dynamic.partition.mode=nonstrict;

视图/物化视图

视图

视图是一个虚拟的表,只保存定义,不实际存储数据,实际查询的时候改写 SQL 去访问实际的数据表。不同于直接操作数据表,视图是依据 SELECT 语句来创建的,所以操作视图时会根据创建视图的 SELECT 语句生成一张虚拟表,然后在这张虚拟表上做 SQL 操作。

-- 创建视图
CREATE VIEW [IF NOT EXISTS] [db_name.]view_name (<列名1>, <列名2>, ...) AS <SELECT语句>;
-- 查看视图
SHOW TABLES;
-- 查看某个视图
DESC view_name;
-- 查看某个视图详细信息
DESC FORMATTED view_name;
-- 修改视图
ALTER VIEW [db_name.]view_name AS <SELECT语句>;
-- 删除视图
DROP VIEW [IF EXISTS] [db_name.]view_name [, <视图名2>, ...];
优缺点

总结:

  1. Hive 中的视图是一种虚拟表,只保存定义,不实际存储数据;
  2. 通常从真实的物理表查询中创建生成视图,也可以从已经存在的视图上创建新视图;
  3. 创建视图时,将冻结视图的结构,如果删除或更改基础表,则视图将失败;
  4. 视图是用来简化操作的,不缓冲记录,也不会提高查询性能。

优点:

  1. 通过视图可以提高数据的安全性,将真实表中特定的列提供给用户,保护数据隐私;
  2. 上层的业务查询和底层数据表解耦,业务上可以查的一张表,但是底层可能映射的是三张或多张表的数据;
  3. 修改底层数据模型只需要重建视图即可,不需要上层业务修改业务逻辑;
  4. 降低查询复杂度,优化查询语句(注意不是提高查询效率)。

缺点:

  1. 无法再对视图进行优化,而且并没有提升查询速度,只是使上层的业务逻辑变得更清晰简洁。
物化视图

物化视图(Materialized View),是一个包括查询结果的数据库对象,可以用于预先计算并保存表连接或聚集等耗时较多的操作结果。在执行查询时,就可以避免进行这些耗时的操作,从而快速的得到结果。

物化视图只可以在事务表上创建。

-- 创建物化视图
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db_name.]materialized_view_name
[DISABLE REWRITE]
[COMMENT materialized_view_comment]
[PARTITIONED ON (col_name, ...)]
[CLUSTERED ON (col_name, ...) | DISTRIBUTED ON (col_name, ...) SORTED ON (col_name, ...)]
[
[ROW FORMAT row_format]
[STORED AS file_format] | STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)]
]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]
AS
<query>;
-- 物化视图是一种特殊的数据表,可以使用 SHOW TABLES 等语法
-- 删除物化视图
DROP MATERIALIZED VIEW [db_name.]materialized_view_name;
刷新物化视图
增量刷新

当物化视图满足一定条件时,默认会执行增加刷新,即只刷新原始源表中的变动会影响到的数据,增量刷新会减少重建步骤的执行时间。要执行增量刷新,物化视图的创建语句和更新源表的方式都须满足一定条件:

  1. 物化视图只使用了事务表;
  2. 如果物化视图中包含 GROUP BY,则该物化视图必须存储在 ACID 表中,因为它需要支持 MERGE 操作。对于由 Scan-Project-Filter-Join 组成的物化视图,不存在该限制。
定时刷新

可以通过 SET hive.materializedview.rewriting.time.window=10min; 设置定期刷新,默认为 0min。该参数也可以作为建表语句的一个属性,在建表时设置。

全量刷新

若只用 INSERT 更新了源表数据,可以对物化视图进行增量刷新。若使用 UPDATE、INSERT 更新了源表数据,那么只能进行重建,即全量刷新(REBUILD)。

当数据源变更(新数据插入 Inserted、数据被修改 Modified),物化视图也需要更新以保持数据一致性,需要用户主动触发 Rebuild

ALTER MATERIALIZED VIEW [db_name.]materialized_view_name REBUILD;

注意:如果一张表创建了许多物化视图,那么在数据写入这张表时,可能会消耗许多机器的资源,比如数据带宽占
满、存储增加等等。

物化视图和视图的什么区别

物化视图和视图都是数据库中的数据对象,但它们的性质和⽤途有所不同:

  1. 视图是⼀种虚拟表,它并不实际存储数据,⽽是基于⼀个或多个表的查询结果以某种特定⽅式创建的。
  2. 视图可以⽤来简化复杂的查询,隐藏数据的细节,减少应⽤程序的代码量,提⾼性能等。
  3. 物化视图(Materialized View)是⼀种实际存储数据的表,它是基于⼀个或多个表的查询结果以某种特定⽅式创建的,与视图不同的是,物化视图存储查询结果,⽽不是每次查询时动态⽣成。具体来说,当创建物化视图时,数据库会执⾏查询操作,并将查询结果存储在⼀个表中。在之后的查询中,数据库可以直接使⽤物化视图中的数据,⽽不需要再次执⾏查询操作,从⽽提⾼查询性能。

总的来说,物化视图和视图的区别在于视图是⼀种虚拟表,不实际存储数据,物化视图是⼀种实际存储数据的表。视图适合处理动态数据集,物化视图适合处理静态数据集,它们应根据具体场景需要选择使⽤。

在这里插入图片描述

高级查询

一行变多行(行转列)

EXPLODE() 可以将 Hive 一行中复杂的 Array 或者 Map 结构拆分成多行,配置 SPLIT 函数一起使用可将某个列的数据转为数组。

多行变一行(列转行)

COLLECT_SET() 和 COLLECT_LIST() 可以将多行数据转成一行数据,区别就是 LIST 的元素可重复而 SET 的元素是去重的。

MySQL 实现方式: GROUP_CONCAT([DISTINCT] 要连接的字段 [ORDER BY 排序字段 ASC/DESC] [SEPARATOR‘分隔符’])

URL 解析

侧视图 LATERAL VIEW 配合 PARSE_URL_TUPLE 函数可以实现 URL 字段的一列变多列。

JSON 解析

使用 JSON 函数处理:
GET_JSON_OBJECT(json_txt, path)

​ 第一个参数:指定要解析的 JSON 字符串
​ 第二个参数:指定要返回的字段,通过 $.column_name 的方式来指定

JSON_TUPLE(jsonStr, p1, p2, …, pn)

​ 第一个参数:指定要解析的 JSON 字符串
​ 第二个参数:指定要返回的第 1 个字段
​ ……
使用 JsonSerDe:建表时指定 JSON 序列化器,加载 JSON 文件到表中时会自动解析为对应的表格式

窗口函数

窗口函数可以为窗口中的每行都返回一个值。就是在查询的结果上再多出一列,这一列可以是聚合值,也可以是排序值

窗口函数语法

窗口函数指的就是 OVER() 函数,其窗口是由一个 OVER 子句定义的多行记录。窗口函数一般分为三类:聚合型窗口函数和分析型窗口函数以及取值型窗口函数。

SELECT XX函数() OVER (PARTITION BY 用于分组的列 ORDER BY 用于排序的列 ROWS/RANGE BETWEEN 开始位置 AND 结
束位置);

XX函数() :聚合型窗口函数/分析型窗口函数/取值型窗口函数

OVER() :窗口函数

​ PARTITION BY :后跟分组的字段,划分的范围被称为窗口
​ ORDER BY :决定窗口范围内数据的排序方式,当 ORDER BY 与聚合函数一起使用时,会形成顺序聚合,如 SUM 聚合与 ORDER BY 结合使用时,就实现类似于累计和的效果。

移动窗口 :

​ 移动方向:

​ CURRENT ROW :当前行
​ PRECENDING :向当前行之前移动
​ FOLLOWING :向当前行之后移动
​ UNBOUNDED :起点或终点(一般结合 PRECEDING,FOLLOWING 使用)
​ UNBOUNDED PRECEDING :表示该窗口第一行(起点)
​ UNBOUNDED FOLLOWING :表示该窗口最后一行(终点)
移动范围: ROWS 和 RANGE

与 GROUP BY 的区别:

结果数据形式
窗口函数可以在保留原表中的全部数据
GROUP BY 只能保留与分组字段聚合的结果
排序范围不同
窗口函数中的 ORDER BY 只是决定着窗口里的数据的排序方式
普通的 ORDER BY 决定查询出的数据以什么样的方式整体排序
SQL 顺序
GROUP BY 先进行计算
窗口函数在 GROUP BY 后进行计算

移动窗口(滑动窗口)

移动方向:
CURRENT ROW :当前行
PRECENDING :向当前行之前移动
FOLLOWING :向当前行之后移动
UNBOUNDED :起点或终点(一般结合 PRECEDING,FOLLOWING 使用)
UNBOUNDED PRECEDING :表示该窗口第一行(起点)
UNBOUNDED FOLLOWING :表示该窗口最后一行(终点)

移动范围:
ROWS :ROWS 后定义窗口从哪里开始(当前行也参与计算),与 BETWEEN 搭配可以表示范围。如果省略 BETWEEN仅指定一个端点,那么将该端点视为起点,终点默认为当前行。ROWS 会根据 ORDER BY 子句排序后,按分组后排序列的顺序取前 N 行或后 N 行进行计算(当前行也参与计算)。
ROWS 2 PRECEDING :窗口从当前行的前两行开始计算,计算到当前行;
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW :等同于上一句;
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING :窗口从当前行开始计算,计算到当前行的后两行;
ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING :窗口从当前行的前两行开始计算,计算到当前行的下一
行,当前行也参与计算;
ROWS UNBOUNDED PRECEDING :窗口从第一行(起点)计算到当前行;
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW :等同于上一句;
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING :窗口从当前行计算到最后一行(终点);
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING :窗口从第一行(起点)计算到当前行下一行;
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING :窗口从第一行(起点)计算到最后一行(终点)。
RANGE :RANGE 后定义窗口从哪里开始(当前行也参与计算),与 BETWEEN 搭配可以表示范围。如果省略
BETWEEN 仅指定一个端点,那么将该端点视为起点,终点默认为当前行。RANGE 会根据 ORDER BY 子句排序后,按分组后排序列的值的整数区间取前 N 行或后 N 行进行计算(相同排序值的行都会被算进来,当前行也参与计算)。
RANGE 的窗口范围子句语法与 ROWS 一模一样,唯一的区别就在于 RANGE 会根据 ORDER BY 子句排序后,按分组后排序列的值的整数区间取前 N 行或后 N 行进行计算(相同排序值的行都会被算进来,当前行也参与计算)。
整数区间解释:如果窗口范围子句为 RANGE BETWEEN 2 PRECEDING AND CURRENT ROW ,假设排序列的值为
1 2 3 4 5 7 8 11,计算规则如下:
1 => 1,因为前面没有任何行,所以只有自己
2 => 1 + 2,因为前面只有一行,所以只加了 1
3 => 1 + 2 + 3,前面两行加当前行
4 => 2 + 3 + 4,前面两行加当前行
5 => 3 + 4 + 5,前面两行加当前行
7 => 5 + 6 + 7,因为 6 不存在,所以实际上只加了 5
8 => 6 + 7 + 8,因为 6 不存在,所以实际上只加了 7
11 => 9 + 10 + 11,因为 9 和 10 都不存在,所以实际上只有自己

当 ORDER BY 缺少窗口范围子句时,窗口范围子句默认为: RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 。
当 ORDER BY 和窗口范围子句都缺失时,窗口范围子句默认为: ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 。
分析型窗口函数

RANK() :间断,相同值同序号,例如 1、2、2、2、5。
DENSE_RANK() :不间断,相同值同序号,例如 1、2、2、2、3。
ROW_NUMBER() :不间断,序号不重复,例如 1、2、3、4、5(2、3 可能是相同的值)。

PERCENT_RANK() :计算小于当前行的值在所有行中的占比:小于 x 的行数 / 窗口或 PARTITION 分区内的总行数。其中 x 等于 ORDER BY 子句中指定的列的当前行中的值。
CUME_DIST() :计算小于等于当前行的值在所有行中的占比。
NTILE(N) :如果把数据按行数分为 N 份,那么该行所属的份数是第几份。注意:N 必须为 INT 类型。

取值型窗口函数

LAG(COL, N, DEFAULT_VAL) :往前第 N 行数据,没有数据的话用 DEFAULT_VAL 代替。
LEAD(COL, N, DEFAULT_VAL) :往后第 N 行数据,没有数据的话用 DEFAULT_VAL 代替。
FIRST_VALUE(EXPR) :分组内第一个值,但是不是真正意义上的第一个,而是截至到当前行的第一个。
LAST_VALUE(EXPR) :分组内最后一个值,但是不是真正意义上的最后一个,而是截至到当前行的最后一个。

自定义函数

UDF(普通函数,⼀进⼀出,可以⽤于字符串处理、⽇期处理)

实现 UDF 的方式有两种:

​ 第一种是比较简单的形式,继承 UDF 类通过 evaluate 方法实现,目前已过时。

​ 第二种是继承 GenericUDF 重写 initialize 方法、evaluate 方法、getDisplayString 方法实现。

UDAF(聚合函数,多进⼀出,可以⽤于求平均值、求最⼤值、求最⼩值等场景)

实现 UDAF 的方式有两种:

​ 第一种是比较简单的形式,先继承 UDAF 类,然后使用静态内部类实现 UDAFEvaluator 接口,目前已过时。

​ 第二种是先继承 AbstractGenericUDAFResolver 类重写 getEvaluator 方法,然后使用静态内部类实现
GenericUDAFEvaluator 接口。

UDTF(表⽣成函数,⼀进多出,⽤于数据拆分、数据过滤等场景 )

​ 实现 UDTF 需要继承的 GenericUDTF,然后重写⽗类的三个抽象⽅法( initialize , process ,close ),输出后有⼏列,在 initialize 中定义,主要处理逻 辑在 process 中实现。

将自定义 UDTF 程序打成 jar 包并上传至 HDFS。

在 Hive 中定义自定义函数。

重新加载函数。

Hive自定义函数的作用是为了解决系统内置函数无法满足实际业务需求的问题,开发者可以根据自身的业务需求编写函数来实现个性化的功能。

Hive 压缩/存储

压缩技术能够有效减少存储系统的读写字节数,提高网络带宽和磁盘空间的效率。

注意:压缩特性运用得当能提高性能,但运用不当也可能降低性能。

压缩的优点:减少存储系统读写字节数、提高网络带宽和磁盘空间的效率。
压缩的缺点:使用数据时需要先对文件解压,加重 CPU 负载,压缩算法越复杂,解压时间越长。
压缩的条件:空间和 CPU 要充裕。如果机器 CPU 比较紧张,慎用压缩。
压缩的技术:
有损压缩(LOSSY COMPRESSION):压缩和解压的过程中数据有丢失,使用场景:视频。
无损压缩(LOSSLESS COMPRESSION):压缩和解压的过程中数据没有丢失,使用场景:日志数据。
对称和非对称:
对称:压缩和解压的时间一致。
非对称:压缩和解压的时间不一致。

计算密集型(CPU-Intensive)作业,少用压缩。

IO 密集型(IO-Intensive)作业,多用压缩。

在这里插入图片描述

Hive 压缩

Mapper 压缩
# 开启 Hive 中间传输数据压缩功能
SET hive.exec.compress.intermediate=true

在 Hive 客户端通过命令的方式开启 Mapper 端压缩。

# 开启 Mapper 输出压缩
SET mapreduce.map.output.compress=true;
# 设置 Mapper 输出压缩的压缩方式
SET mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
Reducer 压缩

在 Hive 客户端通过命令的方式启用压缩。

# 开启 Hive 最终结果数据压缩功能
SET hive.exec.compress.output=true;

在 Hive 客户端通过命令的方式开启 Reducer 压缩。

SET hive.exec.compress.output=true;
# 开启 Reducer 输出压缩
SET mapreduce.output.fileoutputformat.compress=true;
# 设置 Reducer 输出压缩的压缩方式
SET mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.BZip2Codec;
# SequenceFiles 输出可以使用的压缩类型:NONE、RECORD 或者 BLOCK
# 如果作业输出被压缩为 SequenceFiles,该属性用来控制使用的压缩格式。默认为 RECORD,即针对每条记录进行压缩,如果将其改为 BLOCK,将针对一组记录进行压缩,这是推荐的压缩策略,因为它的压缩效率更高。
SET mapreduce.output.fileoutputformat.compress.type=BLOCK;

存储

当今的数据处理大致可分为两大类:OLTP 和 OLAP

在这里插入图片描述

OLTP 事务处理

LTP 联机事务处理(On-Line Transaction Processing):OLTP 是传统关系型数据库的主要应用,来执行一些基本的、日常的事务处理,比如数据库记录的增、删、查、改等。

数据计算和数据存储分开,所有用户发过来的请求都是一个事件 Event,事件处理从关系型数据库中查询并进行返回。特点实时性很好,来一个事件处理一个事件,额外数据存储在关系型数据库中。最大问题是能够同时处理的数据有限,数据库做连表查询的代价很高。

OLAP 分析处理

OLAP 联机分析处理(On-Line Analytical Processing):OLAP 则是分布式数据库的主要应用,它对实时性要求不高,但处理的数据量大通常应用于复杂的动态报表系统上。

一般 OLTP 都是使用行式存储,因为实时性要求高,而且有大量的更新操作;OLAP 都是使用列式存储,因为实时性要求不高,主要是要求性能好。

行式存储(Row-oriented)
  1. 查询满足条件的一整行数据的时,只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
  2. 传统的关系型数据库,如 Oracle、DB2、MySQL、SQL SERVER 等都是采用行式存储,在基于行式存储的数据库中,数据是按照行数据为基础逻辑存储单元进行存储的,一行中的数据在存储介质中以连续存储形式存在。
  3. Text File 和 Sequence File 的存储格式都是基于行存储的。
  4. 这种存储格式比较方便进行 INSERT/UPDATE 操作,不足之处就是如果查询只涉及某几个列,它会把整行数据都读取出来,不能跳过不必要的列读取。当然数据比较少,一般没啥问题,如果数据量比较大就比较影响性能,还有就是由于每一行中,列的数据类型不一致,导致不容易获得一个极高的压缩比,也就是空间利用率不高。
列式存储(Column-oriented)

与行存(将每一行的数据连续存储)不同,列存将每一列的数据连续存储。

  1. 在行存模式下,数据按行连续存储,所有列的数据都存储在一个 Block 中,不参与计算的列在 IO 时也要全部读出,读取操作被严重放大。而列存模式下,只需要读取参与计算的列即可,极大的减低了 IO Cost,加速了查询。
  2. 同一列中的数据属于同一类型,压缩效果显著。列存往往有着高达十倍甚至更高的压缩比,节省了大量的存储空间,降低了存储成本。
  3. 更高的压缩比意味着更小的 Data Size,从磁盘中读取相应数据耗时更短。
  4. 自由的压缩算法选择。不同列的数据具有不同的数据类型,适用的压缩算法也就不尽相同。可以针对不同列类型,选择最合适的压缩算法。
  5. 高压缩比意味着同等大小的内存能够存放更多数据,系统 Cache 效果更好。官方数据显示,通过使用列存,在某些分析场景下,能够获得 100 倍甚至更高的加速效应。
  6. INSERT/UPDATE 很麻烦或者不方便,不适合扫描小量的数据。
  7. 列式存储相对于行式存储来说,是新兴的 HBase、HPVertica、EMCGreenplum、ClickHouse 等分布式数据库均采用的存储方式。在基于列式存储的数据库中, 数据是按照列为基础逻辑存储单元进行存储的,一列中的数据在存储介质中以连续存储形式存在。
存储格式
  1. ORC 文件格式压缩比 Parquet 要高,Parquet 文件的数据格式
  2. Schema 要比 ORC 复杂,占用的空间也就越高。
  3. ORC 文件格式的读取效率要比 Parquet 文件格式高。
  4. 如果数据中有嵌套结构的数据,则 Parquet 会更好。
  5. Hive 对 ORC 的支持更好,对 Parquet 支持不好,ORC 与 Hive 关联紧密。
  6. ORC 还可以支持 ACID、Update 操作等。
  7. Spark 对 Parquet 支持较好,对 ORC 支持不好。
  8. 为了数据能够兼容更多的查询引擎,Parquet 也是一种较好的选择。

表的文件存储格式尽量采用 Parquet 或 ORC,不仅降低存储量,还优化了查询,压缩,表关联等性能。

Hive 优化

EXPLAIN 执行计划

EXPLAIN 会将 HQL 语句的依赖关系、实现步骤、实现过程进行解析返回,有助于我们了解 HQL 语句在底层是如何实现数据的查询与处理的,辅助我们对 Hive 进行优化。

EXPLAIN [EXTENDED|CBO|AST|DEPENDENCY|AUTHORIZATION|LOCKS|VECTORIZATION|ANALYZE] query
  1. EXTENDED:提供一些额外信息。通常是像文件名这样的物理信息。
  2. CBO:输出由 Calcite 优化器生成的计划。CBO(Cost Based Optimizer 对查询进行动态评估、获取最佳物理计划) 。
  3. AST:输出查询的抽象语法树。AST 在 Hive 2.1.0 版本删除了,存在 Bug,转储 AST 可能会导致 OOM 错误,将在 4.0.0版本修复。
  4. DEPENDENCY:以 JSON 格式返回查询所依赖的表和分区信息。
  5. AUTHORIZATION:显示执行查询需要授权的条目。
  6. LOCKS:这对于了解系统将获得哪些锁很有用。LOCKS 从 Hive 3.2.0 开始支持。
  7. VECTORIZATION:显示是否启用了向量化查询,以及为什么没有启用的原因。
  8. ANALYZE:用实际的行数注释计划。从 Hive 2.2.0 开始支持。
  9. query:Hive SQL 语句。

查询计划由以下几个部分组成:

  1. The Abstract Syntax Tree for the query:抽象语法树
  2. The dependencies between the different stages of the plan:计划不同阶段之间的依赖关系
  3. The description of each of the stages:阶段描述

SQL 优化

RBO 优化

Rule-Based Optimization,简称 RBO:基于规则优化的优化器,是一种经验式、启发式的优化思路,优化规则都已经预先定义好了,只需要将 SQL 往这些规则上套即可。

谓词下推

谓词下推(Predicate Pushdown)基本思想:将过滤表达式尽可能移动至靠近数据源的位置,以使真正执行时能直接跳过无关的数据。在文件格式使用 Parquet 或 ORC 时,甚至可能整块跳过不相关的文件。

而 Hive 中的谓词下推主要思想是把过滤条件下推到 Map 端,提前执行过滤,以减少 Map 到 Reduce 的传输数据,提升整体性能。简而言之,就是在不影响结果的情况下,尽量将过滤条件提前执行。

Hive 会自动帮助我们优化 SQL 实现谓词下推,但是当 SQL 很复杂的时候可能会导致谓词下推失效。

列裁剪

列裁剪(Column Pruning)也叫投影下推,表示扫描数据源的时候,只读取那些与查询相关的字段。

常量折叠

常量折叠(Constant Folding)也叫表达式折叠,表示将表达式提前计算出结果,然后使用结果对表达式进行替换。

CBO 优化

CBO(Cost-Based Optimization)意为基于代价优化的策略,它需要计算所有可能执行计划的代价,并挑选出代价最小的执行计划。

要使用基于成本的优化,需要在查询开始时设置以下参数:

# 开启 CBO 优化,默认为 true
SET hive.cbo.enable=true;
# 统计 SQL 的查询结果是否从统计信息中获取,默认为 true
SET hive.compute.query.using.stats=true;
# 是否统计列信息,默认为 false
SET hive.stats.fetch.column.stats=true;
# 是否统计分区信息,默认为 true。3.1.1 版本被废弃,不允许用户修改该属性,因为禁用分区状态的获取可能会导致分区
表出现问题
SET hive.stats.fetch.partition.stats=true;

然后统计表的相关信息才能使用 CBO 优化:

 新创建的表或者分区,插入数据时是否统计其信息,默认为 true
# 新创建的表或者分区,如果通过 INSERT OVERWRITE 的方式插入数据,那么 Hive 会自动将该表或分区的统计信息更新到
元数据
SET hive.stats.autogather=true;
# 对于已经存在表或分区可以通过 ANALYZE 命令手动更新其 Statistics 信息
# 统计全表的所有分区的信息
ANALYZE TABLE 表名 COMPUTE STATISTICS;
# 只统计文件数和文件大小,不扫描文件行数,执行较快
ANALYZE TABLE 表名 COMPUTE STATISTICS NOSCAN;
# 统计指定字段的信息
ANALYZE TABLE 表名 COMPUTE STATISTICS FOR COLUMNS 列名1,列名2,列名n;
# 统计指定分区的信息
ANALYZE TABLE 表名 PARTITION(分区列1=值1, 分区列2=值2) COMPUTE STATISTICS;

对于非分区表列的 Statics 信息存在 Hive 元数据表 TABLE_COL_STATS 中;
对于分区表列的 Statics 信息存在 Hive 元数据表 PART_COL_STATS 中。

JOIN 优化

而 Hive JOIN 的底层是通过 MapReduce 来实现的,Hive 实现 JOIN 时,为了提高 MapReduce 的性能,提供了多种 JOIN 方案:

  1. 小表 JOIN 大表的 Map Join

  2. 大表 JOIN 大表的 Reduce Join,Reduce Join 又分为以下两种:

    ​ Bucket Map Join(中型表和大表 JOIN)
    ​ Sort Merge Bucket Join(大表和大表 JOIN)

Map Join

在这里插入图片描述

Map Join 顾名思义,就是在 Map 阶段进行表之间的连接。而不需要进入到 Reduce 阶段才进行连接。这样就节省了在 Shuffle 阶段时要进行的大量数据传输。

Map Join 简单说就是在 Map 阶段将小表读入内存,顺序扫描大表完成 Join。

Reduce Join

在这里插入图片描述

Map 端的主要工作:为来自不同表或文件的数据,进行打标签以区别不同来源的记录。然后用 JOIN 字段作为 Key,其余部分数据和新加的标志作为 Value,最后进行输出。

Reduce 端的主要工作:在 Reduce 端以 JOIN 字段作为 Key 完成分组,接下来只需要在每一个分组中,将那些来源于不同 Map 端的文件的记录进行合并即可。

应用场景:大表(相较于 Map Join 小表的数据更大) JOIN 大表。
实现原理:将两张表的数据在 Shuffle 阶段利用 Shuffle 的分组将数据按照 JOIN 字段进行合并。
具体使用:Hive 会自动判断是否满足 Map Join,如果不满足 Map Join,则会自动执行 Reduce Join。

Bucket Map Join6

两个 JOIN 表都在 Join Key 上都做 Hash Bucket,并且把你打算复制的那个(相对)小表的 Bucket 数设置为大表的倍数。这样数据就会按照 Key Join 做 Hash Bucket。小表依然复制到所有节点,Map Join 的时候,小表的每一组 Bucket 加载成HashTable,与对应的一个大表 Bucket 做局部 JOIN。

更大的表对更大的表

  1. 将两张大表的数据构建分桶
  2. 数据按照分桶的规则拆分到不同的文件中
    分桶规则 = MapReduce 分区的规则 = Key 的 Hash 取余
    Key = 分桶的字段
  3. 只需要实现桶与桶的 JOIN 即可,减少了比较次数
    分桶本质:底层 MapReduce 的分区,桶的个数 = Reduce 个数 = 文件个数。

Map Join 条件:

SET hive.optimize.bucketmapjoin=true; ,默认为 false
所有要 JOIN 的表必须分桶,如果表不是 Bucket 的,则只是做普通 JOIN
大表的 Bucket 数是小表的 Bucket 数的整数倍(或相等)
Bucket 列 == JOIN 列
必须是应用在 Map Join 的场景中

SMB Join

SMB Join 是基于 Bucket Map Join 的有序 Bucket,可实现在 Map 端完成 JOIN 操作,只要桶内的下一条不是,就不用再比较了,有效地减少或避免 Shuffle 的数据量。

SET hive.optimize.bucketmapjoin=true;
SET hive.optimize.bucketmapjoin.sortedmerge=true;
SET hive.auto.convert.sortmerge.join=true;

所有要 JOIN 的表必须分桶,如果表不是 Bucket 的,则只是做普通 JOIN
大表的 Bucket 数 = 大表的 Bucket 数
Bucket 列 == JOIN 列 == SORT 列
必须是应用在 Bucket Map Join 的场景中

数据倾斜

⾸先数据倾斜(Data Skew)是指在分布式系统中,不同的数据分区或计算任务之间存在数据量或计算处理时间的严重不均衡情况。

原因

任务读取大文件,最常见的就是读取压缩的不可分割的大文件。

任务需要处理大量相同键的数据。

  1. 单表聚合操作,部分 Key 数据量较大,且大 Key 分布在很多不同的切片。
  2. 两表进行 JOIN,都含有大量相同的倾斜数据键。
  3. 分组列的数据含有大量无意义的数据,例如空值(NULL)、空字符串等。
  4. 含有倾斜数据在进行聚合计算时无法聚合中间结果,大量数据都需要经过 Shuffle 阶段的处理,引起数据倾斜。
  5. 数据在计算时做多维数据集合,导致维度膨胀引起的数据倾斜。

压缩引发的数据倾斜

单表聚合数据倾斜

JOIN 数据倾斜

业务无关数据引发的数据倾斜

无法消减中间结果的数据量引发的数据倾斜

多维聚合计算数据膨胀引起的数据倾斜

MISRA C 详解 · 专栏06:2023 与 2025 更新总览(相对 2012 的差异) 本专栏 01~05 篇基于(143 规则 + 16 指令)。这六年间标准又前进了两步——(大改)和(微调)。本文补充说明「相对 2012,标准变在哪」,并给出新增规范的代表性示例。 阅读详情

相关推荐

Python《打砖块》小游戏

Python《打砖块》小游戏!

PanDaoxi 1275

hive:DDL

1.库 1.1创建数据库 语法: CREATE DATABASE [IF NOT EXISTS] database_name [COMMENT database_comment] [LOCATION hdfs_path] [WITH DBPROPERTIES (property_name=property_value, ...)]; PS:数据库在HDFS上的默认存储路径是/user/hive/warehouse/*.db(即如果location那儿不指定具体路径,那么创建的database就放在ware

weixin_41391619的博客 262

Qt QChart实战:从零打造一个实时温度监控仪表盘(附完整源码)

本文详细介绍了如何使用Qt QChart模块从零构建一个工业级实时温度监控仪表盘。通过实战案例,展示了波形图的绘制、坐标轴配置、数据流模拟等关键技术,并提供了完整的源码实现,帮助开发者快速掌握Qt在数据可视化领域的应用。

weixin_42537919的博客 243

一文学完所有的Hive SQL(两万字最全详解)

Hive SQL 大全本文基本涵盖了Hive日常使用的所有SQL,因为SQL太多,所以将SQL进行了如下分类:一、DDL语句(数据定义语句):对数据库的操作:包含创建、修改数据库对数据表的操作:分为内部表及外部表,分区表和分桶表二、DQL语句(数据查询语句):单表查询、关联查询hive函数:包含聚合函数,条件函数,日期函数,字符串函数等行转列及列转行:lateral view 与 explode ...

大数据星球-浪尖 1万+

游戏开发实战:5种AOI算法对比(九宫格 vs 灯塔 vs 十字链表)

本文深入对比了游戏开发中五种主流AOI算法(九宫格、灯塔、十字链表等)的性能与适用场景,帮助开发者根据项目规模选择最优解决方案。通过详细的技术分析和实战数据,揭示不同算法在万人同屏等极端场景下的表现差异,为MMORPG等大型多人在线游戏提供关键性能优化指南。

weixin_29268385的博客 474

vue - 网站接入微信扫码登录功能详细教程,完整详细流程及完整功能示例源代码(附常见问题的解决方案与二维码样式修改方案)

vue.js项目植入微信登录,vue网页项目怎么做微信扫码登录,VUE项目中的微信扫码登录,VUE中最简单的js调用微信扫一扫功能,vue:如何设计网页的微信扫码登录功能,微信扫码功能官方文档,通过vue方式实现二维码扫码功能,vue调用微信扫码,vue H5 调用微信扫描二维码,Vue前端实现微信扫码登录,vue-wxlogin 微信授权登录组件,网站引入的微信登录框如何修改宽高或其他样式,wxlogin 组件插件怎么改变宽高,vue-wxlogin组件怎么自定义样式,微信网页授权,vue调用微信JSDK

✅ 精选推荐文章 6366

Hive的常用HiveQL操作

一、Hive简介 Hive是Facebook开发的构建于Hadoop集群之上的数据仓库应用,可以将结构化的数据文件映射为一张数据库表,并提供完整的SQL查询功能,可以将SQL语句转换为MapReduce任务进行运行。 Hive是一个可以提供有效的、合理的且直观的组织和使用数据的模型,即使对于经验丰富的Java开发工程师来说,将这些常见的数据运算对应到底层的MapReduce Java API也...

weixin_44961794的博客 3224

Microsemi Libero系列教程(六)——Identify片上逻辑分析仪的使用

文章目录FPGA如何进行在线调试关于Microsemi片上逻辑分析仪FPGA片上逻辑分析仪原理预期效果0.准备一个创建好的Libero工程1.新建identify工程,并添加想要监测的信号2.管脚分配,编译下载3.设置触发类型4.IICE逻辑分析仪核资源占用 系列教程:Microsemi Libero系列教程 FPGA如何进行在线调试 对于嵌入式系统来说,如单片机,进行硬件级程序调试时,通常采用的是JLink/ST-Link调试器,在线调试的方式来获取程序实时运行的状态,可以观察程序运行流程、各种变量的值

whik1194的博客 1万+

Camoufox 反侦测浏览器实操指南:网页抓取与 AI 代理如何绕过风控检测

Camoufox 是一款基于 Firefox 深度定制的开源反侦测浏览器,面向网页抓取和 AI 代理场景。它每次启动都会生成一套内部自洽的设备身份,并直接在浏览器 C++ 层返回伪装值,再由 Playwright 驱动;网页脚本读不到自动化代码的痕迹。本文讲清它适不适合你的用途、怎么装、身份是怎么拼出来的,以及被检测时该从哪查起。 [![Camoufox 反侦测浏览器伪装启动向导背景,蓝色渐变衬

gitblog_00597的博客 474

YOLOv11改进 | 注意力篇 | YOLOv11引入24年ECCV的自调制特征聚合注意力模块(SMFA),并构建C2PSA_SMFA

1.SMFA介绍1.1 摘要:基于Transformer的图像复原方法由于Transformer的自注意(self-attention,SA)特性能够更好地挖掘非局部信息,从而获得更好的高分辨率图像重建效果,因此具有重要的应用价值。然而,关键点积SA需要大量的计算资源,这限制了其在低功耗器件中的应用。此外,模拟退火机制的低通特性限制了其捕获局部细节的能力,从而导致平滑的重建结果。

tsg6698的博客 952

把 Client 当成 ABAP 系统的多租户底座:从 sy-mandt 到 CDS_CLIENT 的隔离机制与工程实践

本文深入解析SAP ABAP系统中Client概念的本质与应用。Client不仅是数据表的MANDT字段,更是会话级别的隔离边界,贯穿用户登录、授权和数据访问控制全过程。文章从ABAP SQL的隐式Client处理机制出发,详细介绍了USING CLIENT、USING ALL CLIENTS等安全跨客户端访问方式,并对比了传统CLIENT SPECIFIED的风险。同时阐述了ABAP CDS中@ClientHandling注解的类型与算法选择,包括#AUTOMATED自动拼接和#SESSION_VARIA

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 182

解决 java.lang.NoSuchMethodError 报错

NoSuchMethodError 是 Spring Boot 项目中因编译与运行时类版本不一致引发的典型链接错误。本质是方法签名在运行时缺失,常见于依赖冲突、框架升级或缓存残留。排查需定位实际加载类路径,通过 ProtectionDomain 或 Arthas 工具(sc、sm、jad)确认类来源与方法存在性,再结合依赖树分析(如 mvn dependency:tree)排除版本冲突,最终强制统一依赖版本解决。

小李同学的博客 273

YOLOv12改进策略【独家融合改进】| SPD-Conv+PPA 再次提升模型针对小目标的特征提取能力

SPD-Conv是一种新的 CNN 构建模块,用于替代传统 CNN 架构中使用的步长卷积(strided convolution)和池化(pooling)层,它由空间到深度(Space-to-depth,SPD)层和非步长卷积(non - strided convolution)层组成。

Limiiiing的博客 1028

【SQL 数据分析知识点:Hive 差异、窗口函数】

本文通过5张表的小型数据集,系统梳理数仓开发中的高频陷阱与核心技能:分区字段遗漏导致全表扫描(Hive vs MySQL差异)、GROUP BY与窗口函数的本质区别(行数不变、贴列计算)、排序三件套(ROW_NUMBER/RANK/DENSE_RANK)的适用场景,并提炼出“开窗→过滤→聚合”的通用解题骨架,涵盖分组TopN、连续登录等五类经典题型,强调真实业务中排序兜底、口径对齐的重要性,助力高效精准的数据分析。

春风若有怜花意,可否许我再少年 321

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【大数据处理与分析】数据仓库Hive:09 Hive 编程实践

本文介绍Hive的数据类型(基本类型与ARRAY、MAP、STRUCT集合类型)和基本操作,并通过WordCount实例说明Hive编程比MapReduce更简洁易用。

Morse_Chen的博客 277

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

HiVe-Mem 深度解析:多模态智能体记忆的“二维“解法——横向扩召回,纵向省 Token

HiVe-Mem提出多模态智能体记忆的“二维”解法:横向通过文本与视觉锚点构建轻量亲和图,扩召回漏检的相邻记忆;纵向用3层MLP选择器逐条决策证据暴露粒度(摘要/裁剪图/原图等),实现精准降本。在三个2026新基准上,F1与LJ均达第一,Token消耗较最优基线降低90%以上,兼顾性能与效率。

ChesterXue的博客 349

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

服务器和windows客户端重启后,发现agent连不上lightmem和hive mcp了

服务器重启后,lightmem无法连接PostgreSQL(192.168.0.88:18444),日志显示connectECONNREFUSED。排查发现PG服务正常、jail运行中、bridge0成员齐全,但netstat -Iepair10a -b显示收包为0、错误计数持续增长,且arp无响应。最终定位为FreeBSD vnet jail的epair虚拟网卡在重启后“二次入狱”,导致跨vnet转发失效。修复方法:销毁并重建epair对,确保ifconfig epairXa up。同时发现lightme

skywalk8163的专栏 712

hive 集群连环故障根治 —— 工作总结报告 (运维记录/集群无主根 治_20261110.md)

2026年11月10日,针对hive集群频繁无主问题,定位并修复5大根因:主锁饥饿导致周期性假死(优化锁机制与批量提交)、选主逻辑僵化(引入四级择优链替代多数票约束)、生产与开发环境混用(实现守护进程与隔离部署)、网络单向不通阻碍迁移、文档与实际不符。通过重构选举策略、强化容错、完善文档与术语规范,集群稳定性显著提升,已成功推至远端,实现长期稳定运行。

skywalk8163的专栏 374

JitArchives,一款基于JitWord协同AI文档打造的企业级电子档案系统

本文拆解JitArchives智能电子档案系统,针对传统档案“检索难、关联难、溯源难”痛点,提出以证据驱动的AI架构:基于SQLite实现轻量化部署,融合档案Agent与知识图谱,支持自然语言问答、全文检索与关系挖掘,强调后端强校验、权限隔离与可追溯审计。系统不依赖向量库,通过FTS5+TF-IDF实现高效安全的智能化管理,适用于中小企事业单位私有化落地,推动档案从“存得下”迈向“用得好”。

趣谈前端 326

JSP 与 Servlet 结合开发:原理、协作方式与实战示例

JSP与Servlet结合是传统JavaWeb开发的核心模式:Servlet负责业务逻辑与流程控制,JSP专注页面渲染,实现MVC分离。JSP本质为Servlet的模板写法,通过请求转发(forward)共享request作用域数据,确保高效协作。实战中,Servlet处理数据并转发至JSP,JSP使用EL和JSTL动态展示,提升可读性与维护性。推荐将JSP置于WEB-INF下,避免直接访问,保障安全可控。掌握此模式,为理解SpringMVC等现代框架奠定基础。

xyouyuweizhu的博客 318

Hive的多机 AI Agent 协同看板实战:跨机留言、任务搁置、计划跟踪的三个设计取舍

本文介绍多机AI Agent协同系统Hive的三项关键设计取舍:跨机留言采用内存+本地文件存储,避免Git版本冲突与延迟;搁置状态主动释放资源并保留历史,通过按钮而非新增列管理,保持看板简洁;任务计划以Tab形式聚合任务,实现项目级进度追踪。核心原则是“能用文件解决就不加数据库,能复用界面就不新增层级”,所有功能均在零依赖前提下实现高可用与易用性。附带一次/api/plans 404排查,揭示了配置与路由匹配的深层陷阱,具有普遍参考价值。

skywalk8163的专栏 546

Hive任务调优

实测下来,对于小文件多的表,开启CombineHiveInputFormat后Map数量能降一个数量级,任务时间直接砍半。对于大任务而言,200 个分区往往不够,会导致每个分区的数据量过大、GC 压力升高。其原理是:Hive 在运行时检测到某些 key 的数据量超过阈值,就把这些 key 单独拆出来做 MapJoin,从而避免在 Reduce 端出现倾斜。在企业级场景下,这套自动计算经常不太靠谱。存在多个 Join 时,优化器可能调整关联顺序,让小表或过滤后的数据先参与 Join,从而降低中间结果规模。

weixin_45746689的博客 283

archive 归档文件

是编译后的库归档,不是项目压缩包,不能用“Existing Projects into Workspace → Select archive file”来导入项目。归档文件里应当包含 S32DS 能识别的项目结构,比如项目根目录下有。在 S32 Design Studio 导入项目的语境里,,IDE 会读取压缩包里的项目并导入到工作空间。它通常还带压缩,方便备份、传输和分发。否则 IDE 可能扫描不到项目。一个项目文件夹 → 打包成一个。时,你可以直接选中这个。

mingtiauigena的博客 64

ubuntu安装hive

本文介绍基于Hive 4.0.1、Java 8与MapReduce的Hive分布式环境搭建流程:在master节点安装Hive、MySQL并配置元数据存储,通过HDFS保存数据;worker节点复用现有YARN资源。重点包括环境准备、Java 8与MySQL安装、Hive下载配置、JDBC驱动部署、Hive-site.xml连接设置及元数据库初始化。全程以dyl用户操作,确保兼容Hadoop 3.4.3,最终实现单机Hive服务运行,为后续分布式查询验证奠定基础。

qq_43592674的博客 296

ZArchiver安卓压缩解压缩工具下载安装

ZArchiver是一款功能强大的安卓文件压缩工具,支持7z、zip、rar、tar、iso等多种格式的压缩与解压,兼容Unicode文件名(UTF-8),完美显示中文不乱码。具备多线程加速、密码保护、分卷压缩包处理等功能,可创建和解压带密码的压缩文件,优化多核处理器性能,适用于日常高效管理文件。

hflmwl的博客 569

大数据与应用服务赛题复现-Hive 3.1.3 安装与元数据

漏掉 guava 替换时,schematool 与 hive CLI 都会抛 NoSuchMethodError: com.google.common.base.Preconditions.checkArgument,现象是“命令能跑但一启动就崩”,很容易误判为 Hive 装坏。Hive 3.1.3 的安装、元数据库初始化与服务启动过程,覆盖 jar 依赖修正、hive-site.xml 配置、metastore 与 HiveServer2 的启动身份,以及安装完成后的检查点。执行后无输出即为成功。

lightningyang的博客 262
上一篇: 十道Linux常见的面试问题
下一篇: Hive常见的面试题(十二道)
地球魔
博客等级 码龄6年 317粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值