이번 글에서는 Hive의 디테일한 아키텍처와 HiveServer2, Hive Metastore의 내용을 주로 다루도록 하겠습니다.
Hive 아키텍쳐
Hive 아키텍처는 크게 HiveServer와 RDB, Hadoop의 관계로 구성할 수 있습니다. HiveServer는 Apache Thrift를 기반으로 만들어졌습니다. 여기서 Thrift는 서로 다른 언어 간의 통신을 가능하게 해주는 cross-language RPC 프레임워크로써, HiveServer2 이전에는 HiveServer를 Thrift server라고 부르곤 했습니다. 이번 글에서는 편의상 HiveServer2 이전 버전을 HiveServer1이라고 하겠습니다.
- Client : Client가 Hive 쿼리를 사용할 수 있는 인터페이스.
- Driver : Compiler, Execution Engien, Client의 중간 역할로써 쿼리를 받아 각 모듈로 전달함.
- Compiler : 쿼리를 파싱하고 Metastore로부터 metadata를 받아 최종적으로 Execution plan을 생성.
- Metastore : Hive 테이블과 partition의 논리적인 정보를 보관하고 Client의 요청에 따라 관련 정보를 제공.
- Execution Engine : Compiler가 생성한 DAG로 구성된 execution plan을 실행하는 컴포넌트.
Client가 Hive 쿼리를 사용하면 JDBC/ODBC를 통해 Driver에게 전달됩니다. Driver는 session을 하나 생성하면서 해당 쿼리를 Compiler에게 전달합니다. Compiler는 쿼리에 대한 metadata를 Metastore로부터 받아 DAG를 생성하며 DAG의 각 stage는 Map / Reduce job 을 의미합니다. Execution Engine은 Driver를 통해 해당 DAG를 전달받고 필요한 Hadoop 모듈들과 통신하며 Map / Reduce job 을 실행합니다.
Hive가 테이블 데이터를 HDFS에 저장하지만 실제로는 RDB도 사용하며 데이터베이스나 테이블의 metadata를 저장합니다. 그리고 해당 RDB와 직접적인 통신을 하는 것이 Hive Metastore입니다.
HiveServer1
하지만 기존의 HiveServer1는 Client의 session을 관리하는데 큰 문제를 가지고 있었습니다. 기본적으로 HiveServer1은 thread-local 변수를 사용하여 Client의 session을 유지했고, Client들과 Session들의 매핑 정확성을 Thrift에 의존했습니다. 그러다 보니 아래 예시와 같이 동일한 Client가 첫 번째 session에서 설정한 x의 값이 두 번째 session에도 그대로 남아있는 문제점들이 발생했습니다. 게다가 Thirift는 Client의 disconnection을 감지하는 서비스가 존재하지 않았기 때문에, 특정 session의 thread를 초기화할 수 없었습니다. 그래서 결국 multi-client concurrency를 지원할 수 없었습니다.
# First Connection
% hive -h localhost -p 10000
[localhost:10000] hive> set x=1;
set x=1;
[localhost:10000] hive> set x;
set x;
x=1
[localhost:10000] hive> quit;
quit;
# Second Connection
% hive -h localhost -p 10000
[localhost:10000] hive> set x;
set x;
x=1
[localhost:10000] hive> quit;
quit;
HiveServer2
Hive 0.11 버전부터 HiveServer2를 사용하게 되었고 위에서 말한 HiveServer1의 Client session 관리 문제점을 보완했습니다. Client의 모든 RPC call에게 session ID를 발급하면서 multi-client concurrency를 가능하게 했습니다.
Client의 경우에는 HiveServer2로 업그레이드되면서 Beeline, JDBC, ODBC, Python client, Ruby client로 다양해졌고 기본적으로 Hiveserver2가 Thrift 기반으로 만들어졌기 때문에 Thirft API 통신 또한 가능합니다. 그리고 이전에는 제공하지 않았던 웹 UI를 제공하고 있습니다.
Beeline은 SQLLine 기반의 JDBC client 입니다. Beeline에서 HiveServer2에 connection을 해야 하는 과정이 필요하고 접속 이후에는 Hive CLI처럼 Hive 쿼리, 결과 output 또한 확인이 가능합니다. Beeline을 통해 HiveServer2에 접속할 때는 username, userpassword를 입력해야 하기 때문에 Hive CLI를 직접적으로 사용하는 것보다 보안에 있어 안전함이 있습니다. Hiveserver2를 클러스터의 모든 서버에 설치할 수는 없습니다. 그래서 Beeline의 경우 원격지에 있는 Hiveserver2에 접속 시도를 해야 하는데 이때 Thrift 통신을 이용합니다.
※ Hiveserver2 접속 명령어
Embedded Client - $ !connect jdbc:hive2://
Remote Client - $ !connect jdbc:hive2://<host>:<port>/<db>
HiveServer2는 Client의 session을 관리하기 위해서 Connection pool과 Session pool이라는 개념을 사용합니다.
Connection Pool
Client가 HiveServer2에 connection 할 수 있는 수를 의미합니다. HiveServer2로 들어오는 connection들은 HiveServer2-handler thread에 의해서 관리가 됩니다. 이때 Connection Pool의 크기는 옵션 값을 통해 설정이 가능하고, Pool의 크기를 초과하는 경우(HiveServer2-handler thread의 수가 부족할 경우) Connection이 이루어지지 않습니다.
※ 관련 옵션
- hive.server2.thrift.max.worker.threads
Session Pool
동시에 쿼리를 실행할 수 있는 session 수를 의미합니다. Connection Pool에 있는 connection이 Session Pool이 비어있을 때 Hive 쿼리를 수행할 수 있습니다. 만약 Session Pool의 크기를 초과할 경우 해당 쿼리 수행은 다른 쿼리 수행이 끝날 때까지 기다려야 합니다.
Hive Metastore
Hive Metastore는 HiveServer2와 같이 Hive 아키텍처의 중요한 프로세스 중 하나로써 Hive 테이블의 파티션과 구조, column 정보 등 metadata를 RDB에 저장하고 가져옵니다.
Metastore가 필요한 이유에 대해 생각했을 때 Data abstraction과 Data discovery가 있습니다. Data abstraction로써 역할은 테이블 데이터에 대한 metadata 정보를 소유하고 있다는 점입니다. 만약 이러한 특징이 없다면, 사용자는 쿼리와 관련된 데이터 포맷 등 여러 정보들을 제공해야만 했습니다. 하지만 Hive의 경우 테이블이 생성되는 동안 metadata가 만들어지고 해당 테이블이 사용될 때마다 재사용이 가능합니다. 그리고 Data discovery로써 역할은 사용자가 저장되어 있는 데이터의 metadata를 다룰 수 있다는 점입니다. 특정 데이터와 관련된 데이터를 찾거나 metadata를 통해서 데이터를 모니터링하거나 사용성을 높일 수 있는 툴을 개발할 수도 있습니다.
위의 특징들을 위해서 Metastore는 DataNuCleus라는 Object-Relational Mapping 즉, DB 데이터를 자바 객체로써 다룰 수 있는 Java Data Objects(JDO) 구현체입니다. 이를 사용하는 가장 큰 이유는 Hive의 metadata를 쿼리로써 다룰 수 있기 때문입니다. 하지만 실제 데이터 저장과 meta 데이터 저장을 분리해놓으면서 동기화나 확장성에 이슈가 생기는 단점이 있습니다.
Metastore는 두 가지 방법으로 사용 가능합니다. 첫 째는 주로 사용하는 remote mode로써 Thrift 통신을 통해 Metastore에 접근합니다. Thrift 통신을 하기 때문에 Client는 꼭 Java를 사용할 필요가 없습니다. 두 번째는 embedded mode로써 Client가 metastore에 있는 JDBC에 바로 통신합니다. Hiveserver2가 Metastore에게 요청할 때 Thrift API를 사용합니다.
References
https://cwiki.apache.org/confluence/display/Hive/Design
https://cwiki.apache.org/confluence/display/Hive/HiveServer2+Thrift+API
https://cwiki.apache.org/confluence/display/Hive/HiveServer2+Clients
https://cwiki.apache.org/confluence/display/hive/design#Design-Compiler
'Hadoop Ecosystem > Hive' 카테고리의 다른 글
[Hive] IntelliJ로 Runtime Debugging하기 (0) | 2022.11.12 |
---|---|
[Hive] limit 사용 시 leastNumRows 에러 발생 이슈 (0) | 2022.11.04 |
[Hive] Metastore의 heap memory 증가 이슈 해결 과정 (2) | 2022.07.18 |
[Hive] Unable to fetch table .null 에러 해결 방법 (0) | 2022.05.30 |
[Hive] Metastore & MySQL 문제로 Hive Query 실행 안되는 이슈 해결 방법 (0) | 2022.05.04 |