Hive操作命令上手手册

内容来自于《大数据Hive离线计算开发实战》

Hive原理

Hive是一个基于Hadoop的数据仓库和分析系统,用于管理和查询大型数据集。以下是Hive的原理:

  • 数据仓库:Hive将结构化的数据文件映射成一张表,并提供类SQL查询功能。用户可以使用类似SQL的查询语言(HQL)来管理和查询这些数据。Hive的表是纯逻辑表,本质上是Hadoop的目录/文件,达到了元数据与数据存储分离的目的。
  • 转换机制:Hive的工作原理是通过将查询语句转换成MapReduce程序来执行。当用户输入Hive SQL时,它将被转换成一系列的MapReduce操作,从而完成查询的任务。这样可以提高性能,降低运行时间。
  • 运行环境:Hive基于Hadoop,使用HDFS作为存储,使用MapReduce进行计算。Hive还包含一个Hive服务器,这是一个守护进程,用于管理查询和存储结果。
  • 扩展性:Hive支持自定义函数(UDF),使用户可以根据自己的需要定义函数,以满足自己的数据挖掘需求。此外,Hive还支持自定义存储格式,可以将数据存储在不同的格式中,以方便查询。
  • 高效性:Hive支持对HDFS上的数据进行并行处理,支持多种数据查询和分析。通过这种方式,Hive可以提高数据存储和查询的效率,从而提高系统性能。

总的来说,Hive是一个用于大数据处理的工具,其原理是通过将查询语句转换成MapReduce程序来执行,并利用Hadoop和HDFS进行存储和计算。通过这种方式,Hive可以简化复杂的数据挖掘和大数据分析工作,提高系统性能和效率。

对新手来说,你其实可以理解我就是正常操作结构化的数据库,类似于操作MySQL、PostgreSQL等数据库,写普通的SQL语句进行数据查询、分析。那我该篇文章也就是梳理最简单的最全的操作方法,帮助刚上手Hive的程序员快速开始写SQL语句进行查询分析数据。

Hive数据类型

基本数据类型

数据类型长度
TINYINT1byte有符号整数
SMALLINT2byte有符号整数
INT4byte有符号整数
BIGINT8byte有符号整数
BOOLEAN布尔类型 true 或者 false
FLOAT单精度浮点数
DOUBLE双精度浮点数
STRING字符序列,可以指定字符集;可以使用单引号或双引号
TIMESTAMP整数、浮点数或者字符串
BINARY字节数组

集合数据类型

数据类型描述
STRUCT与C语言中的结构 struct类型相似,都可以通过“点”符号访问元素内容。例如,某表中某个列的数据类型为STRUCT(firName STRING,lastName STRING),那么第一个元素可通过字段名.firName来引用
MAPMAP是一个键值对映射集合。例如,表中某个列的数据类型是MAP,存放数据的格式是键:值,通过键就可以获取值
ARRAYARRAY数组是一组具有相同类型变量的集合,这些变量被称为数组的元素,每个元素度有一个下标编号,编号从0开始
STRUCT数据类型案例
create table enterprise.employee(id int,info struct<name:string,age:int>) row format delimited fields terminated by ',' collection items terminated by ':';

数据实例

1,zhangsan:10
2,lisi:20
3,wangwu:30
MAP数据类型
create table enterprise.employee_1(id int,perf Map<string,int>) row format delimited fields terminated by '\t' collection items terminated by ',' map keys terminated by ':';

数据案例

1   job:80,team:60,person:50
2   job:60,team:70
3   job:80,team:90,person:100
ARRAY数据类型
create table enterprise.employee_2(name string,emp_id_list array<int>) row format delimited fields terminated by ',' collection items terminated by ':';

数据案例

zhangsan,1:2:3
lisi,4:5:6
wangwu,7:8:9

Hive数据定义与操作

HiveQL数据定义语言

创建数据库
hive> create database enterprise;

如果数据库enterprise已经存在的话,会跑出一个异常。

所以命令可以改为

hive> create database if not exists enterprise;

查看Hive中所包含的所有数据库

hive> show databases;

查询数据库以e开头数据库

hive> show databases like 'e.*'

我们还可以为每个数据库增加描述

hive>create database if not exists enterprise commit '企业信息表';

