2019년 10월 21일 월요일

ORC 파일 포맷 v1 자세히 보기

Background
2013년 1월, Hive의 속도 향상과 아파치 하둡에서의 데이터 효율성을 높이기 위해 ORC 파일을 만들었다. 높은 처리 속도와 파일 사이즈 크기를 줄이는 데에 목표를 둔 것이다.

ORC Specification
현재 ORC 파일은 2가지 릴리즈 버전이 있다.
 - ORC v0 : Hive 0.11에서 릴리즈되었다.
 -  Hive 0.12 와 ORC 1.x로 릴리즈되었다.

현재는 ORC v2를 위한 작업 중에 있다.

지금까지 매우 간단한 ORC 소개를 해보았고,
이제 본격적으로(?) ORC v1에 대해서 보자.

ORC Specification v1

Motivation

Hive의 RCFile이 원래 수년간 하둡에서 테이블 데이터를 저장하는 표준 포맷이었으나, RCFile은 각 컬럼을 시멘틱 없이 binary blob으로써만 처리한다는 한계점이 있었다. 그래서 Hive 0.11과 함께 Optimaized Row Columnar (ORC) 파일이라는 새로운 파일 포맷을 추가했다. 
ORC는 타입 정보를 함께 저장하며 이용했다. ORC는 타입 정보를 이용한 경량화된 압축 기법들을 사용했고 그럼으로써 드라마틱한 압축 효과를 볼 수 있었다. 여기서 말하는 경량화된 압축 기법이라 함은 사전 인코딩, 비트 패킹, 델타 인코딩, 연속 길이 인코딩(run length encoding)이었으며 추가적으로 일반적인 압축 방법(zlib, snappy) 또한 적용할 수 있다. 그러나 이런 저장공간 절약은 이점 중 하나에 불과하다.
ORC는 projection을 지원하여 어떤 컬럼을 선택하여 읽기를 요청하든 필요한 만큼만 읽어들여 원하는 컬럼값을 얻을 수 있다. 더 나아가 ORC file은 경량 인덱스(10,000 row 또는 전체 파일에 있는 컬럼에 대한 최소 최대값을 포함)를 포함하고 있다.
ORC file structure




이후의 순서는 ORC 파일 내용을 읽기 위한 순서와 연결해서 생각하면 이해하기 쉽다.

1. File Tail : 파일을 뒤부터 읽어서 필요한 메타데이터를 사용
2. Compression : 압축별 헤더를 통해 skip 가능하도록
3. Stripes : 실제 데이터 부분

File Tail (파일의 끝부분)

HDFS는 파일 쓰기 이후에 데이터 변경을 지원하지 않으므로 ORC는 파일의 끝에 top level index를 저장했다. 
먼저 알아야할 것이 ORC의 메타데이터들은 Protocol Buffer를 사용하여 저장된다. file tail은 아래와 같이 구성되어 있다. 오른쪽 정보는 해당 메타데이터를 표현하는 protobuf meesage type이다.

 - 암호화된 stripe 통계 : list of ColumnarStripeStatistics
 - stripe 통계 : Metadata
 - footer : Footer
 - postscript : PostScript
 - psLen : byte

PostScript

ORC file에서 가장 처음 읽게 되는 정보라고 생각하면 된다. 나머지 파일의 어떤 부분을 어떻게 읽어야할지에 대한 기본 정보를 포함한다. 그러므로 Postscript는 압축되지 않는다.

 - footerLength
 - compression
 - compressionBlockSize
 - version 
 - metadataLength
 - magic // the fixed string "ORC"

*version : 이 파일을 읽을 수 있는 Hive 최소 버전을 sequence로 나타낸다. ex) [0,12] : 0.12 버전 이상

Footer

파일의 바디에 대한 레이아웃 정보를 포함한다.

 - headerLength
 - contentLength
 - (repeated) stripes // Stripe Information
 - (repeated) types // Type Information
 - (repeated) metadata // UserMetadataItem
 - numberOfRows
 - (repeated) statistics // ColumnStatistics
 - rowIndexStride
 - writer
 - encryption
 - stripeStatisticsLength

