
# Hive应用开发规则
#### Hive JDBC驱动的加载
客户端程序以JDBC的形式连接HiveServer时，需要首先加载Hive的JDBC驱动类org.apache.hive.jdbc.HiveDriver。
故在客户端程序的开始，必须先使用当前类加载器加载该驱动类。
如果classpath下没有相应的jar包，则客户端程序抛出Class Not Found异常并退出。
如下：
```
Class.forName("org.apache.hive.jdbc.HiveDriver").newInstance();
```
#### 获取数据库连接
使用JDK的驱动管理类java.sql.DriverManager来获取一个Hive的数据库连接。
Hive的数据库URL为url="jdbc:hive2://xxx.xxx.xxx.xxx:2181,xxx.xxx.xxx.xxx:2181,xxx.xxx.xxx.xxx:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver;sasl.qop=auth-conf;auth=KERBEROS;principal=hive/hadoop.hadoop.com@HADOOP.COM;user.principal=hive/hadoop.hadoop.com;user.keytab=conf/hive.keytab";
以上已经经过安全认证，所以Hive数据库的用户名和密码为null或者空。
如下：
```
// 建立连接
connection = DriverManager.getConnection(url, "", "");
```
#### 使用ZooKeeper模式动态获取HiveServer连接
为了避免HiveServer负载不均衡，建议URL中配置ZooKeeper信息，通过ZooKeeper连接HiveServer实现负载均衡，不建议在URL中直接配置某个HiveServer服务IP地址。
正确示例：
```
url="jdbc:hive2://xxx.xxx.xxx.xxx:2181,xxx.xxx.xxx.xxx:2181,xxx.xxx.xxx.xxx:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver;sasl.qop=auth-conf;auth=KERBEROS;principal=hive/hadoop.hadoop.com@HADOOP.COM;user.principal=hive/hadoop.hadoop.com;user.keytab=conf/hive.keytab";
```
错误示例：
```
url="jdbc:hive2://xxx.xxx.xxx.xxx:21066/; sasl.qop=auth-conf;auth=KERBEROS;principal=hive/hadoop.hadoop.com@HADOOP.COM;user.principal=hive/hadoop.hadoop.com;user.keytab=conf/hive.keytab";
```
#### 并发提交任务
Hive on Tez不支持在同一个session中并发提交任务，如果使用Tez引擎，建议使用连接池，每执行完一个任务及时关闭连接。如果需要在同一个session并发提交任务可将执行引擎切换为MapReduce。
#### 执行HQL
执行HQL，注意HQL不能以";"结尾。
**正确示例：**
```
String sql = "SELECT COUNT(*) FROM employees_info";
Connection connection = DriverManager.getConnection(url, "", "");
PreparedStatement statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
```
**错误示例：**
```
String sql = "SELECT COUNT(*) FROM employees_info;";
Connection connection = DriverManager.getConnection(url, "", "");
PreparedStatement statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
```
#### 关闭数据库连接
客户端程序在执行完HQL之后，注意关闭数据库连接，以免内存泄露，同时这是一个良好的编程习惯。
需要关闭JDK的两个对象statement和connection。
如下：
```
finally {
            if (null != statement) {
                statement.close();
            }
            
            // 关闭JDBC连接
            if (null != connection) {
                connection.close();
            }
        }
```
#### HQL语法规则之判空
判断字段是否为"空"，即没有值，使用"is null"；判断不为空，即有值，使用"is not null"。
要注意的是，在HQL中String类型的字段若是空字符串， 即长度为0，那么对它进行IS NULL的判断结果是False。此时应该使用"col = '' "来判断空字符串；使用"col != '' "来判断非空字符串。
**正确示例：**
```
select * from default.tbl_src where id is null;
select * from default.tbl_src where id is not null;
select * from default.tbl_src where name = '';
select * from default.tbl_src where name != '';
```
**错误示例：**
```
select * from default.tbl_src where id = null;
select * from default.tbl_src where id != null;
select * from default.tbl_src where name is null;
select * from default.tbl_src where name is not null;
```
注：表tbl_src的id字段为Int类型，name字段为String类型。
#### 客户端配置参数需要与服务端保持一致
当集群的Hive、YARN、HDFS服务端配置参数发生变化时，客户端程序对应的参数会被改变，用户需要重新审视在配置参数变更之前提交到HiveServer的配置参数是否和服务端配置参数一致，如果不一致，需要用户在客户端重新调整并提交到HiveServer。例如下面的示例中，如果修改了集群中的YARN配置参数时，Hive客户端、示例程序都需要审视并修改之前已经提交到HiveServer的配置参数：
初始状态：
集群YARN的参数配置如下：
```
mapreduce.reduce.java.opts=-Xmx2048M
```
客户端的参数配置如下：
```
mapreduce.reduce.java.opts=-Xmx2048M
```
集群YARN修改后，参数配置如下：
```
mapreduce.reduce.java.opts=-Xmx1024M
```
如果此时客户端程序不做调整修改，则客户端参数仍旧有效，会导致Reducer内存不足而使任务运行失败。
#### 多线程安全登录方式
如果有多线程进行login的操作，当应用程序第一次登录成功后，所有线程再次登录时应该使用relogin的方式。
login的代码样例：
```
private Boolean login(Configuration conf){
    boolean flag = false;
    UserGroupInformation.setConfiguration(conf);
    
    try {
      UserGroupInformation.loginUserFromKeytab(conf.get(PRINCIPAL), conf.get(KEYTAB));
      System.out.println("UserGroupInformation.isLoginKeytabBased(): " +UserGroupInformation.isLoginKeytabBased());
      flag = true;
    } catch (IOException e) {
      e.printStackTrace();
    }
    return flag;
    
  }
```
relogin的代码样例：
```
public Boolean relogin(){
        boolean flag = false;
        try {
            
          UserGroupInformation.getLoginUser().reloginFromKeytab();
          System.out.println("UserGroupInformation.isLoginKeytabBased(): " +UserGroupInformation.isLoginKeytabBased());
          flag = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }
```
#### 使用WebHCat的REST接口以Streaming方式提交MapReduce任务的前置条件
本接口需要依赖hadoop的streaming包，在以Streaming方式提交MapReduce任务给WebHCat前，需要将"hadoop-streaming-2.7.0.jar"包上传到HDFS的指定路径"hdfs:///apps/templeton"下。首先登录到安装有客户端和Hive服务的节点上，以客户端安装路径为"/opt/client"为例：
配置环境变量：
```
source /opt/client/bigdata_env
```
使用**kinit**登录人机用户或者机机用户。
上传"hadoop-streaming-2.7.0.jar"到HDFS的指定路径"hdfs:///apps/templeton"下：
```
hdfs dfs -put ${BIGDATA_HOME}/FusionInsight_HD_8.1.0.1/FusionInsight-Hadoop-*/hadoop/share/hadoop/tools/lib/hadoop-streaming-*.jar /apps/templeton/
```
#### 避免对同一张表同时进行读写操作
目前的版本中，Hive不支持并发操作，需要避免对同一张表同时进行读写操作，否则会出现查询结果不准确，甚至任务失败的情况。
#### 分桶表不支持insert into
分桶表（bucket table）不支持insert into，仅支持insert overwrite，否则会导致文件个数与桶数不一致。
#### 使用WebHCat的部分REST接口的前置条件
WebHCat的部分REST接口使用依赖于MapReduce的JobHistoryServer实例，具体接口如下：
- mapreduce/jar(POST)
- mapreduce/streaming(POST)
- hive(POST)
- jobs(GET)
- jobs/:jobid(GET)
- jobs/:jobid(DELETE)
 
#### Hive授权说明
Hive授权（数据库、表或者视图）推荐通过Manager授权界面进行授权，不推荐使用命令行授权，除了"alter databases databases_name set owner='user_name'"场景以外。
#### 不允许创建Hive on HBase的分区表
Hive on HBase表将实际数据存储在HBase上。由于HBase会将表划分为多个分区，将分区散列在RegionServer上，因此不允许在Hive中创建Hive on HBase分区表。
#### Hive on HBase表不支持INSERT OVERWRITE
HBase中使用rowkey作为一行记录的唯一标识。在插入数据时，如果rowkey相同，则HBase会覆盖该行的数据。如果在Hive中对一张Hive on HBase表执行INSERT OVERWRITE，会将相同rowkey的行进行覆盖，不相关的数据不会被覆盖。