我们也能查看数据库enterprise的信息

describe database enterprise;

通过数据库信息可知,在HDFS文件系统上,enterprise数据库是以“.db”结尾,这是Hive数据仓库的设计,用来标识该目录是数据库目录,同时可以知道父目录是/user/hive/warehouse,该父目录层级可以在Hive的配置文件hive-site.xml中的hive.metastore.warehouse.dir属性进行配置。

<property>
    <name>hive.metastore.warehouse.dir</name>
    <value>/user/hive/warehouse</value>
</property>
删除数据库
//删除数据库
hive> drop database enterprise;

//防止数据库不存在的时候,抛出异常
hive> drop database if exists enterprise;

// Hive自己先删除数据库中的表,然后删除数据库
hive> drop database if exists enterprise cascade;
创建表
create table if not exists enterprise.account(acc_name string,acc_balance double) row format delimited fiedls terminated by '\t' location '/user/hive/warehouse/enterprise.db/account';

注意,数据目录总是以“.db”结尾的,其中enterprise.db目录下的子目录account就是表,所以我们说Hive中的表也是一个表目录。即在HDFS分布式文件系统上所创建的目录。默认情况下,Hive总是将创建的表目录放置在这个表所属的数据库目录下。

我们可以列举指定数据库下的表

hive> show tables in enterprise;
管理表

管理表也称内部表或临时表,Hive控制着管理表的整个生命周期,默认情况下Hive管理表的数据存放在Hive主目录/usr/hive/datawarehouse/,并且当我们删除一张表时,这张表的数据也会相应地被删除。我们上述操作建表都是建的是管理表。

外部表

Hive中的外部表(External Table)是一种特殊类型的表,它指向已经在HDFS中存在的数据。与内部表(Managed Table)不同,外部表不会移动数据到数据仓库目录中,只是与外部数据建立一个链接。

以下是关于Hive外部表的一些主要特点:

    1. 指向已存在的数据:外部表指向已经在HDFS中存在的数据,可以创建Partition
    1. 只读性:外部数据表都是只读的,因此在外部表不能够执行DML操作,也不能创建索引。
    1. 元数据组织:它和内部表在元数据的组织上是相同的,而实际数据的存储则有较大的差异。
    1. 删除特性:当删除一个外部表时,仅删除该链接,并不会删除实际的数据。
    1. 访问方式:对外部表的访问可以通过SQL语句来完成,而不需要先将外部表中的数据装载进数据库中。
    1. 数据位置:在创建外部表时,可以自己指定目录位置(LOCATION)。

总的来说,Hive的外部表提供了一种方便的方式来查询和管理已经在HDFS中存在的数据,同时保持了Hive表的元数据组织特性。它特别适用于想要在Hive之外使用表的数据的情况,例如多个部门想使用一个表的数据。

在创建表时,如果加上关键字external,则创建为外部表。外部表中的数据生命周期不受Hive的控制,且可以和其他外部表进行数据的共享。

create external table product(pro_name string,pro_price double) row format delimited fields terminated by '\t' location '/data/stocks';
修改表

创建表

create table student(name string,age int) row format delimited fields terminated by '\t';

修改表名

alter table student rename to stu

增加字段

alter table stu add columns(sex string,birthday string)
删除表
drop table tableName;
分区表

随着数据库中数据量不断激增,就不得不考虑数据库存储和计算性能问题。Hive就是使用分区表的结构来解决问题的。

Hive分区的概念与传统关系型数据库表分区不同。传统数据库的表分区方式,就MySQL而言,是指将一张表分解成多个更小的、容易管理的部分。从逻辑上看只有一张表,单底层却是由多个物理分区组成的,每个物理分区中存储真实的数据,在数据插入的时候自动分配分区,这些物理分区可以分布在不同的物理服务器设备上。由于Hive表中的数据实际存储在HDFS上,所以Hive的分区方式是在HDFS文件系统上的一个分区名对应一个目录名,子分区名就是子目录名,并不是一个实际字段。因此可以这样理解,在插入数据的时候指定分区,其实就是新建一个目录或者子目录,并在相应的目录上添加数据文件,实现Hive表分区的功能。

所以概括来说,Hive的分区是创建层级目录的一种方式。

静态分区

创建一张静态分区表student_partition,单分区列为sex

