简介
因为Hive的使用依赖Hadoop,不同的版本之间有很多问题,大的原则上是hive2.x版本对应hadoop2.x版本,hive3.x版本对应hadoop3.x版本。
但是在实际的使用过程中还是有各种兼容问题,具体的hive安装可以参考hive安装,这里我们介绍一下遇到问题的解决方案。
在看到日志中有java.lang.NoClassDefFoundError的异常,一般是缺少jar包,我们只需要找到对应的jar包,jar的名称好找日志中就能看到,具体的版本可以查看对应的源码pom依赖。然后放到对应的lib目录下就可以了。
在日志中看到NoSuchMethod的异常,一般是因为jar包冲突,有多个版本。要找到hive的lib目录,和hadoop的share目录下的各个目录中的jar包,然后使用高版本的替换对应的低版本,因为一般情况高版本是兼容低版本的jar包的。如果用到hbase,hbase的lib目录也要查看。
在hive3.1.1和hadoop3.0.2一起使用的时候就会有disruptor和guava包有多个版本的问题,换成高版本就可以了。
hive
hive是一个操作hive数据库的命令行接口(CLI,Client Line Interface)
beeline
beeline是一个操作hive数据库的新命令行接口(New Client Line Interface)
beeline -u jdbc:hive2://localhost:10000
beeline -u jdbc:hive2://localhost:10000 -n user -p password
beeline !connect jdbc:hive2://localhost:10000
hiveserver
HiveServer是一个服务端接口,使远程客户端可以执行对Hive的查询并返回结果。目前基于Thrift RPC的实现是HiveServer的改进版本,并支持多客户端并发和身份验证。
hive-site.xml
<property>
<name>hive.server2.thrift.port</name>
<value>10000</value>
</property>
<property>
<name>hive.server2.thrift.bind.host</name>
<value>127.0.0.1</value>
</property>
<property>
<name>hive.server2.webui.host</name>
<value>127.0.0.1</value>
</property>
<property>
<name>hive.server2.webui.port</name>
<value>10002</value>
</property>
<property>
<name>hive.server2.enable.doAs</name>
<value>false</value>
</property>
hive.server2.enable.doAs:是为了防止hdfs权限问题,这样hive server会以提交用户的身份去执行语句,如果设置为false,则会以hive server的admin user来执行语句。
hive --service hiveserver
hive --service hiveserver2
hiveserver2 --hiveconf hive.server2.thrift.port=10000
MetaStoreServer
MetaStoreServer使用thrift协议提供了一个访问元数据的服务,这样远程客户端就可以不用通过访问数据库的方式获取元数据信息了,spark就会访问这个服务。
hive --service metastore
<property>
<name>hive.metastore.uris</name>
<value>thrift://192.168.10.7:9083,thrift://192.168.10.8:9083</value>
<description></description>
</property>
Hive数据类型与表创建
- 基本类型
TINYINT,SMALLINT,INT,BIGINT,BOOLEAN,FLOAT,DOUBLE,STRING,BINARY,TIMESTAMP,DECIMAL,CHAR,VARCHAR,DATE
其中:TINYINT 1字节
SMALLINT 2字节
INT 4字节
BIGINT 8字节
FLOAT 4字节
DOUBLE 8字节
- 复杂类型包括
ARRAY,MAP,STRUCT,UNION
array是数组类型,map是键值对,struct是其他类型的组合
array与struct元素的分隔符是^B(ctrl+B,创建表中的八进制\002)
map键与值的分隔符是^C(ctrl+C,创建表中的八进制\003)
Hive默认的列分隔符是:^A(ctrl+A,创建表中的八进制\001)
Hive默认的行分隔符是\n
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]
create table goods(id int,name string,amount int)
partitioned by (ctime date)
row format delimited
fields terminated by '\001'
collection items terminated by '\002'
map keys terminated by '\003'
lines terminated by '\n'
;
可以指定多个分区,分区列不用添加到创建表主体的字段中。
Hive数据
- Hive的本质是将SQL语句转换为MapReduce任务运行
- Hive底层数据是存储在HDFS上
- Hive 的存储结构包括数据库、表、视图、分区和表数据等
- Hive数据库、表、分区等等都对应HDFS上的一个目录
- Hive表数据对应HDFS对应目录下的文件
- Hive数据在HDFS中没有专门的数据存储格式
- Hive只需要在创建表的时候告诉Hive数据中的列分隔符和行分隔符
- Hive的元数据存储在RDBMS中,除元数据外的其它所有数据都基于HDFS存储
- Hive元数据保存在内嵌的Derby数据库中,只能允许一个会话连接,只适合简单的测试
- Hive生产环境中为了支持多用户会话使用独立的元数据库,如MySQL
Hive数据模型
- database,在HDFS中表现为${hive.metastore.warehouse.dir}目录下子目录
- table,在HDFS中表现为database目录下子目录
- external table,与table类似,不过其数据存放位置可以指定任意HDFS目录路径
- partition,在HDFS中表现为table目录下的子目录
- bucket,在HDFS中表现为同一个表目录或者分区目录下根据某个字段的值进行hash散 列之后的多个文件
- view,只读,基于基本表创建
内部表和外部表的区别:
- 删除内部表,删除表元数据和数据
- 删除外部表,删除元数据,不删除数据
如果数据的所有处理都在Hive中进行,那么使用内部表,但是如果Hive 和其他工具要针对相同的数据集进行处理,使用外部表更合适。
分区表和分桶表的区别:
Hive数据表可以根据某些字段进行分区操作,细化数据管理,可以让部分查询更快。同 时表和分区也可以进一步被划分为Buckets。
Java API
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Random;
public class HiveTest {
// public static final String HIVE_URL = "jdbc:hive://127.0.0.1:10000/default";//hiveserver1
private static final String HIVE_URL = "jdbc:hive2://127.0.0.1:10000/test";//hiveserver2
private static String DRIVER_NAME = "org.apache.hive.jdbc.HiveDriver";
private Connection connection;
@Before
public void setUp() throws ClassNotFoundException, SQLException {
Class.forName(DRIVER_NAME);
connection = DriverManager.getConnection(HIVE_URL,"hive","");
}
@Test
public void showDbs() throws SQLException {
Statement statement = connection.createStatement();
String sql = "show databases";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getString(1));
}
}
@Test
public void createDB() throws SQLException {
//create database dbName
//create schema dbName
Statement statement = connection.createStatement();
String sql = "create schema mytable";
statement.execute(sql);
}
@Test
public void createTable() throws SQLException {
String sql = "create table goods(id int,name string,amount int) " +
"partitioned by (ctime date) " +
"row format delimited " +
"fields terminated by '\\001' " +
"collection items terminated by '\\002' " +
"map keys terminated by '\\003' " +
"lines terminated by '\\n'";
Statement statement = connection.createStatement();
statement.execute(sql);
}
@Test
public void createNewGoodsTable() throws SQLException {
String sql = "create table new_goods(id int,name string,amount int) " +
"partitioned by (ctime string) " +
"row format delimited " +
"fields terminated by '\\001' " +
"collection items terminated by '\\002' " +
"map keys terminated by '\\003' " +
"lines terminated by '\\n'";
Statement statement = connection.createStatement();
statement.execute(sql);
}
@Test
public void insertGoods() throws SQLException {
String sql = "insert into goods(id,name,amount,ctime) values(?,?,?,?)";
PreparedStatement ps = connection.prepareStatement(sql);
Random random = new Random();
String[] names = {"allen","alice","bob","tony","ribon"};
Calendar instance = Calendar.getInstance();
for(int i=0;i<10;i++){
ps.setInt(1,random.nextInt(1000));
ps.setString(2,names[random.nextInt(names.length)]);
ps.setInt(3,random.nextInt(100000));
instance.add(Calendar.DAY_OF_MONTH,random.nextInt(3));
ps.setDate(4,new Date(instance.getTimeInMillis()));
ps.executeUpdate();
}
}
@Test
public void insertDood() throws SQLException {
String sql = "insert into new_goods(id,name,amount,ctime) values (1,'allen',100,'2019-07-04')";
Statement statement = connection.createStatement();
statement.executeUpdate(sql);
System.out.println("insert done");
statement.close();
}
@Test
public void selectGood() throws SQLException {
String sql = "select id,name,amount,ctime from new_goods";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
while (rs.next()){
System.out.println("id:" + rs.getInt(1));
System.out.println("name:" + rs.getString(2));
System.out.println("amount:" + rs.getInt(3));
System.out.println("ctime:" + rs.getString(4));
}
}
@Test
public void selectGoods() throws SQLException {
String sql = "select id,name,amount,ctime from goods";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
while (rs.next()){
System.out.println("id:" + rs.getInt(1));
System.out.println("name:" + rs.getString(2));
System.out.println("amount:" + rs.getInt(3));
System.out.println("ctime:" + sdf.format(rs.getDate(4)));
}
}
@Test
public void descTable() throws SQLException {
//desc tableName
//describe tableName
String sql = "desc goods";
Statement statement = connection.createStatement();
statement.execute(sql);
}
// @Test
public void dropTable() throws SQLException {
//drop table
String sql = "drop table goods";
Statement statement = connection.createStatement();
statement.execute(sql);
}
@Test
public void showTable() throws SQLException {
//show tables;
String sql = "show tables";
Statement statement = connection.createStatement();
statement.execute(sql);
}
@After
public void tearDown() throws SQLException {
connection.close();
}
}
使用hiveserver,URL为:
使用hiveserver2,URL为:
如果遇到类似于下面的错误:
User: xxxx is not allowed to impersonate hive
可以在hadoop的core-site.xml文件中加入如下配置,其中curitis为用户组,windows就是用户名。
<property>
<name>hadoop.proxyuser.curitis.groups</name>
<value>*</value>
<description></description>
</property>
<property>
<name>hadoop.proxyuser.curitis.hosts</name>
<value>*</value>
<description></description>
</property>
如果windows下用户名包含.这样的特殊字符,那就需要修改用户名,稍微麻烦一点,先改名字,然后启用Administrator用户,使用Administrator用户修改之前用户名的用户目录,然后修改注册表:
下面找到对应的旧用户名,修改为新的用户名。
如果遇到类似于下面的错误:
AccessControlException Permission denied: user=hive, access=WRITE, inode="/user/hive/warehouse/test.db":admin:supergroup:drwxr-xr-x
可以执行:
hadoop fs -chmod -R 777 /user
如果遇到类似于下面的错误:
ipc.Client: Retrying connect to server: account.jetbrains.com/0.0.0.0:8032. Already tried 2 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleepTime=1000 MILLISECONDS)
可以在hadoop的yarn.site.xml配置文件中加入下面的配置:
<property>
<name>yarn.resourcemanager.address</name>
<value>127.0.0.1:8032</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value> 127.0.0.1:8030</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value> 127.0.0.1:8031</value>
</property>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.curitis</groupId>
<artifactId>hive-learn</artifactId>
<version>1.0.0</version>
<properties>
<spring.version>5.1.3.RELEASE</spring.version>
<junit.version>4.11</junit.version>
<hive.version>3.1.1</hive.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>${hive.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-common</artifactId>
<version>${hive.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>${hive.version}</version>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="INFO" monitorInterval="600">
<Properties>
<!-- <property name="LOG_HOME">${sys:user.home}/hive/test</property>-->
<property name="LOG_HOME">F:/logs/hive/test</property>
<!--输出日志的格式
%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间
%p : 日志输出格式
%c : logger的名称
%m : 日志内容,即 logger.info("message")
%n : 换行符
%C : Java类名
%L : 日志输出所在行数
%M : 日志输出所在方法名
hostName : 本地机器名
hostAddress : 本地ip地址 -->
<Property name="PATTERN_ONE">%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</Property>
<Property name="PATTERN_TWO">%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n</Property>
</Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout pattern="${PATTERN_ONE}" />
</console>
<File name="FileLog" fileName="${LOG_HOME}/hive.log" append="false">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${PATTERN_TWO}"/>
</File>
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<loggers>
<logger name="org.springframework" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="FileLog"/>
<!-- <appender-ref ref="RollingFileInfo"/>-->
<!-- <appender-ref ref="RollingFileWarn"/>-->
<!-- <appender-ref ref="RollingFileError"/>-->
</root>
</loggers>
</configuration>
HWI(Hie Web Interface)
2.2.0之前可用
hive-site.xml
<property>
<name>hive.hwi.listen.host</name>
<value>0.0.0.0</value>
<description>监听的地址</description>
</property>
<property>
<name>hive.hwi.listen.port</name>
<value>9999</value>
<description>监听的端口号</description>
</property>
<property>
<name>hive.hwi.war.file</name>
<value>${HIVE_HOME}/lib/hive-hwi-2.1.0.war</value>
<description>war包所在的地址</description>
</property>
hive --service hwi
localhost:9999/hwi