概述
在Hive中,某些小技巧可以让我们的Job执行得更快,有时一点小小的改动就可以让性能得到大幅提升,这一点其实跟SQL差不多。
首先,Hive != SQL,虽然二者的语法很像,但是Hive最终会被转化成MapReduce的代码去执行,所以数据库的优化原则基本上都不适用于 Hive。也正因如此,Hive实际上是用来做计算的,而不像数据库是用作存储的,当然数据库也有很多计算功能,但一般并不建议在SQL中大量使用计算,把数据库只当作存储是一个很重要的原则。
一、善用临时表
在处理海量数据时我们通常会对很多大表进行操作,基于Hadoop现在的局限性,不能像分布式并行数据库那样很好地在分布式环境利用数据局部性,Hadoop对于大表只能全表扫描并筛选数据,而每一次对大表的扫描都是苦不堪言的。(最后知道真相的我眼泪掉下来。。。)
所以我们会用到在编码中经常用到的重构技巧,提取公共变量,在Hive中,就是创建临时表。
例如:现在要对三个表A、B、C进行处理,Hive QL是:
select T1.*, T2.*
from(
select id, name from A
) T1 join (
select id, price, feedback, type from B
) T2 on(T1.id = T2.id)
;
select T1.*, T2.*
from (
select id, type from C
) T1 join (
select id, price, feedback, attribute from B
) T2 on(T1.id = T2.id)
;
这里A表和C表只会被扫描一次,而B表会被扫描两次,如果B表的数据量很大,那么扫描B表的时间将会占用很大一块。
这里我们可以先创建一个临时表:
create table temp_B as select id, price, feedback, type, attribute from B;
这个表只有B表的部分字段,所以大小会小很多,这里会对B表全表扫一遍。
然后可以用临时表和A、C表做join运算:
select T1.*, T2.*
from (
select id, name from A
) T1 join (
select id, price, feedback, type from temp_B
) T2 on(T1.id = T2.id)
;
select T1.*, T2.*
from (
select id, type from C
) T1 join (
select id, price,feedback, attribute from temp_B
) T2 on(T1.id = T2.id)
;
这样任务的执行速度将会有极大提升!尽管看起来多了一条Hive QL,但是后两个任务需要扫描的数据将会变得很小。
二、一次执行多个COUNT
如果我们要对多种条件进行COUNT,可以利用case语句进行,这样一条Hive QL就可以完成了。
select count(case when type = 1 then 1 end), count(case when type = 2 then 1 end) fromtable;
三、导出表文件
首先需要用create table在HDFS上生成你所需要的表,当需要从HDFS上将表对应的文件导出到本地磁盘时有两种方式:
1、如果需要保持HDFS上的目录结构,原封不动地复制下来,采用下面的命令:
set hive.exec.compress.output='false';
insert overwrite local directory '/home/hesey/directory' select * from table;
这样下载下来的目录中会有很多由Reducer产生的part-*文件。
2、如果想把表的所有数据都下载到一个文件中,则采用下面的命令:
hadoop dfs -getmerge hdfs://hdpnn:9000/hesey/hive/table /home/hesey/table.txt
这样所有文件会由Hadoop合并后下载到本地,最后就只有/home/hesey/table.txt这一个文件。
四、UDF
在Hive中很多时候都需要做一些复杂的计算或者逻辑处理,这时候Hive本身作为一个通用框架没法很好地支持,所以有了UDF(User Defined Function)。
1、使用UDF
(a)如果是已经上传到Hive服务器的UDF,可以直接用
create temporary function dosomething as 'net.hesey.udf.DoSomething';
声明临时函数,然后在下面的Hive QL中就可以调用dosomething这个方法了。
(b)如果是自己编写的UDF,需要在声明临时函数前再加一行:
add jar /home/hesey/foo.jar
这样就可以把自定义的UDF加载进来,然后和(a)一样声明临时函数就可以了。
2、编写UDF
编写UDF十分简单,引入hive-exec包,继承org.apache.hadoop.hive.ql.exec.UDF类,实现evaluate方法即可,方法的输入和输出参数类型就是当你在Hive中调用时的输入和返回值。
例如:
public Text evaluate(final LongWritable number);
(Text和LongWritable是org.apache.hadoop.io下面的类)
这样我们就可以定义自己的函数并方便地在Hive中调用,而不需要写一个重量级的MapReduce。
五、笛卡尔积
Hive本身是不支持笛卡尔积的,不能用select T1.*, T2.* from table_1, table_2这种语法。但有时候确实需要用到笛卡尔积的时候,可以用下面的语法来实现同样的效果:
select T1.*, T2.*
from (
select * from table1
) T1 join (
select * from table2
) T2 on(1=1)
;
其中on 1=1是可选的,注意在Hive的Strict模式下不能用这种语法,需要先用set hive.mapred.mode=nonstrict;设为非strict模式就可以用了。
六、join的规则
当Hive做join运算时,join前面的表会被放入内存,所以在做join时,最好把小表放在前面,有利于提高性能并防止OOM。
七、排序
在SQL中排序通过ORDER by实现,Hive中也支持这种语法,但是使用ORDER by时最终所有的数据会汇总到一个Reducer上进行排序,可能使得该Reducer压力非常大,任务长时间无法完成。
如果排序只要求保证Value有序而Key可以无序,例如要统计每个用户每笔的交易额从高到低排列,只需要对每个用户的交易额排序,而用户ID本身不需要排序。这种情况采用分片排序更好,语法类似于:
select user_id, amount from table distribute by user_id sort by user_id, amount
这里用到的不是ORDER by,而是distribute by和sort by,distribute by标识Map输出时分发的Key。
这样最后排序的时候,相同的user_id和amount在同一个Reducer上被排序,不同的user_id可以同时分别在多个Reducer上排序,相比ORDER by只能在一个Reducer上排序,速度有成倍的提升。
编写udf:
HIVE允许用户使用UDF(user defined function)对数据进行处理。
用户可以使用‘show functions’ 查看function list,
可以使用'describe function function-name'查看函数说明。
hive> show functions;
OK
!
!=
......
Time taken: 0.275 seconds
hive> desc function substr;
OK
substr(str, pos[, len]) - returns the substring of str that starts at pos and is of length len orsubstr(bin, pos[, len]) - returns the slice of byte array that starts at pos and is of length len
Time taken: 0.095 seconds
hive> show functions;
OK
!
!=
......
Time taken: 0.275 seconds
hive> desc function substr;
OK
substr(str, pos[, len]) - returns the substring of str that starts at pos and is of length len orsubstr(bin, pos[, len]) - returns the slice of byte array that starts at pos and is of length len
Time taken: 0.095 seconds
hive提供的build-in函数包括以下几类:
1. 关系操作符:包括 = 、 <> 、 <= 、>=等
2. 算数操作符:包括 + 、 - 、 *、/等
3. 逻辑操作符:包括AND 、 && 、 OR 、 || 等
4. 复杂类型构造函数:包括map、struct、create_union等
5. 复杂类型操作符:包括A[n]、Map[key]、S.x
6. 数学操作符:包括ln(double a)、sqrt(double a)等
7. 集合操作符:包括size(Array)、sort_array(Array)等
8. 类型转换函数: binary(string|binary)、cast(expr as )
9. 日期函数:包括from_unixtime(bigint unixtime[, string format])、unix_timestamp()等
10.条件函数:包括if(boolean testCondition, T valueTrue, T valueFalseOrNull)等
11. 字符串函数:包括acat(string|binary A, string|binary B...)等
12. 其他:xpath、get_json_objectscii(string str)、con
编写Hive UDF有两种方式:
1. extends UDF , 重写evaluate方法
2. extends GenericUDF,重写initialize、getDisplayString、evaluate方法
hive有三种方法使用自定义的UDF函数
1. 临时添加UDF
如下:
hive> select * from test;
OK
Hello
wORLD
ZXM
ljz
Time taken: 13.76 seconds
hive> add jar /home/work/udf.jar;
Added /home/work/udf.jar to class path
Added resource: /home/work/udf.jar
hive> create temporary function mytest as 'test.udf.ToLowerCase';
OK
Time taken: 0.103 seconds
hive> show functions;
......
mytest
......
hive> select mytest(test.name) from test;
......
OK
hello
world
zxm
ljz
Time taken: 38.218 seconds
hive> select * from test;
OK
Hello
wORLD
ZXM
ljz
Time taken: 13.76 seconds
hive> add jar /home/work/udf.jar;
Added /home/work/udf.jar to class path
Added resource: /home/work/udf.jar
hive> create temporary function mytest as 'test.udf.ToLowerCase';
OK
Time taken: 0.103 seconds
hive> show functions;
......
mytest
......
hive> select mytest(test.name) from test;
......
OK
hello
world
zxm
ljz
Time taken: 38.218 seconds
这种方式在会话结束后,函数自动销毁,因此每次打开新的会话,都需要重新add jar并且create temporary function
2. 进入会话前自动创建
使用hive -i参数在进入hive时自动初始化
$ cat hive_init
add jar /home/work/udf.jar;
create temporary function mytest as 'test.udf.ToLowerCase';
$ hive -i hive_init
Logging initialized using configuration in file:/home/work/hive/hive-0.8.1/conf/hive-log4j.properties
Hive history file=/tmp/work/hive_job_log_work_201209200147_1951517527.txt
hive> show functions;
......
mytest
......
hive> select mytest(test.name) from test;
......
OK
hello
world
zxm
ljz
[plain] view plaincopy
$ cat hive_init
add jar /home/work/udf.jar;
create temporary function mytest as 'test.udf.ToLowerCase';
$ hive -i hive_init
Logging initialized using configuration in file:/home/work/hive/hive-0.8.1/conf/hive-log4j.properties
Hive history file=/tmp/work/hive_job_log_work_201209200147_1951517527.txt
hive> show functions;
......
mytest
......
hive> select mytest(test.name) from test;
......
OK
hello
world
zxm
ljz
方法2和方法1本质上是相同的,区别在于方法2在会话初始化时自动完成
3. 自定义UDF注册为hive内置函数
可参考:hive利器 自定义UDF+重编译hive
和前两者相比,第三种方式直接将用户的自定义函数作为注册为内置函数,未来使用起来非常简单,但这种方式也非常危险,一旦出错,将是灾难性的,因此,建议如果不是特别通用,并且固化下来的函数,还是使用前两种方式比较靠谱。
eg:
编写UDF代码实例(更多例子参考https://svn.apache.org/repos/asf/hive/tags/release-0.8.1/ql/src/java/org/apache/hadoop/hive/ql/udf/):
例子1:功能:大小转小写
ToLowerCase.java:
package test.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class ToLowerCase extends UDF {
public Text evaluate(final Text s) {
if (s == null) { return null; }
return new Text(s.toString().toLowerCase());
}
}
[plain] view plaincopy
package test.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class ToLowerCase extends UDF {
public Text evaluate(final Text s) {
if (s == null) { return null; }
return new Text(s.toString().toLowerCase());
}
}
例子2:功能:计算array中去重后元素个数
UDFArrayUniqElementNumber .java
package test.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ListObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.IntWritable;
@Description(name = "array_uniq_element_number", value = "_FUNC_(array) - Returns nubmer of objects with duplicate elements eliminated.", extended = "Example:n"
+ " > SELECT _FUNC_(array(1, 2, 2, 3, 3)) FROM src LIMIT 1;n" + " 3")
public class UDFArrayUniqElementNumber extends GenericUDF {
private static final int ARRAY_IDX = 0;
private static final int ARG_COUNT = 1; // Number of arguments to this UDF
private static final String FUNC_NAME = "ARRAY_UNIQ_ELEMENT_NUMBER"; // External Name
private ListObjectInspector arrayOI;
private ObjectInspector arrayElementOI;
private final IntWritable result = new IntWritable(-1);
public ObjectInspector initialize(ObjectInspector[] arguments)
throws UDFArgumentException {
// Check if two arguments were passed
if (arguments.length != ARG_COUNT) {
throw new UDFArgumentException("The function " + FUNC_NAME
+ " accepts " + ARG_COUNT + " arguments.");
}
// Check if ARRAY_IDX argument is of category LIST
if (!arguments[ARRAY_IDX].getCategory().equals(Category.LIST)) {
throw new UDFArgumentTypeException(ARRAY_IDX, """
+ org.apache.hadoop.hive.serde.Constants.LIST_TYPE_NAME
+ "" " + "expected at function ARRAY_CONTAINS, but "
+ """ + arguments[ARRAY_IDX].getTypeName() + "" "
+ "is found");
}
arrayOI = (ListObjectInspector) arguments[ARRAY_IDX];
arrayElementOI = arrayOI.getListElementObjectInspector();
return PrimitiveObjectInspectorFactory.writableIntObjectInspector;
}
public IntWritable evaluate(DeferredObject[] arguments)
throws HiveException {
result.set(0);
Object array = arguments[ARRAY_IDX].get();
int arrayLength = arrayOI.getListLength(array);
if (arrayLength <= 1) {
result.set(arrayLength);
return result;
}
//element compare; Algorithm complexity: O(N^2)
int num = 1;
int i, j;
for(i = 1; i < arrayLength; i++)
{
Object listElement = arrayOI.getListElement(array, i);
for(j = i - 1; j >= 0; j--)
{
if (listElement != null) {
Object tmp = arrayOI.getListElement(array, j);
if (ObjectInspectorUtils.compare(tmp, arrayElementOI, listElement,
arrayElementOI) == 0) {
break;
}
}
}
if(-1 == j)
{
num++;
}
}
result.set(num);
return result;
}
public String getDisplayString(String[] children) {
assert (children.length == ARG_COUNT);
return "array_uniq_element_number(" + children[ARRAY_IDX]+ ")";
}
}
package test.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ListObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.IntWritable;
@Description(name = "array_uniq_element_number", value = "_FUNC_(array) - Returns nubmer of objects with duplicate elements eliminated.", extended = "Example:n"
+ " > SELECT _FUNC_(array(1, 2, 2, 3, 3)) FROM src LIMIT 1;n" + " 3")
public class UDFArrayUniqElementNumber extends GenericUDF {
private static final int ARRAY_IDX = 0;
private static final int ARG_COUNT = 1; // Number of arguments to this UDF
private static final String FUNC_NAME = "ARRAY_UNIQ_ELEMENT_NUMBER"; // External Name
private ListObjectInspector arrayOI;
private ObjectInspector arrayElementOI;
private final IntWritable result = new IntWritable(-1);
public ObjectInspector initialize(ObjectInspector[] arguments)
throws UDFArgumentException {
// Check if two arguments were passed
if (arguments.length != ARG_COUNT) {
throw new UDFArgumentException("The function " + FUNC_NAME
+ " accepts " + ARG_COUNT + " arguments.");
}
// Check if ARRAY_IDX argument is of category LIST
if (!arguments[ARRAY_IDX].getCategory().equals(Category.LIST)) {
throw new UDFArgumentTypeException(ARRAY_IDX, """
+ org.apache.hadoop.hive.serde.Constants.LIST_TYPE_NAME
+ "" " + "expected at function ARRAY_CONTAINS, but "
+ """ + arguments[ARRAY_IDX].getTypeName() + "" "
+ "is found");
}
arrayOI = (ListObjectInspector) arguments[ARRAY_IDX];
arrayElementOI = arrayOI.getListElementObjectInspector();
return PrimitiveObjectInspectorFactory.writableIntObjectInspector;
}
public IntWritable evaluate(DeferredObject[] arguments)
throws HiveException {
result.set(0);
Object array = arguments[ARRAY_IDX].get();
int arrayLength = arrayOI.getListLength(array);
if (arrayLength <= 1) {
result.set(arrayLength);
return result;
}
//element compare; Algorithm complexity: O(N^2)
int num = 1;
int i, j;
for(i = 1; i < arrayLength; i++)
{
Object listElement = arrayOI.getListElement(array, i);
for(j = i - 1; j >= 0; j--)
{
if (listElement != null) {
Object tmp = arrayOI.getListElement(array, j);
if (ObjectInspectorUtils.compare(tmp, arrayElementOI, listElement,
arrayElementOI) == 0) {
break;
}
}
}
if(-1 == j)
{
num++;
}
}
result.set(num);
return result;
}
public String getDisplayString(String[] children) {
assert (children.length == ARG_COUNT);
return "array_uniq_element_number(" + children[ARRAY_IDX]+ ")";
}
}
生成udf.jar
例子3:
Hive中分组取前N个值的实现-row_number()
博客分类: Hive
Hive去重UDF
背景
假设有一个学生各门课的成绩的表单,应用hive取出每科成绩前100名的学生成绩。
这个就是典型在分组取Top N的需求。
解决思路
对于取出每科成绩前100名的学生成绩,针对学生成绩表,根据学科,成绩做order by排序,然后对排序后的成绩,执行自定义函数row_number(),必须带一个或者多个列参数,如ROW_NUMBER(col1, ....),它的作用是按指定的列进行分组生成行序列。在ROW_NUMBER(a,b) 时,若两条记录的a,b列相同,则行序列+1,否则重新计数。
只要返回row_number()返回值小于100的的成绩记录,就可以返回每个单科成绩前一百的学生
Sql代码
create table score_table (
subject string,
student string,
score int
)
partitioned by (date string);
Sql代码
create table score_table (
subject string,
student string,
score int
)
partitioned by (date string);
如果要查询2012年每科成绩前100的学生成绩,sql如下
Java代码
create temporary function row_number as 'com.blue.hive.udf.RowNumber';
select subject,score,student from
(select subject,score,student from score where dt='2012' order by subject,socre desc) order_score
where row_number(subject) <= 100;
Java代码
create temporary function row_number as 'com.blue.hive.udf.RowNumber';
select subject,score,student from
(select subject,score,student from score where dt='2012' order by subject,socre desc) order_score
where row_number(subject) <= 100;
com.blue.hive.udf.RowNumber是自定义函数,函数的作用是按指定的列进行分组生成行序列。这里根据每个科目的所有成绩,生成序列,序列值从1开始自增。
执行row_number函数,返回值如下
科目 成绩 学生 row_number
物理 100 张一 1
物理 90 张二 2
物理 80 张三 3
.....
数学 100 李一 1
数学 90 李二 2
数学 80 李三 3
....
row_number的源码
函数row_number(),必须带一个或者多个列参数,如ROW_NUMBER(col1, ....),它的作用是按指定的列进行分组生成行序列。在ROW_NUMBER(a,b) 时,若两条记录的a,b列相同,则行序列+1,否则重新计数。
package com.blue.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
public class RowNumber extends UDF {
private static int MAX_VALUE = 50;
private static String comparedColumn[] = new String[MAX_VALUE];
private static int rowNum = 1;
public int evaluate(Object... args) {
String columnValue[] = new String[args.length];
for (int i = 0; i < args.length; i++) 『
columnValue[i] = args[i].toString();
}
if (rowNum == 1) {
for (int i = 0; i < columnValue.length; i++)
comparedColumn[i] = columnValue[i];
}
for (int i = 0; i < columnValue.length; i++) {
if (!comparedColumn[i].equals(columnValue[i])) {
for (int j = 0; j < columnValue.length; j++) {
comparedColumn[j] = columnValue[j];
}
rowNum = 1;
return rowNum++;
}
}
return rowNum++;
}
}
编译后,打包成一个jar包,如/usr/local/hive/udf/blueudf.jar
然后在hive shell下使用,如下:
add jar /usr/local/hive/udf/blueudf.jar;
create temporary function row_number as 'com.blue.hive.udf.RowNumber';
select subject,score,student from
(select subject,score,student from score where dt='2012' order by subject,socre desc) order_score
where row_number(subject) <= 100;
同样,这个函数可以用作去重操作。
可以替代大批量数据的DISTINCT
通过执行如:
select * from(
select type,value,row_number() as rn
from log_table
distribute by type,value
sort by type,value
)
where rn = 1;
注意!============================
但是使用row_number()函数需要注意一点,必须使用sort by。
测试的时候必须使用order by。
row_number()函数会假设数据有序的基础上进行的。
hive利器 自定义UDF+重编译hive
用hive也有一段时间里,不过一直没写过相关到日志,因为主要用hive也无非是create table,upload data,CRUD 这几个过程。后来工作中需要用到一些常用到方法,了解到hive中支持UDF(User Define Function),看里一些文章发现UDF到编写也很简单,继承UDF然后重写evaluate方法即可,下面以一个ip2long到方法作为参考。
1.编写UDF类
import org.apache.hadoop.hive.ql.exec.UDF;
public class NewIP2Long extends UDF {
public static long ip2long(String ip) {
String[] ips = ip.split("[.]");
long ipNum = 0;
if (ips == null) {
return 0;
}
for (int i = 0; i < ips.length; i++) {
ipNum = ipNum << Byte.SIZE | Long.parseLong(ips[i]);
}
return ipNum;
}
public long evaluate(String ip) {
if (ip.matches("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")) {
try {
long ipNum = ip2long(ip);
return ipNum;
} catch (Exception e) {
return 0;
}
} else {
return 0;
}
}
public static void main(String[] argvs) {
NewIP2Long ipl = new NewIP2Long();
System.out.println(ip2long("112.64.106.238"));
System.out.println(ipl.evaluate("58.35.186.62"));
}
}
2.编译,然后打包成ip2long.jar。
3.在需要使用ip2long这个方法到时候:
1 add jar /tmp/NEWIP2Long.jar;
2 drop temporary function ip2long;
3 create temporary function ip2long as 'NewIP2Long';//如果类有包名,要加上包名
4 select ip2long(ip) from XXX ;
这种方法每次使用都要add,create一下,还是很麻烦,如果能把UDF编译到hive源码中那一定是件很high的事。
进阶:将自定义UDF编译到hive中
重编译hive:
1)将写好的Jave文件拷贝到~/install/hive-0.8.1/src/ql/src/java/org/apache/hadoop/hive/ql/udf/
1 cd ~/install/hive-0.8.1/src/ql/src/java/org/apache/hadoop/hive/ql/udf/
2 ls -lhgt |head
2)修改~/install/hive-0.8.1/src/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java,增加import和RegisterUDF
1 import com.meilishuo.hive.udf.UDFIp2Long; //添加import
2
3 registerUDF("ip2long", UDFIp2Long.class, false); //添加register
3)在~/install/hive-0.8.1/src下运行ant -Dhadoop.version=1.0.1 package
1 cd ~/install/hive-0.8.1/src
2 ant -Dhadoop.version=1.0.1 package
4)替换exec的jar包,新生成的包在/hive-0.8.1/src/build/ql目录下,替换链接
1 cp hive-exec-0.8.1.jar /hadoop/hive/lib/hive-exec-0.8.1.jar.0628
2 rm hive-exec-0.8.1.jar
3 ln -s hive-exec-0.8.1.jar.0628 hive-exec-0.8.1.jar
5)重启hive服务
6)测试
最后
以上就是风趣皮带为你收集整理的Hive日常使用(1)的全部内容,希望文章能够帮你解决Hive日常使用(1)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复