create table student_partition(name string,age int) partitioned by(sex string) row format delimited fields terminated by '\t';

load数据

load data local inpth '/home/school/student.txt' into table student_partition partition(sex='woman')

创建一张静态分区表student_partition,多分区列为sex、native

create table student_partition_multi(name string,age int) partitioned by(sex string,native string) row format delimited fields terminated by '\t';

load数据

load data local inpth '/home/school/student.txt' into table student_partition_multi partition(sex='man',native='GanSu')
动态分区

如果在实际开发中经常使用静态分区的话,在插入数据的时候,就必须首先知道有哪些分区类型,针对每一个分区要单独使用load data命令载入数据。使用Hive的动态分区就可以解决自动将数据分配到各自分区。动态分区和静态分区的区别就是不用指定分区目录,由系统自己选择

开启动态分区功能

set hive.exec.dynamic.partition=true;
//此属性的默认值是strict,意思是不允许分区列全部是动态的。将其值设置为nonstrict,意思是所有的分区都是动态的
set hive.exec.dynamic.partition.mode=nonstrict;
//最大动态分区个数
set hive.exec.max.dynamic.partitions.pernode=1000;

动态分区表

create table student_partition_multi(name string,age int) partitioned by(sex string,native string) row format delimited fields terminated by '\t';

load数据

load data local inpath '/home/school/student.txt' into table student;

写入分区表

insert overwrite table student_partition_multi partition(sex,native) select name,age,sex,native from student;

HiveQL数据操作

经查询语句向表中插入数据
insert overwrite table sogou.sogou_xj select * from sogou.sogou_500w where keyword like '%仙剑奇侠传%'
单个查询语句中创建表并加载数据
create table sogou.sogou_xj_backup as select * from sogou.sogou_xj;
导入数据

建表

create external table if not exists sogou.sogou_liangjian(ts string,uid string,keyword string,rank int,orders int,url string) row format delimited fields terminated by '\t' stored as textfile location '/sogou/liangjian';

通过HDFS命令行接口直接将liangjian.txt数据导入Hive表,其实就是将数据文件放到LOCATION属性所指向的路径下,然后就可以在Hive中通过HiveQL进行操作查询了。

hadoop fs -put liangjian.txt /sogou/liangjian
导出数据
hadoop fs -get /user/hive/warehouse/sogou.db/sogou_xj_backup/000000_0

HiveQL数据查询基础

HiveQL数据查询语句

SELECT语句
select */field1,field2 from tableName
WHERE语句
select count(*) from sogou.sogou_500w where keyword like '%亮剑%'
GROUP BY语句

GROUP BY语句通常会和聚合函数一起使用,其语意为按照一个或者多个列对结果进行分组,然后使用聚合函数对每个组执行聚合运算。

统计搜索过关键词包含“亮剑”一词的用户及关键词搜索频率

select t.uid,t.keyword,count(*) from (select * from sogou.sogou_500w where keyword like '%亮剑%') t group by t.uid,t.keyword
HAVING分组筛选

HAVING子句允许用户通过一个简单的语法,来完成原本需要通过子查询才能对GROUP BY语句产生的分组结果进行条件过滤的任务。

统计搜索关键词中包含“亮剑”一词的用户及关键词搜索次数,并且过滤出搜索次数大于30的用户

select t.uid,t.keyword,count(*)  as cnt from (select * from sogou.sogou_500w where keyword like '%亮剑%') t group by t.uid,t.keyword having cnt >= 30;
ORDER BY语句和SORT BY语句

Hive中ORDER BY语句和SQL中的定义是一样的,其会对查询结果集执行一次全局排序,也就是说会有一个所有数据都通过一个reducer进行处理的过程。对于大数据集,这个过程可能会消耗太多的时间。

Hive增加了一个可供选择的方式,那就是SORT BY语句,该语句只会在每个reducer中对数据排序,也就是说会执行一个局部排序,因此可以保证每个reducer的输出数据都是有序的(但并非全局有序),如此就可以提高后面进行全局排序效率。

因此若要在数据量级非常大的情况下排序,可以选择SORT BY语句,平时可以选择ORDER BY语句完成排序任务。

select uid,count(*) as cnt from sogou.sogou_500w group by uid order by cnt desc limit 30;