*Stripe Information : 각각의 stripe는 하나의 row가 나눠지는 일은 없도록 데이터를 포함시킨다. 그리고 stripe는 크게 3개의 섹션 indexes, data, stripe footer로 나눠진다.
*Type Information : ORC 파일 내 모든 row는 같은 스키마를 가져야한다. 그리고 tree 구조로 나타냄으로써 map, struct 같은 계층적인 타입으로 표현할 수 있다.
ORC column structure
*User Metadata : 사용자가 임의로 key/value pair를 추가할 수 있다.
*Column Statistics : 파일내 모든 컬럼별 통계 정보를 저장하는데, writer가 타입별로 미리 정의된 유용한 정보를 기록한다. 대부분의 primitive 타입은 최소, 최대를 포함하고 숫자 형태는 sum까지 저장한다.

Metadata

 - (repeated) stripeStats // StripeStatistics
* StripeStatistics는 ColumnStatistics Set

즉 metadata에는 stripe level에서의 컬럼별 통계 정보를 포함한다.  데이터 조건절에 대한 stripe별 평가 지표로 사용될 수 있다.

Compression

ORC file writer에 일반 압축 코덱(zlib or snappy)를 선택하면, Postscript를 제외한 모든 부분이 해당 코덱으로 압축된다.
그러나 ORC 요구사항 중엔 이런게 있다.
 'reader는 전체 stream을 압축 해제하지 않고도 필요 없는 데이터(압축된)를 스킵할 수 있어야 한다' 
그래서 ORC는 아래와 같이 청크별로 데이터를 압축하며 3 bytes의 헤더를 붙인다. 압축된 후가 더 크다면 원래 정보를 그대로 저장하며 isOriginal 비트에 체크한다.
compression chunk default size는 256K이긴 하나, writer에서 해당 값을 선택할 수도 있다. chunk가 커지면 압축률은 좋아지겠지만 더 많은 메모리를 사용할 것이다. 지정된 청크 사이즈는 Postscript에 기록되므로 그에 맞는 적절한 크기의 버퍼를 통해 데이터를 read할 수 있다. Reader는 압축된 청크를 해제하더라도 지정된 청크 사이즈보다는 커지지 않을 것이라는 걸 알고 있기 때문에.

compression streams

Stripes

ORC 파일 바디는 stripe들로 구성되어 있다.
ORC 파일에서 각각의 컬럼은 여러개의 stream들을 통해 파일 내에 나란히 저장된다.

예1) integer column 같은 경우엔, PRESENT, DATA 라는 2개의 스트림으로 표현된다. PRESENT 는 데이터가 null이 아닌지를 표현하는 하나의 비트 표현이고, DATA는 null이 아닌 데이터를 기록한다. 모든 컬럼의 값이 null이 아니라면 PRESENT 스트림은 생략될 수 있다.

예2) binary data를 저장하기 위해서는 PRESENT, DATA, LENGTH를 사용한다. LENGTH 말 그대로 각 값의 크기(length)를 저장한다.

Stripe 구조는 아래와 같다.

 - index streams
 - data streams
 - stripe footer

* index streams : row group index, bloom filter index

Stripe Footer

Stripe footer는 각 컬럼별 인코딩 정보와 stripe 내 stream에 대한 위치를 포함한 정보를 가지고 있다.
 - (repeated) streams // Stream
 - (repeated) columns // ColumnEncoding
 - writerTimezone
 - (repeated) encryption

*Stream : 각각의 스트림 정보를 설명하기 위해, 스트림별로 종류(kind), columm id, size in bytes를 저장한다.




2019년 6월 26일 수요일

Hive 3.x의 새로운 변경 1. Managed Table vs External Table

Hive 3.x을 고려한다면 알아야할 변경 사항 1. Table Type

Managed Table 과 External Table


처음에 Hive 3.0이 나오면서 새로운 기능들에 초점을 맞추어 읽었고, (확실히 Hortonworks에서 Hive 개발을 많이 하는 가보다) Hive 3.0에 추가된 몇가지만 소개하면 아래와 같습니다.

  • Execution engine changes : Hive default engine으로서 Tez가 자리 잡다.
  • Design changes that affect security : 주요 권한 모델로 Ranger를 사용.
  • HDFS permission changes : Ranger를 안 쓴다면 HDFS ACL를 사용한다는 데 뭐가 바뀐건지는..
  • Transaction processing changes : 2에서 제공하던 transactional 기능과 성능 및 사용성 향상
  • Query execution of batch and interactive workloads : 뭔가 신기해보이는데 아직 잘 모르겠..

