개요
기존 데이터베이스로는 대용량 데이터 분석 시 성능 병목이 발생하는 경우가 많습니다. 이 글에서는 Spring Boot 환경에서 Hive 서버에 JDBC로 접속하여 데이터를 조회하고 처리하는 방법을 살펴봅니다. CDH(Cloudera Distribution Including Apache Hadoop) 환경을 기준으로 작성되었습니다.
실행 환경
| 구성 요소 | 버전 |
|---|---|
| Spring Boot | 2.1.8.RELEASE |
| CDH | 5.15.1 |
| Hive | 1.1.0 |
Hive 버전 확인
먼저 서버에 접속하여 Hive 버전을 확인합니다.
hive --version출력된 버전 정보를 확인한 후, 클라이언트 의존성도 동일한 버전으로 맞춰야 프로토콜 불일치 문제를 피할 수 있습니다.
프로젝트 설정
Maven 의존성 추가
pom.xml에 Hadoop 공통 라이브러리와 Hive JDBC 드라이버를 추가합니다. Hive 서버 버전이 1.1.0이므로 클라이언트도 동일하게 지정합니다.
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.5</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>1.1.0</version>
</dependency>샘플 애플리케이션
아래 예제는 CommandLineRunner를 활용해 애플리케이션 실행 시 자동으로 Hive 쿼리를 수행하는 코드입니다. 실제 환경에서는 설정 외부화와 예외 처리를 추가하시기 바랍니다.
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.sql.*;
@Slf4j
@SpringBootApplication
public class HiveAnalyticsRunner implements CommandLineRunner {
private static final String HIVE_DRIVER = "org.apache.hive.jdbc.HiveDriver";
private static final String JDBC_URL = "jdbc:hive2://datanode02:10000/test";
private static final String USERNAME = "hive";
private static final String PASSWORD = "";
public static void main(String[] args) {
SpringApplication.run(HiveAnalyticsRunner.class, args);
}
@Override
public void run(String... args) throws Exception {
loadDriver();
try (Connection conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD);
Statement st = conn.createStatement()) {
String targetTable = "sample_metrics";
initializeTable(st, targetTable);
inspectSchema(st, targetTable);
fetchAllRecords(st, targetTable);
executeAggregation(st, targetTable);
}
}
private void loadDriver() {
try {
Class.forName(HIVE_DRIVER);
} catch (ClassNotFoundException ex) {
throw new RuntimeException("Hive 라이버 로드 실패", ex);
}
}
private void initializeTable(Statement statement, String table) throws SQLException {
log.info("기존 테이블 제거 및 재생성");
statement.execute(String.format("DROP TABLE IF EXISTS %s", table));
statement.execute(String.format(
"CREATE TABLE %s (metric_key INT, metric_value STRING)", table));
}
private void inspectSchema(Statement statement, String table) throws SQLException {
log.info("테이블 스키마 확인");
ResultSet rs = statement.executeQuery(String.format("DESCRIBE %s", table));
while (rs.next()) {
log.info("{} : {}", rs.getString(1), rs.getString(2));
}
}
private void fetchAllRecords(Statement statement, String table) throws SQLException {
log.info("전체 레코드 조회");
ResultSet rs = statement.executeQuery(String.format("SELECT * FROM %s", table));
while (rs.next()) {
log.info("key={}, value={}", rs.getInt(1), rs.getString(2));
}
}
private void executeAggregation(Statement statement, String table) throws SQLException {
log.info("집계 쿼리 실행");
ResultSet rs = statement.executeQuery(
String.format("SELECT COUNT(1) AS total FROM %s", table));
if (rs.next()) {
log.info("레코드 수: {}", rs.getLong("total"));
}
}
}실행 결과
애플리케이션을 실행하면 다음과 유사한 로그가 출력됩니다.
INFO o.a.h.j.Utils : Supplied authorities: datanode02:10000
INFO o.a.h.j.HiveConnection : Will try to open client transport with JDBC Uri: jdbc:hive2://datanode02:10000/test
INFO c.e.d.HiveAnalyticsRunner : 기존 테이블 제거 및 재생성
INFO c.e.d.HiveAnalyticsRunner : 테이블 스키마 확인
INFO c.e.d.HiveAnalyticsRunner : metric_key : int
INFO c.e.d.HiveAnalyticsRunner : metric_value : string
INFO c.e.d.HiveAnalyticsRunner : 전체 레코드 조회
INFO c.e.d.HiveAnalyticsRunner : 집계 쿼리 실행
INFO c.e.d.HiveAnalyticsRunner : 레코드 수: 0주의 사항
- 드라이버 클래스 경로: 사용하는 hive-jdbc JAR에 따라 드라이버 클래스명이 상이할 수 있으므로, 실제 패키지 경로를 확인하세요. 일부 버전에서는
org.apache.hive.jdbc.HiveDriver대신 다른 경로를 사용하기도 합니다. - 버전 호환성: 클라이언트와 서버의 Hive 메이저 버전이 다르면 연결 과정에서 오류가 발생할 수 있습니다.
- 리소스 해제: JDBC 리소스는 try-with-resources 문법으로 자동 반납하도록 구성하는 것이 안전합니다.