HiveQL连接查询语句

库表准备

create database if not exists scott;

use scott;

//员工表 emp
create external table emp(empno varchar(50),ename varhcar(30),job varchar(50) mgr varchar(30) sal double,deptno varchar(50)) row format delimited fields terminated by ',' stored as textfile location '/scott/emp'


//部门表 dept
create external table dept(deptno varchar(50),dname varchar(30),loc varchar(30)) row format delimited fields terminated by ',' stored as textfile location '/scott/dept'

Hive的连接分为:内连接、自然连接和外连接

内连接

也可以简写为join,只有进行连接的两个表中都存在于连接标准相匹配的数据才会被保留下来,内连接分为等值连接和不等值连接。

等值连接是指,在使用等号操作符的连接。

查看部门30中的员工,要求显示员工的姓名、职位、所属部门编号以及部门名称。

select emp.ename,emp.job,dept.deptno,dept.dname from emp inner join dept on emp.deptno = dept.deptno where dept.deptno = 30;

不等值连接是指,使用 > 、>=、 <=、 < 、!> 、!< 和<> 操作符的连接。

获取部门编号不等于10的所有员工的姓名、职位以及员工所属的部门名称和部门地理位置信息。

select emp.ename,emp.job,dept.dname,dept.loc from emp innert join dept on emp.deptno = dept.deptno where dept.deptno != 10;
自然连接

自然连接是在广义笛卡尔积中选出同名属性上符合相等条件的元祖,再进行投影,去掉重复的同名属性,组成新的关系。即自然连接是在两张表中寻找那些数据类型和列名都相同的字段,然后自动将它们连接起来,并返回所有符合条件的结果,它是通过对参与表关系中所有同名的属性对取等(即相等比较)来完成,故无需自己添加连接条件。自然连接和外连接的区别在于,对于无法匹配的记录,外连接会虚拟一条与之匹配的记录来保全连接表中的所有记录,但自然连接不会。

查询部门编号为10和30的所有员工的姓名、职位和部门名称

select ename,job,dname from emp natural join dept where dept.deptno in ('10','30');
外连接

外连接分为左外连接查询、右外连接查询和全外连接。

左外连接即以连接中的左表为主,返回左表的所有信息和右表中符合连接条件的信息,对于右表中不符合连接条件的则补空值。

select e.empno,e.ename,e.job,d.deptno,d.dname,d.loc from emp e left outer join dept d on deptno = d.deptno

右外连接即以连接中的右表为主,返回右表的所有信息和左表中符合连接条件的信息,对于左表中不符合连接条件的则补空值。

select e.empno,e.ename,e.job,d.deptno,d.dname,d.loc from emp e right outer join dept d on deptno = d.deptno

全外连接查询结果等于左外连接和右外连接的和

select e.empno,e.ename,e.job,d.deptno,d.dname,d.loc from emp e full outer join dept d on deptno = d.deptno

自连接指连接的表是同一张表,使用自连接可以将自身表的一个镜像当成另一个表来对待,所以自连接适用于表自己喝自己的连接查询。

在员工表emp中我们想查询经理的下属员工有哪些

select e1.empno,e2.empno,e2.ename,e2.job,e2.sal from emp e1,emp e2 where e1.empno = e2.mgr;

HiveQL数据查询进阶

查看Hive的内置函数

show functions;

数学函数

加法“+”

select 10+2;

减法“-”

select 10-2;

乘法“*”

select 10*2;

除法“/”

select 10/2;

round四舍五入函数的应用

select round(88.945,2);

ceil向上取整函数应用

select ceil(88.12);

floor向下取整函数的应用

select floor(88.92);

pow取平方函数的应用

select pow(3,2);

pmod取模函数,即取余数的应用

select pmod(13,3);

字符函数

lower转小写函数

select lower('AAAAA');

upper转大写函数

select upper('AAAAA');

length字符串长度函数

select length('AAAAA');

concat字符串拼接函数

select concat("hadoo","hive");

substr求子串函数

// 从字符串a中,第3位开始取,取右边所有的字符
sekect substr("hadoop",3);

// 从字符串a中,第b位开始取,取c个字符
sekect substr("hadoop",3,1);

trim去前后空格函数

select trim("  hive  ");