(위 내용은  HDP3 에서 hive 내용을 참고했습니다.)


 그런데 막상 적용하려고 보니 "기존에 제공하던 기능들이 여전히 잘 동작하느냐?" 가 관건이었고, 그 중에서도 테이블에 관한 변화에 대해서는 Hive 3을 고려할 때 가장 우선이 되어야할 내용이라고 생각했습니다.

 추가된 기능 상세 내용보다는 Hive 2와 비교해서 어떤 점이 달라졌고, 그래서 Hive 3을 쓰려면 무엇을 고려해야 하는지에 초점을 맞춰서 살펴보았습니다.

A. Managed Table의 default는 transactional(ACID)이며, ORC fileformat을 갖는다.

 - 기본 설정으로 사용할 경우, insert 뿐 아니라 row 단위의 update/delete가 가능하며 파티션별 트랜잭션으로 작업을 수행(isolation)합니다.

 - file format을 ORC외의 타입을 설정하면 insert_only로 지정됩니다.
 -- insert_only도 insert와 select 시에 실행 중인 것과 실패(aborted)된 트랜잭션을 고려하여 작업을 수행합니다.

 - transactional=true가 기본값으로 설정되며, managed table로서 사용하는 경우 변경할 수 없습니다.


B. Hive는 managed table에 대한 모든 제어 권한을 갖는다.

 - Managed table에 대한 데이터를 Hive를 통해서만 접근, 변경할 수 있습니다.
 -- 만약 Hive를 지나쳐서 data에 직접 접근하고 싶다면, external table을 사용하거나 Druid나 HBase 같은 storage handler를 사용해야 합니다.


아래 표는 Hive에서 사용하는 테이블 타입별 ACID 지원 여부와 필요로 하는 스토리지 포맷과 주요 SQL 처리에 대해 나타내고 있습니다.

Table TypeACIDFile FormatINSERTUPDATE/DELETE
Managed: CRUD transactionalYesORCYesYes
Managed: Insert-only transactionalYesAnyYesNo
Managed: TemporaryNoAnyYesNo
ExternalNoAnyYesNo


C. Managed table의 Owner는 Hive이다.


 - 위의 설명을 보면서, Insert-only transactional 을 보고 Hive 2에서 쓰던 일반 테이블과 비슷하겠구나 생각했었는데 이게 또 같지가 않았습니다.
 -- HDP 에서 2.x와 3.x의 테이블 타입의 차이점을 잘 설명하고 있습니다.

HDP 2.xHDP 3.x
Table TypeACID v1FormatOwner (user) of Hive Table FileTable TypeACID v2
ExternalNoNative or non-nativehive or non-hiveExternalNo
ManagedYesORChive or non-hiveManaged, updatableYes
ManagedNoORChiveManaged, updatableYes
non-hiveExternal, with data delete*No
ManagedNoNative (but non-ORC)hiveManaged, insert onlyYes
non-hiveExternal, with data delete*No
ManagedNoNon-nativehive or non-hiveExternal, with data delete*No

 - 위의 내용을 종합해서 보면, ORC 외의 native format (parquet, avro, rcfile 등)을 사용하는 managed table 중에서 hive가 아닌 외부 사용자(non-hive)를 통해서 데이터를 적재하던 테이블은 external table이 되어야 한다는 내용을 볼 수 있습니다.

 - external table로 사용해야 한다고 하더라도 with data delete*라고 해서 external.table.purge=true 테이블 프로퍼티를 사용하면 delete를 통해 HDFS에서의 삭제까지도 연결해서 사용 가능하도록 하고 있습니다.
-- https://issues.apache.org/jira/browse/HIVE-19981
-- 이 표 또한 HDP 문서인만큼 여기에선 문제가 없겠지만, 해당 JIRA를 보면 master에만 merge가 되었고 Fix Version에 4.0.0만 표기가 되어있습니다.
-- 즉 Apache Hive를 사용할 사람이라면 어느 버전에서 사용 가능할지 추가 확인이 필요합니다. 또한 이런 내용들(4.0.0에서나 반영될 수 있는 이슈와 버그)을 체크하여 patch 대상을 관리해야할 것으로 보입니다.



References
 - https://docs.hortonworks.com/HDPDocuments/Ambari-2.7.1.0/bk_ambari-upgrade-major-ppc/content/hive_post_upgrade_tasks.html
 - https://docs.hortonworks.com/HDPDocuments/HDP3/HDP-3.0.1/using-hiveql/content/hive_hive_3_tables.html
 - https://docs.hortonworks.com/HDPDocuments/HDP3/HDP-3.0.1/hive-overview/content/hive-apache-hive-3-architectural-overview.html