C#에서 Hive 조회·적재하기
Windows 서버에 배포해 Hive 테이블을 만들고(INSERT OVERWRITE / CTAS) 조회하는 C# 예제 두 가지입니다. 실제 배치에는 권장 코드를, 연결이 되는지 먼저 확인할 때는 간단 코드를 쓰면 됩니다.
회사에 Trino가 있다면 ODBC 대신 NuGet Trino.Ado로도 붙을 수 있습니다. 차이는 Trino.Ado 소개와 ODBC 연결과의 차이에 정리했습니다.
0공통 준비
드라이버 설치, DSN 만들기, 인증 방식 고르기는 Hive ODBC 설정 가이드에 단계별로 정리해 두었습니다. 처음이라면 먼저 보고 오세요.
- 서버와 개발 PC에 Cloudera ODBC Driver for Apache Hive(또는 Microsoft Hive ODBC Driver)를 설치합니다.
- 비트 수를 맞춥니다. 64비트 드라이버를 설치했다면 프로젝트 속성 > 빌드 > 플랫폼 대상을
x64로 둡니다.Any CPU+ "32비트 선호"면 32비트로 떠서 "데이터 원본 이름이 없고 기본 드라이버를 지정하지 않았습니다(IM002)" 오류가 납니다. - 연결 문자열의
Driver={...}이름은 ODBC 데이터 원본 관리자의 드라이버 탭에 보이는 이름(PowerShellGet-OdbcDriver -Name "*Hive*")과 글자까지 같아야 합니다. Microsoft 드라이버라면{Microsoft Hive ODBC Driver}입니다. AuthMech는 클러스터 인증 방식에 맞춥니다. 0 = 인증 없음, 2 = 사용자 이름만, 3 = 사용자 이름+비밀번호(LDAP), 1 = Kerberos.
1권장 코드 운영 배치용
파일은 HiveJob.cs 하나에 클래스 두 개(HiveClient, Program)가 들어 있고, 접속 정보는 App.config에 따로 둡니다.
전일 파티션을 INSERT OVERWRITE로 다시 만들고, 건수를 조회해 확인한 뒤 종료 코드로 성공·실패를 알립니다.
HiveJob.cs
// HiveJob.cs
// 대상: .NET Framework 4.5 콘솔 앱 (C# 5 문법만 사용)
// 참조 추가: System.Configuration (System.Data 는 기본 참조)
// 실행: HiveJob.exe -> 전일(어제) 파티션 생성
// HiveJob.exe 20261026 -> 지정한 날짜 파티션 생성
using System;
using System.Configuration;
using System.Data;
using System.Data.Odbc;
using System.Globalization;
namespace HiveJob
{
/// <summary>
/// HiveServer2 에 ODBC 로 접속해 조회/실행하는 작은 래퍼.
/// 호출마다 연결을 열고 닫는다 (ODBC 연결 풀링이 재사용을 처리).
/// </summary>
public sealed class HiveClient
{
private readonly string _connectionString;
private readonly int _commandTimeoutSeconds;
public HiveClient(string connectionString, int commandTimeoutSeconds)
{
if (string.IsNullOrEmpty(connectionString))
throw new ArgumentException("연결 문자열이 비어 있습니다.", "connectionString");
_connectionString = connectionString;
_commandTimeoutSeconds = commandTimeoutSeconds;
}
/// <summary>SELECT 결과를 DataTable 로 돌려준다. ? 자리에 args 가 순서대로 들어간다.</summary>
public DataTable Query(string sql, params object[] args)
{
using (var conn = new OdbcConnection(_connectionString))
using (var cmd = CreateCommand(conn, sql, args))
using (var adapter = new OdbcDataAdapter(cmd))
{
var table = new DataTable();
conn.Open();
adapter.Fill(table);
return table;
}
}
/// <summary>INSERT OVERWRITE, CREATE TABLE AS SELECT, SET 등 결과가 없는 문장을 실행한다.</summary>
public void Execute(string sql, params object[] args)
{
using (var conn = new OdbcConnection(_connectionString))
using (var cmd = CreateCommand(conn, sql, args))
{
conn.Open();
// Hive 드라이버는 영향받은 행 수를 대부분 -1 로 돌려주므로 반환값은 쓰지 않는다.
cmd.ExecuteNonQuery();
}
}
private OdbcCommand CreateCommand(OdbcConnection conn, string sql, object[] args)
{
var cmd = conn.CreateCommand();
cmd.CommandText = sql;
cmd.CommandTimeout = _commandTimeoutSeconds; // 0 이면 무제한
if (args != null)
{
foreach (var value in args)
{
var p = cmd.CreateParameter();
p.Value = value ?? DBNull.Value;
cmd.Parameters.Add(p); // ODBC 는 이름이 아니라 순서로 바인딩
}
}
return cmd;
}
}
internal static class Program
{
private static int Main(string[] args)
{
try
{
var setting = ConfigurationManager.ConnectionStrings["Hive"];
if (setting == null)
throw new ConfigurationErrorsException("App.config 의 connectionStrings 에 'Hive' 항목이 없습니다.");
int timeout;
if (!int.TryParse(ConfigurationManager.AppSettings["HiveCommandTimeout"], out timeout))
timeout = 3600;
var hive = new HiveClient(setting.ConnectionString, timeout);
// 기준일: 인자가 있으면 그 날짜(yyyyMMdd), 없으면 전일
DateTime baseDate = args.Length > 0
? DateTime.ParseExact(args[0], "yyyyMMdd", CultureInfo.InvariantCulture)
: DateTime.Today.AddDays(-1);
string dt = baseDate.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
Log("작업 시작 dt=" + dt);
// 파티션 값은 위에서 DateTime 으로 만든 문자열이라 외부 입력이 섞이지 않는다.
// (PARTITION 절에는 ? 파라미터를 쓸 수 없어 문자열로 넣는다.)
string insertSql = string.Format(
"INSERT OVERWRITE TABLE mart.daily_sales PARTITION (dt = '{0}') " +
"SELECT store_id, SUM(amount) AS total_amount, COUNT(*) AS order_cnt " +
"FROM raw.orders " +
"WHERE dt = '{0}' " +
"GROUP BY store_id", dt);
hive.Execute(insertSql);
Log("INSERT OVERWRITE 완료");
// 일반 조건절 값은 ? 파라미터로 넘긴다.
DataTable result = hive.Query(
"SELECT COUNT(*) AS cnt FROM mart.daily_sales WHERE dt = ?", dt);
long count = Convert.ToInt64(result.Rows[0][0], CultureInfo.InvariantCulture);
Log("적재 건수=" + count);
if (count == 0)
{
Log("적재 건수가 0 건이라 실패로 처리합니다.");
return 1;
}
return 0; // 작업 스케줄러에서 0 = 성공
}
catch (Exception ex)
{
Console.Error.WriteLine(Stamp() + " 오류: " + ex);
return 1; // 0 이 아니면 실패로 감지
}
}
private static void Log(string message)
{
Console.WriteLine(Stamp() + " " + message);
}
private static string Stamp()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
}
}
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<connectionStrings>
<!-- 드라이버 이름은 ODBC 데이터 원본 관리자 > 드라이버 탭에 보이는 이름과 정확히 같아야 합니다. -->
<add name="Hive"
connectionString="Driver={Cloudera ODBC Driver for Apache Hive};Host=hive-server01;Port=10000;Schema=default;HiveServerType=2;AuthMech=3;UID=etl_user;PWD=********;" />
<!-- DSN 을 미리 만들어 둔 경우: connectionString="DSN=HiveProd;UID=etl_user;PWD=********;" -->
</connectionStrings>
<appSettings>
<!-- 명령 타임아웃(초). 대용량 INSERT OVERWRITE 는 넉넉하게. 0 = 무제한 -->
<add key="HiveCommandTimeout" value="3600" />
</appSettings>
</configuration>
코드 설명
HiveClient.Query
SELECT 결과를 DataTable로 받아옵니다. 연결은 호출마다 열고 using으로 바로 닫습니다.
HiveClient.Execute
INSERT OVERWRITE, CTAS, SET처럼 결과 집합이 없는 문장용입니다. Hive 드라이버는 영향받은 행 수를 보통 -1로 주기 때문에, 적재 결과는 따로 COUNT로 확인합니다.
CommandTimeout
기본 30초로는 MapReduce/Tez 작업이 끝나기 전에 끊깁니다. App.config의 HiveCommandTimeout(초)으로 조절하고 0이면 무제한입니다.
? 파라미터
ODBC는 이름이 아니라 순서로 바인딩합니다. 다만 PARTITION (dt=…) 절에는 파라미터를 쓸 수 없어서, 코드에서 DateTime으로 만든 값만 문자열로 넣습니다.
기준일 인자
인자 없이 실행하면 전일, HiveJob.exe 20261026처럼 주면 그 날짜를 다시 적재합니다. 재처리할 때 편합니다.
종료 코드 0 / 1
성공이면 0, 예외가 나거나 적재 건수가 0이면 1을 돌려줍니다. 작업 스케줄러의 "마지막 실행 결과"나 배치 툴에서 실패를 바로 잡아냅니다.
CTAS로 바꾸려면 insertSql을 CREATE TABLE mart.daily_sales_tmp STORED AS ORC AS SELECT …로 바꾸고 hive.Execute로 실행하면 됩니다.
이미 있는 테이블이면 먼저 DROP TABLE IF EXISTS를 실행하세요. 테이블·컬럼 이름(mart.daily_sales, raw.orders)은 예시이니 실제 이름으로 바꿔 주세요.
2간단 코드 연결 테스트용
콘솔 앱 Program.cs 하나로 끝납니다. 연결 문자열을 코드에 직접 쓰고, 테이블 10행을 읽어 탭으로 구분해 출력합니다.
드라이버 설치와 방화벽, 계정이 맞는지 처음 확인할 때 쓰기 좋습니다.
// Program.cs (.NET Framework 4.5 콘솔 앱, 추가 참조 없음)
using System;
using System.Data.Odbc;
class Program
{
static void Main()
{
string connStr =
"Driver={Cloudera ODBC Driver for Apache Hive};" +
"Host=hive-server01;Port=10000;Schema=default;" +
"HiveServerType=2;AuthMech=3;UID=etl_user;PWD=********;";
using (var conn = new OdbcConnection(connStr))
{
conn.Open();
using (var cmd = new OdbcCommand("SELECT * FROM sample_table LIMIT 10", conn))
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
Console.WriteLine(string.Join("\t", values));
}
}
}
Console.WriteLine("완료. 아무 키나 누르세요.");
Console.ReadKey();
}
}
코드 설명
OdbcConnection으로 HiveServer2(10000 포트)에 붙고OdbcCommand.ExecuteReader로 결과를 한 행씩 읽습니다.reader.GetValues로 한 행의 모든 컬럼을 배열에 담아 출력하므로 컬럼 구성을 몰라도 동작합니다.- 예외 처리와 타임아웃 설정이 없어서 오래 걸리는 쿼리(기본 30초 초과)나 운영 배치에는 맞지 않습니다.
- 마지막
Console.ReadKey()는 창이 바로 닫히지 않게 하는 용도입니다. 스케줄러에서 돌린다면 지워야 합니다.
3두 방식의 차이점
| 항목 | 간단 코드 | 권장 코드 |
|---|---|---|
| 목적 | 연결 확인, 빠른 조회 | Windows 서버에서 도는 정기 적재 배치 |
| 파일 구성 | Program.cs 1개 | HiveJob.cs 1개 + App.config |
| 접속 정보 | 코드에 하드코딩 (바꾸면 재빌드) | App.config (서버별로 파일만 수정) |
| 코드 구조 | Main 안에 모두 작성 | HiveClient로 접속 로직 분리, Main은 작업 흐름만 |
| 조회 방식 | DataReader로 한 행씩 출력 | DataTable로 받아 후속 처리 가능 |
| 데이터 생성 | 없음 (SELECT만) | INSERT OVERWRITE / CTAS 실행 후 건수 검증 |
| 타임아웃 | 기본 30초 | 설정값 (기본 3600초, 0 = 무제한) |
| 파라미터 | 없음 | ? 순서 바인딩 지원 |
| 날짜 처리 | 없음 | 기본 전일, 인자로 재처리 날짜 지정 |
| 오류 처리 | 없음 (예외 시 프로그램 중단) | try/catch + 오류 로그를 stderr로 |
| 로그 | 결과만 출력 | 시각이 붙은 단계별 로그 |
| 종료 코드 | 항상 0 | 성공 0 / 실패 1 (스케줄러가 실패 감지) |
| 무인 실행 | ReadKey에서 멈춤 | 가능 |
4Windows 서버 배포 체크
- 빌드 결과 폴더의
HiveJob.exe와HiveJob.exe.config(App.config가 빌드 시 이 이름으로 바뀜)를 함께 복사합니다. - 서버에도 개발 PC와 같은 비트 수의 Hive ODBC 드라이버를 설치합니다.
- 서버에서 HiveServer2 호스트의 10000 포트로 나갈 수 있는지 확인합니다. PowerShell:
Test-NetConnection hive-server01 -Port 10000 - 작업 스케줄러에 등록하고, "마지막 실행 결과"가
0x0이면 성공,0x1이면 실패입니다. 로그를 남기려면 동작을cmd /c HiveJob.exe >> C:\logs\hivejob.log 2>&1로 등록합니다.
App.config의 비밀번호는 평문입니다. 운영에서는 서버 접근 권한을 제한하거나 DSN에 저장하는 방식을 검토하세요.