get_json_object用于处理json格式数
据的函数

select get_json_object('{"name":"zhangsan","age":12}','$.name')

转换函数

类型转换函数cast应用

select cast(99 as double);

select cast("2020-01-30" as date);

日期函数

select year("2019-9-16 17:18:10"),month("2019-9-16 17:18:10"),day("2019-9-16 17:18:10")

to_date返回日期时间字段中的日期部分

select to_date("2019-9-16 17:18:10")

条件函数

case…when… 是,条件表达式,语法格式为case A when B then C [when D then E]* [else F] end.

对A来说,如果判断为B则返回C,如果判断为D则返回E(此处判断条件可为多个),如果以上都不是则返回F。注意,最后还有一个end结束符。

select ename,job,sal case job wher 'manager' then sal + 2000 when 'clerk' then sal + 1000 else sal + 400 from emp;

聚合函数

count:返回行数

select count(*) from emp;

sum:组内某列求和函数

select sum(sal),job from emp group by job;

min:组内某列最小值

select min(sal),job from emp group by job;

max:组内某列最大值

select max(sal),job from emp group by job;

avg:组内某列平均值

select avg(sal),job from emp group by job;

Hive视图

Hive中的视图和关系型数据库中视图在概念上是一致的,都是一组数据的逻辑表示,享用基本原始表的数据而不会另生成一份数据,是纯粹的逻辑对象。本质上,视图是一条SQL语句的集合,但该条SQL不会立即执行,我们称其为逻辑视图,它没有关联的实际存储。当有查询需要引用视图时,Hive才真正开始将查询中的过滤器推送到视图中去执行。

创建视图

语法

create view [if not exists] db_name.view_name as select [column_name...] from table_name where ...

创建一个视图,其内容包含表sogou_500w中关键词不为空的前1000条数据

create view sogou_view as select * from sogou_500w where keyword is not null limit 1000;

查看视图

show tables;

desc sogou_view;

desc formatted sogou_view;

删除视图

drop view if exists sogou.sogou_view;

Hive分桶表

Hive分桶表是相对于分区表来说的,分区表它属于一种粗粒度的划分,而分桶表是对数据进行更细粒度的划分。分桶表将整个数据内容按照某列属性值的哈希值进行区分,例如按照用户ID属性分为3个桶,分桶的规则就是对分桶字段值进行取哈希值,然后用该哈希值除以桶的个数取余数,余数决定了该条记录将会被分在哪个桶中。余数相同的记录会被分子啊同一个桶中。需要注意的是,在物理结构上一个桶对应一个文件,而分区表的分区只是一个目录,至于目录下有多少数据是不确定的。

创建表

通过clustered by(字段名) into bucket_num buckets 分桶,意思是根据字段名分成多个桶

create table sougou_bucket(uid string,keyword string) comment 'test' clustered by(uid) into 5 buckets row format delimited fields terminated by '\t';

插入数据

必须使用启动MapReduce作业的方式才能把文件顺利分桶,若使用load data local inpath 这种方式加载数据,即使设置了强制分桶,也不起作用。注意,插入数据之前,需要设置属性hive.enforce.bucketing=true,其含义是数据分桶是否被强制执行,默认为false,如果开启,则写入table数据时会启动分桶。所以必须要将该属性的值设置为true。

set hive.enforce.bucketing=true;
insert overwrite table sogou_bucket select uid,keyword from sogou_500w limit 10000;

分桶表相对于分区表来说的,分桶表在物理结构上一个桶对应一个文件,而分区表一个分区对应一个目录。

1. HIVE结构 Hive 是建立在 Hadoop 上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数 据提取转化加载 (ETL),这是一种可以存储、 查询和分析存储在 Hadoop 中的大规模数据的 机制。 Hive 定义了简单的类 SQL 查询语言,称为 QL,它允许熟悉 SQL 的用户查询数据。 同时,这个语言也允许熟悉 MapReduce 开发者的开发自定义的 mapper 和 reducer 来处理 内建的 mapper 和 reducer 无法完成的复杂的分析工作。 1.1HIVE 架构 Hive 的结构可以分为以下几部分: 用户接口:包括 CLI, Client, WUI 元数据存储。通常是存储在关系数据库如 mysql, derby 中 6 解释器、编译器、优化器、执行器 Hadoop:用 HDFS 进行存储,利用 MapReduce 进行计算 1、 用户接口主要有三个: CLI,Client 和 WUI。其中最常用的是 CLI , Cli 启动的时候, 会同时启动一个 Hive 副本。 Client 是 Hive 的客户端,用户连接至 Hive Server 。 在启动 Client 模式的时候, 需要指出 Hive Server 所在节点,并且在该节点启动 Hive Server 。 WUI 是通过浏览器访问 Hive 。 2、 Hive 将元数据存储在数据库中,如 mysql 、 derby 。 Hive 中的元数据包括表的名字, 表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等。 3、 解释器、编译器、优化器完成 HQL 查询语句从词法分析、语法分析、编译、优化以及 查询计划的生成。生成的查询计划存储在 HDFS 中,并在随后有 MapReduce 调用执行。 4、 Hive 的数据存储在 HDFS 中,大部分的查询由 MapReduce 完成(包含 * 的查询,比 如 select * from tbl 不会生成 MapRedcue 任务)。 1.2HiveHadoop 关系 Hive 构建在 Hadoop 之上, HQL 中对查询语句的解释、优化、生成查询计划是由 Hive 完成的 所有的数据都是存储在 Hadoop 中 查询计划被转化为 MapReduce 任务,在 Hadoop 中执行(有些查询没有 MR 任 务,如: select * from table ) HadoopHive 都是用 UTF-8 编码的 7 1.3Hive 和普通关系数据库的异同 Hive RDBMS 查询语言 HQL SQL 数据存储 HDFS Raw Device or Local FS 索引 无 有 执行 MapReduce Excutor 执行延迟 高 低 处理数据规模 大 小 1. 查询语言。由于 SQL 被广泛的应用在数据仓库中,因此,专门针对 Hive 的特性设计 了类 SQL 的查询语言 HQL。熟悉 SQL 开发的开发者可以很方便的使用 Hive 进行开 发。 2. 数据存储位置。 Hive 是建立在 Hadoop 之上的,所有 Hive 的数据都是存储在 HDFS 中 的。而数据库则可以将数据保存在块设备或者本地文件系统中。 3. 数据格式。 Hive 中没有定义专门的数据格式,数据格式可以由用户指定,用户定义数 据格式需要指定三个属性:列分隔符(通常为空格、” t ”、” x001″)、行分隔符 (” n”)以及读取文件数据的方法( Hive 中默认有三个文件格式 TextFile , SequenceFile 以及 RCFile )。由于在加载数据的过程中,不需要从用户数据格式到 Hive 定义的数据格式的转换,因此, Hive 在加载的过程中不会对数据本身进行任何修 改,而只是将数据内容复制或者移动到相应的 HDFS 目录中。而在数据库中,不同的数 据库有不同的存储引擎,定义了自己的数据格式。所有数据都会按照一定的组织存储, 因此,数据库加载数据的过程会比较耗时。 4. 数据更新。由于 Hive 是针对数据仓库应用设计的,而数据仓库的内容是读多写少的。 因此, Hive 中不支持对数据的改写和添加,所有的数据都是在加载的时候中确定好的。 而数据库中的数据通常是需要经常进行修改的,因此可以使用 INSERT INTO ... VALUES 添加数据,使用 UPDATE ... SET 修改数据。 5. 索引。之前已经说过, Hive 在加载数据的过程中不会对数据进行任何处理,甚至不会 对数据进行扫描,因此也没有对数据中的某些 Key 建立索引。 Hive 要访问数据中满足 条件的特定值时,需要暴力扫描整个数据,因此访问延迟较高。由于 MapReduce 的引 入, Hive 可以并行访问数据,因此即使没有索引,对于大数据量的访问, Hive 仍然 可以体现出优势。数据库中,通常会针对一个或者几个列建立索引,因此对于少量的特 定条件的数据的访问,数据库可以有很高的效率,较低的延迟。由于数据的访问延迟较 高,决定了 Hive 不适合在线数据查询。 6. 执行。 Hive 中大多数查询的执行是通过 Hadoop 提供的 MapReduce 来实现的(类似 select * from tbl 的查询不需要 MapReduce)。而数据库通常有自己的执行引擎。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值