Regions
Regions
Region은 테이블의 가용성과 분산의 기본 요소이며, 컬럼 패밀리당 하나의 Store로 구성돼요. HBase가 region 수를 어떻게 관리하는지, region-RegionServer 할당과 로컬리티, 분할과 병합, Store와 MemStore/StoreFile/KeyValue, 그리고 다양한 압축(compaction) 정책을 살펴볼게요.
출처: 문서
본문
Region은 테이블의 가용성과 분산의 기본 요소이며, 컬럼 패밀리당 하나의 Store로 구성돼요. 객체의 계층 구조는 다음과 같아요.
Table (HBase table)
Region (Regions for the table)
Store (Store per ColumnFamily for each Region for the table)
MemStore (MemStore for each Store for each Region for the table)
StoreFile (StoreFiles for each Store for each Region for the table)
Block (Blocks within a StoreFile within a Store for each Region for the table)
HBase 파일이 HDFS에 기록될 때 어떻게 보이는지에 대한 설명은 Browsing HDFS for HBase Objects를 참고해 주세요.
region 수에 대한 고려 사항 (Considerations for Number of Regions)
일반적으로 HBase는 서버당 상대적으로 큰(5-20Gb) region을 적은 수(20-200개)로 실행하도록 설계됐어요. 이에 대한 고려 사항은 다음과 같아요.
지역 수를 낮게 유지해야 하는 이유는 무엇인가요? (Why should I keep my Region count low?)
일반적으로 여러 이유로 HBase에서 region 수를 낮게 유지하고 싶어요. 보통 RegionServer당 100개 정도의 region이 가장 좋은 결과를 냈어요. region 수를 낮게 유지해야 하는 이유 중 일부는 다음과 같아요.
- MSLAB(MemStore-local allocation buffer)은 MemStore당 2MB를 필요로 해요(region마다 패밀리당 2MB). 2개 패밀리를 가진 1000개 region은 힙 3.9GB를 사용하며, 아직 데이터를 저장하지 않은 상태예요. 참고: 2MB 값은 구성 가능해요.
- 모든 region을 거의 같은 속도로 채우면, 전역 메모리 사용량 때문에 region이 너무 많을 때 작은 플러시가 강제되어 압축을 유발해요. 같은 데이터를 수십 번 다시 쓰는 것은 정말 피하고 싶은 일이에요. 예를 들어 1000개 region(하나의 패밀리)을 균등하게 채우고 전역 MemStore 사용량 하한을 5GB라고 가정해 봐요(region server는 큰 힙을 가질 것). 5GB에 도달하면 가장 큰 region을 강제로 플러시하며, 그 시점에는 거의 모든 region에 약 5MB의 데이터가 있어서 그 양을 플러시할 거예요. 5MB가 나중에 삽입되면 또 다른 region(이제 5MB를 약간 넘는 데이터)이 플러시될 거예요. 이는 현재 region 수의 주요 제한 요소이며, 상세 공식은 Number of regions per RS - upper bound를 참고해 주세요.
- Master는 현재 많은 region에 민감하며, region을 배치로 할당하고 이동하는 데 많은 시간을 소요해요. 이유는 ZK 사용이 무겁고 현재 그다지 비동기적이지 않기 때문이에요(개선될 수 있고 — 0.96 HBase에서 많이 개선되었음).
- HBase의 오래된 버전(사전 HFile v2, 0.90 및 이전)에서는 몇 개 RS에 많은 region이 있으면 store file 인덱스가 증가해 힙 사용량이 늘고 RS에 메모리 압박이나 OOME를 일으킬 수 있어요.
또 다른 문제는 MapReduce 작업에 대한 region 수의 영향이에요. 일반적으로 HBase region 하나당 매퍼 하나가 사용돼요. 따라서 RS당 5개 region만 호스팅하면 MapReduce 작업에 충분한 태스크 수를 얻지 못할 수 있는 반면, 1000개 region은 훨씬 많은 태스크를 생성할 거예요.
구성 지침은 Determining region count and size를 참고해 주세요.
Region-RegionServer 할당 (Region-RegionServer Assignment)
이 섹션은 Region이 RegionServer에 어떻게 할당되는지 설명해요.
시작 (Startup)
HBase가 시작할 때 region은 다음과 같이 할당돼요(짧게):
- Master가 시작 시 AssignmentManager를 호출해요.
- AssignmentManager가 hbase:meta의 기존 region 할당을 확인해요.
- 할당이 여전히 유효하면(즉, RegionServer가 여전히 온라인이면) 할당이 유지돼요.
- 할당이 유효하지 않으면 LoadBalancerFactory가 호출되어 region을 할당해요. 로드 밸런서(HBase 1.0에서 기본 StochasticLoadBalancer)가 region을 RegionServer에 할당해요.
- RegionServer가 region을 열 때(필요하면) hbase:meta가 RegionServer 할당과 RegionServer 시작 코드(RegionServer 프로세스의 시작 시각)로 갱신돼요.
장애 조치 (Failover)
RegionServer가 실패하면:
- RegionServer가 다운되었으므로 region이 즉시 사용 불가능해져요.
- Master가 RegionServer가 실패했음을 감지할 거예요.
- region 할당이 유효하지 않은 것으로 간주되고 시작 시퀀스처럼 다시 할당될 거예요.
- 진행 중인 질의는 재시도되며 손실되지 않아요.
- 다음 시간 내에 새 RegionServer로 연산이 전환돼요:
ZooKeeper session timeout + split time + assignment/replay time
Region 부하 분산 (Region Load Balancing)
Region은 LoadBalancer에 의해 주기적으로 이동될 수 있어요.
Region 상태 전환 (Region State Transition)
HBase는 각 region에 대한 상태를 유지하고 그 상태를 hbase:meta에 영속화해요. hbase:meta region 자체의 상태는 ZooKeeper에 영속화돼요. Master 웹 UI에서 전환 중인 region의 상태를 볼 수 있어요. 가능한 region 상태 목록은 다음과 같아요.
가능한 Region 상태:
- OFFLINE: region이 오프라인이고 열리지 않는 상태
- OPENING: region을 여는 과정 중인 상태
- OPEN: region이 열려 있고 RegionServer가 master에 알린 상태
- FAILED_OPEN: RegionServer가 region을 여는 데 실패한 상태
- CLOSING: region을 닫는 과정 중인 상태
- CLOSED: RegionServer가 region을 닫고 master에 알린 상태
- FAILED_CLOSE: RegionServer가 region을 닫는 데 실패한 상태
- SPLITTING: RegionServer가 region이 분할 중임을 master에 알린 상태
- SPLIT: RegionServer가 region 분할이 끝났음을 master에 알린 상태
- SPLITTING_NEW: 이 region은 진행 중인 분할에 의해 생성 중인 상태
- MERGING: RegionServer가 이 region이 다른 region과 병합 중임을 master에 알린 상태
- MERGED: RegionServer가 이 region이 병합되었음을 master에 알린 상태
- MERGING_NEW: 이 region은 두 region의 병합으로 생성 중인 상태
그래프 범례:
- 갈색(Brown): 오프라인 상태. 일시적(닫힌 후 열리기 전), 종단(비활성화된 테이블의 region), 또는 초기(새로 생성된 테이블의 region)일 수 있는 특수 상태
- 옅은 녹색(Palegreen): region이 요청을 서빙할 수 있는 온라인 상태
- 연한 파란색(Lightblue): 일시적 상태
- 빨간색(Red): 운영자의 주의가 필요한 실패 상태
- 금색(Gold): 분할/병합된 region의 종단 상태
- 회색(Grey): 분할/병합을 통해 생성된 region의 초기 상태
전환 상태 설명:
- master가 region을 OFFLINE에서 OPENING 상태로 이동시키고 region을 RegionServer에 할당하려 시도해요. RegionServer는 open region 요청을 받았을 수도 있고 아닐 수도 있어요. master는 RPC가 성공하거나 재시도가 소진될 때까지 open region 요청을 RegionServer로 보내는 것을 재시도해요. RegionServer가 open region 요청을 받은 후, RegionServer는 region 열기를 시작해요.
- master의 재시도가 소진되면, master는 RegionServer가 region을 열기 시작하더라도 region을 CLOSING 상태로 이동시켜 닫으려 시도함으로써 region이 열리는 것을 막아요.
- RegionServer가 region을 연 후, master가 region을 OPEN 상태로 이동시켜 RegionServer에 알릴 때까지 master에 알리려 계속 시도해요. 이제 region이 열려 있어요.
- RegionServer가 region을 열지 못하면 master에 알려요. master는 region을 CLOSED 상태로 이동시키고 다른 RegionServer에서 region을 열려 시도해요.
- master가 일정 수의 region 중 어떤 region에서도 region을 열지 못하면 region을 FAILED_OPEN 상태로 이동시키고, 운영자가 HBase shell에서 개입하거나 서버가 죽을 때까지 더 이상 조치를 취하지 않아요.
- master가 region을 OPEN에서 CLOSING 상태로 이동시켜요. region을 보유한 RegionServer는 close region 요청을 받았을 수도 있고 아닐 수도 있어요. master는 RPC가 성공하거나 재시도가 소진될 때까지 서버로 close 요청을 보내는 것을 재시도해요.
- RegionServer가 온라인 상태가 아니거나 NotServingRegionException을 던지면, master는 region을 OFFLINE 상태로 이동시키고 다른 RegionServer에 다시 할당해요.
- RegionServer가 온라인이지만 master의 재시도가 소진된 후에도 도달할 수 없으면, master는 region을 FAILED_CLOSE 상태로 이동시키고 운영자가 HBase shell에서 개입하거나 서버가 죽을 때까지 더 이상 조치를 취하지 않아요.
- RegionServer가 close region 요청을 받으면 region을 닫고 master에 알려요. master는 region을 CLOSED 상태로 이동시키고 다른 RegionServer에 다시 할당해요.
- region을 할당하기 전에 master는 region이 CLOSED 상태이면 자동으로 OFFLINE 상태로 이동시켜요.
- RegionServer가 region을 분할하려 할 때 master에 알려요. master는 분할될 region을 OPEN에서 SPLITTING 상태로 이동시키고 생성될 두 개의 새 region을 RegionServer에 추가해요. 이 두 region은 초기에 SPLITTING_NEW 상태예요.
- master에 알린 후 RegionServer는 region 분할을 시작해요. 되돌릴 수 없는 지점을 지나면 RegionServer가 다시 master에 알려 master가 hbase:meta 테이블을 갱신할 수 있게 해요. 하지만 master는 서버로부터 분할이 끝났다는 알림을 받을 때까지 region 상태를 갱신하지 않아요. 분할이 성공하면 분할 중인 region은 SPLITTING에서 SPLIT 상태로, 두 개의 새 region은 SPLITTING_NEW에서 OPEN 상태로 이동해요.
- 분할이 실패하면 분할 중인 region은 SPLITTING에서 OPEN 상태로 돌아가고, 생성된 두 개의 새 region은 SPLITTING_NEW에서 OFFLINE 상태로 이동해요.
- RegionServer가 두 region을 병합하려 할 때 먼저 master에 알려요. master는 병합될 두 region을 OPEN에서 MERGING 상태로 이동시키고, 병합된 region의 내용을 담을 새 region을 RegionServer에 추가해요. 새 region은 초기에 MERGING_NEW 상태예요.
- master에 알린 후 RegionServer는 두 region 병합을 시작해요. 되돌릴 수 없는 지점을 지나면 RegionServer가 다시 master에 알려 master가 META를 갱신할 수 있게 해요. 하지만 master는 RegionServer로부터 병합이 완료되었다는 알림을 받을 때까지 region 상태를 갱신하지 않아요. 병합이 성공하면 두 병합 중인 region은 MERGING에서 MERGED 상태로, 새 region은 MERGING_NEW에서 OPEN 상태로 이동해요.
- 병합이 실패하면 두 병합 중인 region은 MERGING에서 OPEN 상태로 돌아가고, 병합된 region의 내용을 담기 위해 생성된 새 region은 MERGING_NEW에서 OFFLINE 상태로 이동해요.
- FAILED_OPEN 또는 FAILED_CLOSE 상태의 region에 대해 master는 운영자가 HBase Shell을 통해 다시 할당할 때 다시 닫으려 시도해요.
Region-RegionServer 로컬리티 (Region-RegionServer Locality)
시간이 지나면서 Region-RegionServer 로컬리티는 HDFS 블록 복제를 통해 달성돼요. HDFS 클라이언트는 복제본 위치를 선택할 때 기본적으로 다음을 수행해요.
- 첫 번째 복제본은 로컬 노드에 기록
- 두 번째 복제본은 다른 랙의 임의 노드에 기록
- 세 번째 복제본은 두 번째와 같은 랙에 있지만 무작위로 선택된 다른 노드에 기록
- 이후 복제본은 클러스터의 임의 노드에 기록. 이 페이지 참고: HDFS Architecture
따라서 HBase는 플러시나 압축 후 region에 대한 로컬리티를 결국 달성해요. RegionServer 장애 조치 상황에서는 RegionServer에 비로컬 StoreFile(복제본 중 어떤 것도 로컬이 아니기 때문에)이 있는 region이 할당될 수 있지만, region에 새 데이터가 기록되거나 테이블이 압축되어 StoreFile이 다시 쓰이면 그 RegionServer에 "로컬"이 돼요.
자세한 내용은 HDFS Architecture 페이지의 Replica Placement: The First Baby Steps와 Lars George의 HBase와 HDFS 로컬리티 블로그를 참고해 주세요.
Region 분할 (Region Splits)
Region은 구성된 임계값에 도달하면 분할돼요. 아래에서 이 주제를 간단히 다뤄요. 더 긴 설명은 Enis Soztutar의 Apache HBase Region Splitting and Merging을 참고해 주세요.
분할은 RegionServer에서 자체적으로 실행돼요. 즉, Master는 참여하지 않아요. RegionServer가 region을 분할하고, 분할된 region을 오프라인으로 만들고, 딸 region(daughters)을 hbase:meta에 추가하며, 부모의 호스팅 RegionServer에서 딸을 열고, 분할을 Master에 보고해요. 분할을 수동으로 관리하는 방법(그리고 왜 그렇게 하는지)은 Managed Splitting을 참고해 주세요.
커스텀 분할 정책 (Custom Split Policies)
커스텀 RegionSplitPolicy(HBase 0.94+)를 사용해 기본 분할 정책을 재정의할 수 있어요. 일반적으로 커스텀 분할 정책은 HBase의 기본 분할 정책인 IncreasingToUpperBoundRegionSplitPolicy를 확장해야 해요.
정책은 HBase 구성으로 전역적으로 설정하거나 테이블별로 설정할 수 있어요.
hbase-site.xml에서 분할 정책 전역 구성
<property>
<name>hbase.regionserver.region.split.policy</name>
<value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value>
</property>
Java API로 테이블에 분할 정책 구성
HTableDescriptor tableDesc = new HTableDescriptor("test");
tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, ConstantSizeRegionSplitPolicy.class.getName());
tableDesc.addFamily(new HColumnDescriptor(Bytes.toBytes("cf1")));
admin.createTable(tableDesc);
HBase Shell로 테이블에 분할 정책 구성
hbase> create 'test', {METADATA => {'SPLIT_POLICY' => 'org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy'}},{NAME => 'cf1'}
정책은 사용된 HBaseConfiguration으로 전역적으로 설정하거나 제목당(per table) 설정할 수 있어요.
HTableDescriptor myHtd = ...;
myHtd.setValue(HTableDescriptor.SPLIT_POLICY, MyCustomSplitPolicy.class.getName());
DisabledRegionSplitPolicy 정책은 수동 region 분할을 차단해요.
수동 Region 분할 (Manual Region Splitting)
테이블 생성 시(사전 분할, pre-splitting) 또는 나중에 관리 작업으로 테이블을 수동으로 분할할 수 있어요. 다음 이유 중 하나 이상으로 region을 분할하기로 선택할 수 있어요. 다른 타당한 이유도 있을 수 있지만, 테이블을 수동으로 분할해야 할 필요는 스키마 설계에 문제가 있음을 가리킬 수도 있어요.
테이블을 수동으로 분할해야 하는 이유:
- 데이터가 시계열(timeseries)이나 테이블 끝에 새 데이터를 정렬하는 비슷한 알고리즘으로 정렬되어 있어요. 이는 마지막 region을 보유한 Region Server가 항상 부하를 받고, 다른 Region Server는 유휴하거나 대부분 유휴 상태라는 뜻이에요. 또한 Monotonically Increasing Row Keys/Timeseries Data를 참고해 주세요.
- 테이블의 한 region에 예상치 못한 핫스팟이 생겼어요. 예를 들어 웹 검색을 추적하는 애플리케이션이 그 유명인에 대한 뉴스 사건 때 그 유명인에 대한 많은 검색으로 넘쳐날 수 있어요. 이 특정 시나리오에 대한 더 많은 논의는 perf.one.region을 참고해 주세요.
- 클러스터의 RegionServer 수가 크게 늘어난 후, 부하를 빠르게 분산하기 위해.
- region에 걸쳐 비정상적이고 고르지 않은 부하를 일으킬 가능성이 있는 벌크 로드 전에.
완전 수동으로 분할을 관리하는 것의 위험과 잠재적 이점에 대한 논의는 Managed Splitting을 참고해 주세요.
DisabledRegionSplitPolicy 정책은 수동 region 분할을 차단해요.
분할 지점 결정 (Determining Split Points)
테이블을 수동으로 분할하는 목표는 좋은 rowkey 설계만으로는 도달할 수 없는 상황에서 클러스터 전체의 부하 분산 기회를 개선하는 것이에요. 이를 염두에 두고, region을 분할하는 방법은 데이터의 특성에 크게 의존해요. 이미 테이블을 분할하는 최선의 방법을 알고 있을 수도 있어요. 아니면 테이블을 분할하는 방법은 키가 어떤지에 따라 달라져요.
영숫자 Rowkeys (Alphanumeric Rowkeys) rowkey가 문자나 숫자로 시작하면 테이블을 문자 또는 숫자 경계에서 분할할 수 있어요. 예를 들어 다음 명령은 각 모음에서 분할되는 region을 가진 테이블을 만들어, 첫 번째 region이 A-D, 두 번째 region이 E-H, 세 번째 region이 I-N, 네 번째 region이 O-V, 다섯 번째 region이 U-Z를 갖게 해요.
커스텀 알고리즘 사용 (Using a Custom Algorithm) HBase와 함께 제공되는 RegionSplitter 도구는 SplitAlgorithm을 사용해 분할 지점을 결정해 줘요. 매개변수로 알고리즘, 원하는 region 수, 컬럼 패밀리를 넘겨줘요. 세 가지 분할 알고리즘이 포함돼요. 첫 번째는 HexStringSplit 알고리즘으로, row key가 16진수 문자열이라고 가정해요. 두 번째는 DecimalStringSplit 알고리즘으로, row key가 00000000부터 99999999 범위의 십진수 문자열이라고 가정해요. 세 번째 UniformSplit은 row key가 임의의 바이트 배열이라고 가정해요. 제공된 것들을 모델로 사용해 자신만의 SplitAlgorithm을 개발해야 할 수도 있어요.
온라인 Region 병합 (Online Region Merges)
온라인 region 병합 이벤트에는 Master와 RegionServer가 모두 참여해요. 클라이언트가 병합 RPC를 master로 보내면, master는 region들을 함께 더 부하가 높은 region이 있던 RegionServer로 이동시켜요. 마지막으로 master가 이 RegionServer에 병합 요청을 보내고, RegionServer가 병합을 실행해요. region 분할 과정과 유사하게 region 병합은 RegionServer의 로컬 트랜잭션으로 실행돼요. 병합 중인 region을 오프라인으로 만들고 파일시스템에서 두 region을 병합하며, hbase:meta에서 병합 중인 region을 원자적으로 삭제하고 병합된 region을 hbase:meta에 추가하며, RegionServer에서 병합된 region을 열고 병합을 Master에 보고해요.
HBase shell에서 region 병합의 예시
$ hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME'
$ hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', true
비동기 연산이며, 병합 완료를 기다리지 않고 호출이 즉시 반환돼요. 선택적 세 번째 매개변수로 true를 넘기면 병합을 강제해요. 일반적으로 인접한 region만 병합할 수 있어요. force 매개변수는 이 동작을 재정의하며 전문가 전용이에요.
Store
Store는 MemStore와 0개 이상의 StoreFile(HFile)을 호스팅해요. Store는 주어진 region의 테이블에 대한 컬럼 패밀리와 대응돼요.
MemStore
MemStore는 Store에 대한 인메모리 수정을 보유해요. 수정은 Cell/KeyValue예요. 플러시가 요청되면 현재 MemStore가 스냅샷으로 이동되고 비워져요. HBase는 플러셔가 플러시 성공을 보고할 때까지 새 MemStore와 백업 스냅샷에서 편집을 계속 서빙해요. 이 시점에 스냅샷은 버려져요. 플러시가 발생할 때 같은 region에 속한 MemStore는 모두 플러시된다는 점에 주의해 주세요.
MemStore 플러시 (MemStore Flush)
MemStore 플러시는 아래 나열된 조건 중 하나에서 트리거될 수 있어요. 최소 플러시 단위는 개별 MemStore 수준이 아니라 region 단위예요.
- MemStore가 hbase.hregion.memstore.flush.size가 지정한 크기에 도달하면, 그 region에 속한 모든 MemStore가 디스크로 플러시돼요.
- 전체 MemStore 사용량이 hbase.regionserver.global.memstore.upperLimit가 지정한 값에 도달하면, RegionServer의 전체 MemStore 사용량을 줄이기 위해 여러 region의 MemStore가 디스크로 플러시돼요. 플러시 순서는 region의 MemStore 사용량 내림차순이에요. 전체 MemStore 사용량이 hbase.regionserver.global.memstore.lowerLimit에 도달하거나 약간 그 아래로 내려갈 때까지 region의 MemStore가 플러시돼요.
- 주어진 region server의 WAL에서 WAL 로그 항목 수가 hbase.regionserver.max.logs가 지정한 값에 도달하면, WAL의 로그 수를 줄이기 위해 여러 region의 MemStore가 디스크로 플러시돼요. 플러시 순서는 시간 기반이에요. WAL 수가 hbase.regionserver.max.logs 아래로 내려갈 때까지 가장 오래된 MemStore를 가진 region부터 플러시돼요.
스캔 (Scans)
- 클라이언트가 테이블에 대해 스캔을 발행하면, HBase는 스캔 요청을 서빙하기 위해 region당 하나씩 RegionScanner 객체를 생성해요.
- RegionScanner 객체는 컬럼 패밀리당 하나씩 StoreScanner 객체 목록을 포함해요.
- 각 StoreScanner 객체는 해당 컬럼 패밀리의 각 StoreFile 및 HFile에 대응하는 StoreFileScanner 객체 목록과, MemStore용 KeyValueScanner 객체 목록을 더 포함해요.
- 두 목록은 하나로 병합되며, MemStore의 스캔 객체를 목록 끝에 둔 채 오름차순으로 정렬돼요.
- StoreFileScanner 객체가 구성될 때 MultiVersionConcurrencyControl 읽기 지점(현재 memstoreTS)과 연결되어, 읽기 지점을 넘는 새 갱신을 걸러내요.
StoreFile (HFile)
StoreFile은 데이터가 사는 곳이에요.
HFile 형식
HFile 파일 형식은 BigTable [2006] 논문에 설명된 SSTable 파일과 Hadoop의 TFile(단위 테스트 스위트와 압축 하네스는 TFile에서 직접 가져옴)을 기반으로 해요. Schubert Zhang의 HFile 블로그 글인 HFile: A Block-Indexed File Format to Store Sorted Key-Value Pairs는 HBase의 HFile에 대한 철저한 소개예요. Matteo Bertozzi도 유용한 설명인 HBase I/O: HFile을 제공했어요.
자세한 내용은 HFile 소스 코드를 참고해 주세요. 0.92에 포함된 HFile v2 형식에 대한 정보는 inline blocks(v2)가 있는 HBase file format을 참고해 주세요.
HFile Tool
HFile 내용의 텍스트화된 버전을 보려면 hbase hfile 도구를 사용할 수 있어요. 사용법을 보려면 다음을 입력해 주세요.
$ ${HBASE_HOME}/bin/hbase hfile
예를 들어 hdfs://10.81.47.41:9000/hbase/default/TEST/1418428042/DSMP/4759508618286845475 파일의 내용을 보려면 다음을 입력해 주세요.
$ ${HBASE_HOME}/bin/hbase hfile -v -f hdfs://10.81.47.41:9000/hbase/default/TEST/1418428042/DSMP/4759508618286845475
-v 옵션을 빼면 HFile에 대한 요약만 봐요. hfile 도구로 할 수 있는 다른 것들은 usage를 참고해 주세요.
이 도구의 출력에서 'Mid-key'/'firstKey'/'lastKey' 같은 곳에서 특정 키에 대해 'seqid=0'을 볼 수 있어요. 이들은 'KeyOnlyKeyValue' 유형 인스턴스로, seqid가 무관하며 이 Key-Value 인스턴스의 키만 필요하다는 뜻이에요.
HDFS의 StoreFile 디렉터리 구조
StoreFiles가 디렉터리 구조와 관련해 HDFS에서 어떻게 보이는지에 대한 자세한 내용은 Browsing HDFS for HBase Objects를 참고해 주세요.
블록 (Blocks)
StoreFile은 블록으로 구성돼요. 블록 크기는 컬럼 패밀리별로 구성돼요.
압축은 StoreFile 내 블록 수준에서 발생해요. 압축에 대한 자세한 내용은 HBase의 압축 및 데이터 블록 인코딩(Compression and Data Block Encoding In HBase)을 참고해 주세요.
블록에 대한 자세한 내용은 HFileBlock 소스 코드를 참고해 주세요.
KeyValue
KeyValue 클래스는 HBase의 데이터 저장의 핵심이에요. KeyValue는 바이트 배열을 감싸고, 전달된 배열에서 콘텐츠를 KeyValue로 해석하기 시작할 위치를 지정하는 오프셋과 길이를 취해요.
바이트 배열 안의 KeyValue 형식은 다음과 같아요.
- keylength
- valuelength
- key
- value
Key는 더 분해되는데:
- rowlength
- row (즉, rowkey)
- columnfamilylength
- columnfamily
- columnqualifier
- timestamp
- keytype (예: Put, Delete, DeleteColumn, DeleteFamily)
KeyValue 인스턴스는 블록을 가로질러 분할되지 않아요. 예를 들어 8MB KeyValue가 있으면, 블록 크기가 64kb여도 이 KeyValue가 일관된 블록으로 읽혀요. 자세한 내용은 KeyValue 소스 코드를 참고해 주세요.
예시 (Example)
위 요점을 강조하기 위해, 같은 행의 두 다른 컬럼에 대한 두 번의 Put에서 무슨 일이 일어나는지 살펴보세요.
- Put #1: rowkey=row1, cf:attr1=value1
- Put #2: rowkey=row1, cf:attr2=value2
이들이 같은 행에 대한 것이더라도, 컬럼마다 KeyValue가 생성돼요.
Put #1의 Key 부분:
- rowlength -----------→ 4
- row -----------------→ row1
- columnfamilylength --→ 2
- columnfamily --------→ cf
- columnqualifier -----→ attr1
- timestamp -----------→ server time of Put
- keytype -------------→ Put
Put #2의 Key 부분:
- rowlength -----------→ 4
- row -----------------→ row1
- columnfamilylength --→ 2
- columnfamily --------→ cf
- columnqualifier -----→ attr2
- timestamp -----------→ server time of Put
- keytype -------------→ Put
rowkey, ColumnFamily, column(일명 columnqualifier)이 KeyValue 인스턴스 안에 내장되어 있다는 것을 이해하는 것이 중요해요. 이 식별자가 길수록 KeyValue가 커져요.
압축 (Compaction)
모호한 용어:
- StoreFile은 HFile의 퍼사드(facade)예요. 압축 측면에서는 과거에 StoreFile 사용이 우세했어요.
- Store는 ColumnFamily와 같은 것이에요. StoreFile은 Store 즉 ColumnFamily와 관련돼요.
- StoreFile 대 HFile, Store 대 ColumnFamily에 대해 더 읽고 싶다면 HBASE-11316을 참고해 주세요.
MemStore가 주어진 크기(hbase.hregion.memstore.flush.size)에 도달하면 내용을 StoreFile로 플러시해요. Store의 StoreFile 수는 시간이 지나면서 증가해요. 압축은 StoreFile을 병합해 Store의 StoreFile 수를 줄임으로써 읽기 연산 성능을 높이는 연산이에요. 압축은 리소스 집약적일 수 있으며, 많은 요인에 따라 성능을 돕거나 해칠 수 있어요.
압축은 minor와 major의 두 범주로 나뉘어요. Minor와 major 압축은 다음 방식으로 달라요.
Minor 압축은 보통 적은 수의 작고 인접한 StoreFile을 선택해 단일 StoreFile로 다시 써요. Minor 압축은 잠재적 부작용 때문에 삭제나 만료된 버전을 버리지(걸러내지) 않아요. 삭제와 버전이 압축과 관련해 어떻게 처리되는지에 대한 정보는 Compaction and Deletions와 Compaction and Versions를 참고해 주세요. Minor 압축의 최종 결과는 주어진 Store에 대해 더 적고 더 큰 StoreFile이에요.
Major 압축의 최종 결과는 Store당 단일 StoreFile이에요. Major 압축은 또한 delete 마커와 최대 버전도 처리해요.
압축과 삭제 (Compaction and Deletions)
HBase에서 명시적 삭제가 발생하면 데이터가 실제로 삭제되지 않아요. 대신 톰스톤(tombstone) 마커가 기록돼요. 톰스톤 마커는 질의로 데이터가 반환되는 것을 막아요. Major 압축 중에 데이터가 실제로 삭제되고 톰스톤 마커가 StoreFile에서 제거돼요. 삭제가 만료된 TTL 때문에 발생하면 톰스톤이 생성되지 않아요. 대신 만료된 데이터가 걸러지고 압축된 StoreFile에 다시 기록되지 않아요.
압축과 버전 (Compaction and Versions)
Column Family를 만들 때 ColumnFamilyDescriptorBuilder.setMaxVersions(int versions)를 지정해 유지할 최대 버전 수를 지정할 수 있어요. 기본값은 1이에요. 지정한 최대값보다 많은 버전이 존재하면 초과 버전이 걸러지고 압축된 StoreFile에 다시 기록되지 않아요.
어떤 상황에서는 새 버전이 명시적으로 삭제되면 이전 버전이 의도치 않게 부활할 수 있어요. 더 깊이 있는 설명은 Major compactions change query results를 참고해 주세요. 이 상황은 압축이 끝나기 전에만 가능해요.
이론적으로 major 압축은 성능을 개선해요. 하지만 부하가 높은 시스템에서는 major 압축이 부적절한 리소스를 요구해 성능에 악영향을 줄 수 있어요. 기본 구성에서 major 압축은 7일 기간에 한 번 실행되도록 자동으로 예약돼요. 운영 중인 시스템에는 때로 부적절해요. major 압축을 수동으로 관리할 수 있어요. Managed Compactions를 참고해 주세요.
압축은 region 병합을 수행하지 않아요. region 병합에 대한 자세한 내용은 Merge를 참고해 주세요.
압축 스위치 (Compaction Switch)
region server에서 압축을 켜고 끌 수 있어요. 압축을 끄면 현재 진행 중인 압축도 중단돼요. hbase shell의 "compaction_switch" 명령으로 동적으로 수행할 수 있어요. 커맨드라인에서 수행하면 서버 재시작 시 이 설정이 손실돼요. 변경을 region server에 영속화하려면 hbase-site.xml에서 hbase.regionserver.compaction.enabled 구성을 수정하고 HBase를 재시작해 주세요.
압축 정책 - HBase 0.96.x 이상 (Compaction Policy - HBase 0.96.x and newer)
큰 StoreFile을 압축하거나 한 번에 너무 많은 StoreFile을 압축하면 클러스터가 성능 문제 없이 처리할 수 있는 것보다 더 많은 IO 부하가 발생할 수 있어요. HBase가 압축에 포함할 StoreFile을 선택하는 방법(그리고 압축이 minor인지 major인지 여부)을 압축 정책(compaction policy)이라고 해요.
HBase 0.96.x 이전에는 압축 정책이 하나뿐이었어요. 그 원래 압축 정책은 여전히 RatioBasedCompactionPolicy로 사용할 수 있어요. ExploringCompactionPolicy라고 불리는 새 기본 압축 정책은 이후 HBase 0.94와 HBase 0.95로 백포트되었으며, HBase 0.96 이상에서 기본값이에요. HBASE-7842에서 구현됐어요. 간단히 말하면 ExploringCompactionPolicy는 가장 적은 작업으로 압축할 최상의 StoreFile 집합을 선택하려 시도하는 반면, RatioBasedCompactionPolicy는 기준을 충족하는 첫 번째 집합을 선택해요.
사용하는 압축 정책과 관계없이 파일 선택은 여러 구성 가능한 매개변수로 제어되며 다단계 접근으로 발생해요. 이 매개변수들은 맥락에서 설명되고, 그다음 설명, 기본값, 변경 시 영향력을 보여주는 표로 제시될 거예요.
갇힘 (Being Stuck)
MemStore가 너무 커지면 내용을 StoreFile로 플러시해야 해요. 하지만 Store는 StoreFile 수의 상한인 hbase.hstore.blockingStoreFiles로 구성되며, 초과하면 하나 이상의 압축으로 StoreFile 수가 줄어들 때까지 MemStore 플러시가 기다려야 해요. MemStore가 너무 크고 StoreFile 수가 너무 많으면 알고리즘이 "갇혀(stuck)" 있다고 해요. 기본적으로 hbase.hstore.blockingWaitTime 밀리초까지 압축을 기다려요. 이 기간이 만료되면 hbase.hstore.blockingStoreFiles 수를 초과하더라도 어쨌든 플러시해요.
hbase.hstore.blockingStoreFiles 수를 올리면 플러시가 발생할 수 있지만, StoreFile이 많은 Store는 읽기 지연 시간이 더 높을 가능성이 커요. 압축이 따라잡지 못하는 이유를 알아보세요. 이 상황을 가져온 것이 쓰기 파동(spurt)인지, 아니면 정기적으로 발생하며 클러스터가 쓰기 볼륨에 대해 과소 프로비저닝되었는지?
ExploringCompactionPolicy 알고리즘
ExploringCompactionPolicy 알고리즘은 압축이 가장 큰 이점이 될 집합을 선택하기 전에 인접한 StoreFile의 각 가능한 집합을 고려해요.
ExploringCompactionPolicy가 특히 잘 작동하는 한 상황은 데이터를 벌크 로드하고, 벌크 로드가 벌크 로드된 데이터보다 오래된 데이터를 보유한 StoreFile보다 큰 StoreFile을 만들 때예요. 이는 HBase가 "속아서" 압축이 필요할 때마다 major compaction을 선택하도록 만들고 많은 추가 오버헤드를 일으킬 수 있어요. ExploringCompactionPolicy를 사용하면 minor 압축이 더 효율적이므로 major 압축이 훨씬 덜 자주 발생해요.
일반적으로 ExploringCompactionPolicy는 대부분의 상황에서 올바른 선택이며, 따라서 기본 압축 정책이에요. ExploringCompactionPolicy를 Experimental: Stripe Compactions와 함께 사용할 수도 있어요.
이 정책의 로직은 hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/ExploringCompactionPolicy.java에서 검토할 수 있어요. 다음은 ExploringCompactionPolicy 로직의 워크스루예요.
-
Store의 모든 기존 StoreFile 목록을 만들어요. 알고리즘의 나머지가 이 목록을 필터링해 압축에 선택될 HFile 부분 집합을 도출해요.
-
이것이 사용자 요청 압축이면, 일반적으로 선택했을 것과 관계없이 요청된 압축 유형을 수행하려 시도해요. 사용자가 major 압축을 요청해도 major 압축을 수행하지 못할 수 있다는 점에 주의해 주세요. 이는 Column Family의 모든 StoreFile이 압축 가능하지 않거나 Column Family에 Store가 너무 많기 때문일 수 있어요.
-
일부 StoreFile은 자동으로 고려에서 제외돼요. 여기에는 다음이 포함돼요: hbase.hstore.compaction.max.size보다 큰 StoreFile 압축을 명시적으로 제외한 벌크 로드 작업으로 생성된 StoreFile. 벌크 로드 결과 StoreFile을 압축에서 제외하기로 결정할 수 있어요. 이를 위해 벌크 로드 연산 중 hbase.mapreduce.hfileoutputformat.compaction.exclude 매개변수를 지정해 주세요.
-
1단계의 목록을 순회하며 함께 압축할 모든 잠재적 StoreFile 집합 목록을 만들어요. 잠재적 집합은 목록에서 hbase.hstore.compaction.min개의 연속 StoreFile의 그룹이에요. 각 집합에 대해 일부 sanity 검사를 수행하고 이것이 할 수 있는 최상의 압축인지 판단해요.
이 집합의 StoreFile 수가(크기가 아니라) hbase.hstore.compaction.min보다 적거나 hbase.hstore.compaction.max보다 많으면 고려에서 제외해요.
이 StoreFile 집합의 크기를 지금까지 목록에서 찾은 가장 작은 가능한 압축의 크기와 비교해요. 이 StoreFile 집합의 크기가 할 수 있는 가장 작은 압축을 나타내면, 알고리즘이 "갇혀" 있고 그렇지 않으면 어떤 StoreFile도 선택되지 않을 때 폴백으로 사용하도록 저장해요. Being Stuck 참고.
이 StoreFile 집합의 각 StoreFile에 대해 크기 기반 sanity 검사를 수행해요.
이 StoreFile의 크기가 hbase.hstore.compaction.max.size보다 크면 고려에서 제외해요. 크기가 hbase.hstore.compaction.min.size보다 크거나 같으면 파일 기반 비율로 sanity 검사하여 너무 커서 고려할 수 없는지 확인해요.
sanity 검사는 다음의 경우 성공해요:
이 집합에 StoreFile이 하나뿐이거나, 각 StoreFile에 대해 그 크기에 hbase.hstore.compaction.ratio(또는 비수요 시간이 구성되고 비수요 시간이면 hbase.hstore.compaction.ratio.offpeak)를 곱한 값이 집합의 다른 HFile 크기 합보다 작은 경우.
-
이 StoreFile 집합이 여전히 고려 중이면 이전에 선택한 최상의 압축과 비교해요. 더 좋으면 이전에 선택한 최상의 압축을 이것으로 바꿔요.
-
전체 잠재적 압축 목록이 처리되면 찾은 최상의 압축을 수행해요. 압축에 선택된 StoreFile이 없지만 StoreFile이 여러 개 있으면 알고리즘이 갇혔다고 가정하고(Being Stuck 참고), 그렇다면 3단계에서 찾은 가장 작은 압축을 수행해요.
RatioBasedCompactionPolicy 알고리즘
RatioBasedCompactionPolicy는 HBase 0.96 이전의 유일한 압축 정책이었지만, ExploringCompactionPolicy가 이제 HBase 0.94와 0.95로 백포트되었어요. ExploringCompactionPolicy 대신 RatioBasedCompactionPolicy를 사용하려면 hbase-site.xml 파일에서 hbase.hstore.defaultengine.compactionpolicy.class를 RatioBasedCompactionPolicy로 설정해 주세요. ExploringCompactionPolicy로 다시 전환하려면 hbase-site.xml에서 설정을 제거해 주세요.
다음 섹션은 RatioBasedCompactionPolicy에서 압축할 StoreFile을 선택하는 데 사용되는 알고리즘을 안내해요.
- 첫 번째 단계는 압축 후보 목록을 만드는 것이에요. 이미 압축 큐에 없는 모든 StoreFile과, 현재 압축 중인 가장 최신 파일보다 최신인 모든 StoreFile의 목록이 만들어져요. 이 StoreFile 목록은 시퀀스 ID로 정렬돼요. 시퀀스 ID는 Put이 로그 선행 쓰기(WAL)에 추가될 때 생성되고 HFile의 메타데이터에 저장돼요.
- 알고리즘이 갇혔는지 확인하고(Being Stuck 참고), 맞다면 major 압축이 강제돼요. 이는 ExploringCompactionPolicy 알고리즘이 RatioBasedCompactionPolicy보다 종종 더 나은 선택인 핵심 영역이에요.
- 압축이 사용자 요청이었다면 요청된 압축 유형을 수행하려 시도해요. 모든 HFile이 압축 가능하지 않거나 StoreFile이 너무 많으면(hbase.hstore.compaction.max보다 크면) major 압축이 불가능할 수 있다는 점에 주의해 주세요.
- 일부 StoreFile은 자동으로 고려에서 제외돼요. 여기에는 다음이 포함돼요: hbase.hstore.compaction.max.size보다 큰 StoreFile 압축을 명시적으로 제외한 벌크 로드 작업으로 생성된 StoreFile. 벌크 로드 결과 StoreFile을 압축에서 제외하기로 결정할 수 있어요. 이를 위해 벌크 로드 연산 중 hbase.mapreduce.hfileoutputformat.compaction.exclude 매개변수를 지정해 주세요.
- major 압축에서 허용되는 최대 StoreFile 수는 hbase.hstore.compaction.max 매개변수로 제어돼요. 목록에 이 수보다 많은 StoreFile이 있으면, 그렇지 않았다면 major 압축이 실제로 수행되었을 때도 minor 압축이 수행돼요. 하지만 압축할 hbase.hstore.compaction.max보다 많은 StoreFile이 있어도 사용자 요청 major 압축은 여전히 발생해요.
- 목록에 압축할 hbase.hstore.compaction.min보다 적은 StoreFile이 있으면 minor 압축이 중단돼요. 단일 HFile에 대해서도 major 압축을 수행할 수 있다는 점에 주의해 주세요. 그 기능은 삭제와 만료된 버전을 제거하고 StoreFile의 로컬리티를 리셋하는 것이에요.
- hbase.hstore.compaction.ratio 매개변수의 값에 주어진 파일보다 작은 StoreFile의 합을 곱해, minor 압축 중에 그 StoreFile이 압축에 선택되는지 판단해요. 예를 들어 hbase.hstore.compaction.ratio가 1.2이고 FileX가 5MB, FileY가 2MB, FileZ가 3MB라면:
5 <= 1.2 x (2 + 3) or 5 <= 6
이 시나리오에서 FileX는 minor 압축 대상이에요. FileX가 7MB라면 minor 압축 대상이 아니에요. 이 비율은 더 작은 StoreFile을 선호해요. hbase.offpeak.start.hour와 hbase.offpeak.end.hour도 구성하면 비수요 시간에 사용할 다른 비율을 hbase.hstore.compaction.ratio.offpeak 매개변수로 구성할 수 있어요.
- 마지막 major 압축이 너무 오래 전이고 압축할 StoreFile이 하나 이상이면, 그 외에는 minor가었을지라도 major 압축이 실행돼요. 기본적으로 major 압축 사이의 최대 시간은 7일에서 ±4.8시간으로, 해당 매개변수 내에서 무작위로 결정돼요. HBase 0.96 이전에는 major 압축 기간이 24시간이었어요. 아래 표의 hbase.hregion.majorcompaction을 참고해 시간 기반 major 압축을 튜닝하거나 비활성화해 주세요.
압축 알고리즘에서 사용하는 매개변수 (Parameters Used by Compaction Algorithm)
이 표는 압축의 주요 구성 매개변수를 담고 있어요. 이 목록은 완전하지 않아요. 기본값에서 이 매개변수를 튜닝하려면 hbase-default.xml 파일을 편집해 주세요. 사용 가능한 모든 구성 매개변수의 전체 목록은 config.files를 참고해 주세요.
-
hbase.hstore.compaction.min 압축이 실행되기 전에 압축 대상이어야 하는 최소 StoreFile 수. hbase.hstore.compaction.min을 튜닝하는 목표는 압축해야 할 너무 많은 아주 작은 StoreFile이 생기지 않도록 하는 것이에요. 이 값을 2로 설정하면 Store에 StoreFile이 두 개 있을 때마다 minor 압축이 발생하며, 이것은 아마 적절하지 않아요. 이 값을 너무 높게 설정하면 다른 모든 값을 그에 맞춰 조정해야 해요. 대부분의 경우 기본값이 적절해요. 이전 HBase 버전에서 hbase.hstore.compaction.min 매개변수는 hbase.hstore.compactionThreshold라고 불렸어요. 기본값: 3
-
hbase.hstore.compaction.max 대상 StoreFile 수와 관계없이 단일 minor 압축에서 선택될 최대 StoreFile 수. 사실상 hbase.hstore.compaction.max 값은 단일 압축을 완료하는 데 걸리는 시간을 제어해요. 더 크게 설정하면 압축에 더 많은 StoreFile이 포함돼요. 대부분의 경우 기본값이 적절해요. 기본값: 10
-
hbase.hstore.compaction.min.size 이 크기보다 작은 StoreFile은 항상 minor 압축 대상이 돼요. 이 크기 이상의 StoreFile은 대상 여부를 결정하기 위해 hbase.hstore.compaction.ratio로 평가돼요. 이 한계는 이 값보다 작은 모든 StoreFile에 대한 "자동 포함" 한계를 나타내므로, 1-2 MB 범위의 많은 파일이 플러시되는 쓰기가 많은 환경에서는 값이 줄어들어야 할 수 있는데, 모든 StoreFile이 압축 대상이 될 것이고 결과 StoreFile이 여전히 최소 크기 미만이라 추가 압축이 필요할 수 있기 때문이에요. 이 매개변수를 낮추면 ratio 검사가 더 빨리 트리거돼요. 이것은 HBase의 이전 버전에서 보였던 일부 문제를 해결했지만, 대부분의 상황에서 이 매개변수를 변경하는 것은 더 이상 필요하지 않아요. 기본값: 128 MB
-
hbase.hstore.compaction.max.size 이 크기보다 큰 StoreFile은 압축에서 제외돼요. hbase.hstore.compaction.max.size를 높이는 효과는 더 적고 더 큰, 자주 압축되지 않는 StoreFile이 되는 것이에요. 압축이 큰 이점 없이 너무 자주 발생한다고 느끼면 이 값을 높여 볼 수 있어요. 기본값: Long.MAX_VALUE
-
hbase.hstore.compaction.ratio minor 압축의 경우 이 비율은 hbase.hstore.compaction.min.size보다 큰 주어진 StoreFile이 압축 대상인지 판단하는 데 사용돼요. 그 효과는 큰 StoreFile의 압축을 제한하는 것이에요. hbase.hstore.compaction.ratio 값은 부동소수점 십진수로 표현돼요.
큰 비율(예: 10)은 단일 거대한 StoreFile을 생성할 거예요. 반대로 .25 값은 BigTable 압축 알고리즘과 유사한 동작을 생성해 4개의 StoreFile을 만들 거예요. 1.0에서 1.4 사이의 중간 값이 권장돼요. 이 값을 튜닝할 때 쓰기 비용과 읽기 비용을 균형 있게 하는 것이에요. 값을 올리면(1.4 같은) 더 큰 StoreFile을 압축하므로 쓰기 비용이 더 커져요. 하지만 읽기 중에 HBase가 읽기를 완료하기 위해 더 적은 StoreFile을 통과해 찾아야 해요. Bloom Filters를 활용할 수 없다면 이 접근을 고려해 보세요. 또는 값을 1.0 같은 것으로 낮춰 쓰기의 백그라운드 비용을 줄이고 읽기 중에 접촉하는 StoreFile 수를 제한하는 데 사용할 수 있어요. 대부분의 경우 기본값이 적절해요. 기본값: 1.2F
-
hbase.hstore.compaction.ratio.offpeak 비수요 시간도 구성되어 있으면(아래 참조) 비수요 시간 압축 중에 사용되는 압축 비율. 부동소수점 십진수로 표현돼요. 이는 설정된 시간대에 더 공격적인(또는 hbase.hstore.compaction.ratio보다 낮게 설정하면 덜 공격적인) 압축을 허용해요. 비수요 시간이 비활성화되면(기본) 무시돼요. 이는 hbase.hstore.compaction.ratio와 동일하게 작동해요. 기본값: 5.0F
-
hbase.offpeak.start.hour 비수요 시간의 시작. 0과 23 사이의 정수(포함)로 표현돼요. -1로 설정하면 비수요 시간을 비활성화해요. 기본값: -1 (disabled)
-
hbase.offpeak.end.hour 비수요 시간의 끝. 0과 23 사이의 정수(포함)로 표현돼요. -1로 설정하면 비수요 시간을 비활성화해요. 기본값: -1 (disabled)
-
hbase.regionserver.thread.compaction.throttle 압축을 위한 두 개의 서로 다른 스레드 풀이 있어요. 하나는 큰 압축용, 다른 하나는 작은 압축용이에요. 이는 hbase:meta 같은 lean 테이블의 압축을 빠르게 유지하는 데 도움이 돼요. 압축이 이 임계값보다 크면 큰 압축 풀에 들어가요. 대부분의 경우 기본값이 적절해요. 기본값: 2 x hbase.hstore.compaction.max x hbase.hregion.memstore.flush.size (기본 128)
-
hbase.hregion.majorcompaction major 압축 사이의 시간. 밀리초로 표현돼요. 0으로 설정하면 시간 기반 자동 major 압축을 비활성화해요. 사용자 요청 및 크기 기반 major 압축은 여전히 실행돼요. 이 값에 hbase.hregion.majorcompaction.jitter를 곱해 주어진 시간 창 동안 압축이 다소 임의의 시간에 시작하게 해요. 기본값: 7 days (604800000 milliseconds)
-
hbase.hregion.majorcompaction.jitter hbase.hregion.majorcompaction에 적용되는 승수로, hbase.hregion.majorcompaction 간격의 양쪽으로 주어진 시간만큼 압축이 발생하게 해요. 숫자가 작을수록 압축이 hbase.hregion.majorcompaction 간격에 더 가까이 발생해요. 부동소수점 십진수로 표현돼요. 기본값: .50F
압축 파일 선택 (Compaction File Selection)
레거시 정보
이 섹션은 역사적 이유로 보존되었으며 HBase 0.96.x 이전의 압축 방식을 가리켜요. RatioBasedCompactionPolicy 알고리즘을 활성화하면 이 동작을 여전히 사용할 수 있어요. HBase 0.96.x 이상에서 압축이 작동하는 방식에 대한 정보는 Compaction을 참고해 주세요.
StoreFile 선택의 핵심 알고리즘을 이해하기 위해 Store 소스 코드에 있는 일부 ASCII 아트가 유용한 참조 역할을 해요.
아래에 복사했어요:
/* normal skew:
*
* older ----> newer
* _
* | | _
* | | | | _
* --|-|- |-|- |-|---_-------_------- minCompactSize
* | | | | | | | | _ | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
*/
중요한 손잡이(knobs):
- hbase.hstore.compaction.ratio 압축 파일 선택 알고리즘에 사용되는 비율(기본 1.2f).
- hbase.hstore.compaction.min (HBase v 0.90에서 이것은 hbase.hstore.compactionThreshold라 함)(파일 수) 압축이 발생하기 위해 Store당 선택되는 최소 StoreFile 수(기본 2).
- hbase.hstore.compaction.max (파일 수) minor 압축당 압축할 최대 StoreFile 수(기본 10).
- hbase.hstore.compaction.min.size (바이트) 이 설정보다 작은 StoreFile은 자동으로 압축 후보가 돼요. 기본 hbase.hregion.memstore.flush.size (128 mb).
- hbase.hstore.compaction.max.size (.92) (바이트) 이 설정보다 큰 StoreFile은 압축에서 자동으로 제외돼요(기본 Long.MAX_VALUE).
minor 압축 StoreFile 선택 로직은 크기 기반이며, 파일 ⇐ sum(smaller_files) * hbase.hstore.compaction.ratio일 때 파일을 압축에 선택해요.
Minor 압축 파일 선택 - 예시 #1 (기본)
이 예시는 단위 테스트 TestCompactSelection의 예시를 반영해요.
- hbase.hstore.compaction.ratio = 1.0f
- hbase.hstore.compaction.min = 3 (files)
- hbase.hstore.compaction.max = 5 (files)
- hbase.hstore.compaction.min.size = 10 (bytes)
- hbase.hstore.compaction.max.size = 1000 (bytes)
다음 StoreFile이 존재해요: 100, 50, 23, 12, 12바이트(오래된 것에서 새 것 순). 위 매개변수로 minor 압축에 선택될 파일은 23, 12, 12예요.
왜?
- 100 → 아니오, sum(50, 23, 12, 12) * 1.0 = 97이므로.
- 50 → 아니오, sum(23, 12, 12) * 1.0 = 47이므로.
- 23 → 예, sum(12, 12) * 1.0 = 24이므로.
- 12 → 예, 이전 파일이 포함되었고 최대 파일 수 5를 초과하지 않으므로.
- 12 → 예, 이전 파일이 포함되었고 최대 파일 수 5를 초과하지 않으므로.
Minor 압축 파일 선택 - 예시 #2 (압축할 파일이 충분하지 않음)
- hbase.hstore.compaction.ratio = 1.0f
- hbase.hstore.compaction.min = 3 (files)
- hbase.hstore.compaction.max = 5 (files)
- hbase.hstore.compaction.min.size = 10 (bytes)
- hbase.hstore.compaction.max.size = 1000 (bytes)
다음 StoreFile이 존재해요: 100, 25, 12, 12바이트(오래된 것에서 새 것 순). 위 매개변수로 압축이 시작되지 않을 거예요.
왜?
- 100 → 아니오, sum(25, 12, 12) * 1.0 = 47이므로.
- 25 → 아니오, sum(12, 12) * 1.0 = 24이므로.
- 12 → 아니오. 후보이지만 sum(12) * 1.0 = 12이고, 압축할 파일이 2개뿐이며 그건 임계값 3보다 적으므로.
- 12 → 아니오. 이전 StoreFile이 후보였지만, 압축할 파일이 충분하지 않으므로.
Minor 압축 파일 선택 - 예시 #3 (압축할 파일 제한)
- hbase.hstore.compaction.ratio = 1.0f
- hbase.hstore.compaction.min = 3 (files)
- hbase.hstore.compaction.max = 5 (files)
- hbase.hstore.compaction.min.size = 10 (bytes)
- hbase.hstore.compaction.max.size = 1000 (bytes)
다음 StoreFile이 존재해요: 7, 6, 5, 4, 3, 2, 1바이트(오래된 것에서 새 것 순). 위 매개변수로 minor 압축에 선택될 파일은 7, 6, 5, 4, 3이에요.
왜?
- 7 → 예, sum(6, 5, 4, 3, 2, 1) * 1.0 = 21이므로. 또한 7은 min-size보다 작아요.
- 6 → 예, sum(5, 4, 3, 2, 1) * 1.0 = 15이므로. 또한 6은 min-size보다 작아요.
- 5 → 예, sum(4, 3, 2, 1) * 1.0 = 10이므로. 또한 5는 min-size보다 작아요.
- 4 → 예, sum(3, 2, 1) * 1.0 = 6이므로. 또한 4는 min-size보다 작아요.
- 3 → 예, sum(2, 1) * 1.0 = 3이므로. 또한 3은 min-size보다 작아요.
- 2 → 아니오. 이전 파일이 선택되었고 2가 min-size보다 작으므로 후보지만, 압축할 최대 파일 수에 도달했어요.
- 1 → 아니오. 이전 파일이 선택되었고 1이 min-size보다 작으므로 후보지만, 압축할 최대 파일 수에 도달했어요.
핵심 구성 옵션의 영향
이 정보는 이제 압축 알고리즘에서 사용하는 매개변수의 구성 매개변수 표에 포함돼요.
날짜 계층 압축 (Date Tiered Compaction)
날짜 계층 압축은 시계열 데이터의 시간 범위 스캔에 유용한 날짜 인식 store file 압축 전략이에요.
날짜 계층 압축을 언제 사용하나요 (When To Use Date Tiered Compactions)
제한된 시간 범위에 대한 읽기, 특히 최근 데이터의 스캔에 Date Tiered Compaction 사용을 고려해 주세요.
다음에는 사용하지 마세요.
- 제한된 시간 범위가 없는 임의 get
- 빈번한 삭제와 갱신
- 긴 꼬리(tails)를 만드는 빈번한 순서 없는 데이터 쓰기, 특히 미래 타임스탬프를 가진 쓰기
- 시간 범위가 많이 겹치는 빈번한 벌크 로드
성능 개선 (Performance Improvements) 성능 테스트는 제한된 시간 범위, 특히 최근 데이터의 스캔에 대해 시간 범위 스캔 성능이 크게 개선되는 것을 보여줬어요.
날짜 계층 압축 활성화 (Enabling Date Tiered Compaction)
테이블 또는 컬럼 패밀리에 대해 hbase.hstore.engine.class를 org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine로 설정해 Date Tiered 압축을 활성화할 수 있어요.
모든 기본 설정을 사용한다면 hbase.hstore.blockingStoreFiles를 기본값 12가 아닌 60 같은 높은 숫자로 설정해야 해요. 매개변수를 변경하면 1.5~2 x 예상 파일 수를 사용하세요. 예상 파일 수 = 계층당 창 수 x 계층 수 + 들어오는 창 최솟값 + 최대 연령보다 오래된 파일 수.
또한 hbase.hstore.compaction.max를 hbase.hstore.blockingStoreFiles와 같은 값으로 설정해 major 압축을 차단 해제해야 해요.
절차: 날짜 계층 압축 활성화
HBase shell에서 다음 명령 중 하나를 실행하세요. 테이블 이름 orders_table을 자신의 테이블 이름으로 바꾸세요.
alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine', 'hbase.hstore.blockingStoreFiles' => '60', 'hbase.hstore.compaction.min'=>'2', 'hbase.hstore.compaction.max'=>'60'}
alter 'orders_table', {NAME => 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine', 'hbase.hstore.blockingStoreFiles' => '60', 'hbase.hstore.compaction.min'=>'2', 'hbase.hstore.compaction.max'=>'60'}}
create 'orders_table', 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine', 'hbase.hstore.blockingStoreFiles' => '60', 'hbase.hstore.compaction.min'=>'2', 'hbase.hstore.compaction.max'=>'60'}
필요하면 다른 옵션도 구성하세요. 자세한 내용은 Configuring Date Tiered Compaction을 참고해 주세요.
절차: 날짜 계층 압축 비활성화
hbase.hstore.engine.class 옵션을 nil 또는 org.apache.hadoop.hbase.regionserver.DefaultStoreEngine로 설정하세요. 두 옵션 모두 같은 효과가 있어요. 변경한 다른 옵션도 원래 설정으로 되돌려두세요.
alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DefaultStoreEngine', 'hbase.hstore.blockingStoreFiles' => '12', 'hbase.hstore.compaction.min'=>'6', 'hbase.hstore.compaction.max'=>'12'}
어느 쪽이든 store engine을 변경하면 대부분의 region에서 major compaction이 수행될 가능성이 높아요. 새 테이블에서는 필요하지 않아요.
날짜 계층 압축 구성 (Configuring Date Tiered Compaction)
날짜 계층 압축의 각 설정은 테이블 또는 컬럼 패밀리 수준에서 구성해야 해요. HBase shell을 사용한다면 일반 명령 패턴은 다음과 같아요.
alter 'orders_table', CONFIGURATION => {'key' => 'value', ..., 'key' => 'value'}}
데이터 계층 매개변수 (Data Tier Parameters) 다음 매개변수의 설정을 변경해 날짜 계층을 구성할 수 있어요.
| Setting | Notes |
|---|---|
| hbase.hstore.compaction.date.tiered.max.storefile.age.millis | 이보다 작은 max-timestamp를 가진 파일은 더 이상 압축되지 않습니다. 기본 Long.MAX_VALUE. |
| hbase.hstore.compaction.date.tiered.base.window.millis | 밀리초 단위의 기본 창 크기. 기본 6시간. |
| hbase.hstore.compaction.date.tiered.windows.per.tier | 계층당 창 수. 기본 4. |
| hbase.hstore.compaction.date.tiered.incoming.window.min | 들어오는 창에서 압축할 최소 파일 수. 낭비적인 압축을 피하기 위해 창의 예상 파일 수로 설정하세요. 기본 6. |
| hbase.hstore.compaction.date.tiered.window.policy.class | 같은 시간 창 안의 store file을 선택하는 정책. 들어오는 창에는 적용되지 않습니다. 기본 exploring compaction. 낭비적인 압축을 피하기 위한 것입니다. |
압축 스로틀러 (Compaction Throttler) 계층화 압축에서는 클러스터의 모든 서버가 동시에 창을 더 높은 계층으로 승격시키므로 압축 스로틀을 사용하는 것이 권장돼요: hbase.regionserver.throughput.controller를 org.apache.hadoop.hbase.regionserver.compactions.PressureAwareCompactionThroughputController로 설정하세요.
날짜 계층 압축에 대한 자세한 내용은 https://docs.google.com/document/d/1_AmlNb2N8Us1xICsTeGDLKIqL6T-oHoRLZ323MG_uy8의 설계 명세를 참고해 주세요.
실험적: Stripe 압축 (Experimental: Stripe Compactions)
Stripe 압축은 HBase 0.98에서 추가된 실험적 기능으로, 큰 region이나 균일하지 않게 분포된 row key에 대한 압축을 개선하는 것을 목표로 해요. 더 작고/더 세분화된 압축을 달성하기 위해 region 내의 StoreFile이 region의 여러 row-key 하위 범위, 즉 "stripe"에 대해 별도로 유지돼요. Stripe는 HBase의 나머지 부분에 투명하므로 HFile이나 데이터에 대한 다른 연산은 수정 없이 작동해요.
Stripe 압축은 HFile 레이아웃을 변경해 region 안에 하위 region을 만들어요. 이 하위 region은 압축하기 더 쉬우며, major 압축이 더 적게 이어져야 해요. 이 접근은 더 큰 region의 일부 문제를 완화해요.
Stripe 압축은 Compaction과 완전히 호환되며 ExploringCompactionPolicy 또는 RatioBasedCompactionPolicy 중 하나와 함께 작동해요. 기존 테이블에 대해 활성화할 수 있고, 나중에 비활성화하면 테이블이 정상적으로 계속 작동해요.
Stripe 압축을 언제 사용하나요 (When To Use Stripe Compactions)
다음 중 하나가 있으면 stripe 압축 사용을 고려해 주세요.
- 큰 region. MemStore와 region 관리 오버헤드에 대한 추가 오버헤드 없이 더 작은 region의 긍정적 효과를 얻을 수 있어요.
- 키의 시간 차원 같은 비균일 키. 새 키를 받는 stripe만 압축하면 돼요. 오래된 데이터는 자주 압축되지 않으며, 압축된다 해도 안 돼요.
성능 개선 (Performance Improvements) 성능 테스트는 읽기 성능이 다소 개선되고, 읽기와 쓰기 성능의 변동성이 크게 줄어드는 것을 보여줬어요. 해시 접두사 타임스탬프 키 같은 크고 비균일한 row-key region에서 전반적인 장기 성능 개선이 보여요. 이러한 성능 이점은 이미 큰 테이블에서 가장 극적이에요. 성능 개선이 region 분할까지 확장될 수도 있어요.
Stripe 압축 활성화 (Enabling Stripe Compaction)
테이블 또는 컬럼 패밀리에 대해 hbase.hstore.engine.class를 org.apache.hadoop.hbase.regionserver.StripeStoreEngine로 설정해 stripe 압축을 활성화할 수 있어요. 또한 hbase.hstore.blockingStoreFiles를 높은 숫자(기본값 10이 아닌 100 같은)로 설정해야 해요.
절차: Stripe 압축 활성화
HBase shell에서 다음 명령 중 하나를 실행하세요. 테이블 이름 orders_table을 자신의 테이블 이름으로 바꾸세요.
alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.StripeStoreEngine', 'hbase.hstore.blockingStoreFiles' => '100'}
alter 'orders_table', {NAME => 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.StripeStoreEngine', 'hbase.hstore.blockingStoreFiles' => '100'}}
create 'orders_table', 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.StripeStoreEngine', 'hbase.hstore.blockingStoreFiles' => '100'}
필요하면 다른 옵션도 구성하세요. 자세한 내용은 Configuring Stripe Compaction을 참고해 주세요.
테이블을 활성화하세요.
절차: Stripe 압축 비활성화
hbase.hstore.engine.class 옵션을 nil 또는 org.apache.hadoop.hbase.regionserver.DefaultStoreEngine로 설정하세요. 두 옵션 모두 같은 효과가 있어요.
alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DefaultStoreEngine'}
테이블을 활성화하세요.
어느 쪽이든 store engine을 변경한 후 큰 테이블을 활성화하면 대부분의 region에서 major compaction이 수행될 가능성이 높아요. 새 테이블에서는 필요하지 않아요.
Stripe 압축 구성 (Configuring Stripe Compaction)
stripe 압축의 각 설정은 테이블 또는 컬럼 패밀리 수준에서 구성해야 해요. HBase shell을 사용한다면 일반 명령 패턴은 다음과 같아요.
alter 'orders_table', CONFIGURATION => {'key' => 'value', ..., 'key' => 'value'}}
Region과 stripe 크기 조정 (Region and stripe sizing) region 크기 조정에 기반해 stripe 크기 조정을 구성할 수 있어요. 기본적으로 새 region은 하나의 stripe로 시작해요. stripe가 너무 커지면(16 x MemStore flushes 크기) 다음 압축에서 두 stripe로 분할돼요. region이 자랄 때 stripe 분할이 계속되며, region이 분할될 만큼 충분히 커질 때까지요.
자신의 데이터에 맞게 이 패턴을 개선할 수 있어요. 좋은 규칙은 stripe 크기를 최소 1 GB로, 균일한 row key에 대해 약 8-12개의 stripe로 목표하는 것이에요. 예를 들어 region이 30 GB라면 12 x 2.5 GB stripe가 좋은 시작점일 수 있어요.
Stripe 크기 조정 설정 (Stripe Sizing Settings)
| Setting | Notes |
|---|---|
| hbase.store.stripe.initialStripeCount | stripe 압축이 활성화될 때 생성할 stripe 수. 다음과 같이 사용할 수 있습니다:- 상대적으로 균일한 row key의 경우, 위에서 대략적인 목표 stripe 수를 안다면 몇 개(2, 5, 10...)로 시작해 분할 오버헤드 일부를 피할 수 있습니다. 초기 데이터가 전체 row key 분포를 대표하지 않으면 이는 효율적이지 않을 것입니다.- 많은 데이터가 있는 기존 테이블의 경우 이 설정이 사실상 stripe를 사전 분할합니다.- region당 둘 이상의 hash prefix가 있는 hash-prefix sequential key 같은 키의 경우 사전 분할이 의미가 있을 수 있습니다. |
| hbase.store.stripe.sizeToSplit | stripe이 분할 전 자랄 수 있는 최대 크기. 위 크기 조정 고려 사항에 따라 목표 stripe 크기를 제어하기 위해 hbase.store.stripe.splitPartCount와 함께 사용하세요 (sizeToSplit = splitPartsCount * target stripe size). |
| hbase.store.stripe.splitPartCount | stripe을 분할할 때 생성할 새 stripe 수. 기본은 2이며 대부분의 경우 적절합니다. 비균일 row key의 경우 추가 분할 없이 도착하는 갱신을 region의 더 좁은 조각으로 격리하도록 수를 3이나 4로 늘려 실험할 수 있습니다. |
MemStore 크기 설정 (MemStore Size Settings) 기본적으로 플러시는 기존 stripe 경계와 플러시할 row key에 따라 하나의 MemStore에서 여러 파일을 만들어요. 이 접근은 쓰기 증폭을 최소화하지만, MemStore가 작고 stripe가 많으면 파일이 너무 작아져 바람직하지 않을 수 있어요. 이런 상황에서 hbase.store.stripe.compaction.flushToL0을 true로 설정할 수 있어요. 이렇게 하면 MemStore 플러시가 단일 파일을 대신 만들어요. hbase.store.stripe.compaction.minFilesL0(기본 4)개의 그러한 파일이 최소한 쌓이면 striped 파일로 압축돼요.
일반 압축 구성과 Stripe 압축 (Normal Compaction Configuration and Stripe Compaction) 일반 압축에 적용되는 모든 설정(압축 알고리즘에서 사용하는 매개변수 참조)은 stripe 압축에도 적용돼요. 예외는 minimum과 maximum 파일 수로, stripe의 파일이 더 작기 때문에 기본값이 더 높게 설정돼요. stripe 압축에서 이를 제어하려면 hbase.hstore.compaction.min과 hbase.hstore.compaction.max 대신 hbase.store.stripe.compaction.minFiles와 hbase.store.stripe.compaction.maxFiles를 사용해 주세요.
FIFO 압축
FIFO 압축 정책은 모든 cell이 만료된 파일만 선택해요. 컬럼 패밀리는 반드시 기본이 아닌 TTL을 가져야 해요. 본질적으로 FIFO compactor는 만료된 store file만 수집해요.
실제 압축을 하지 않으므로 CPU와 IO(디스크 및 네트워크)를 사용하지 않고 블록 캐시에서 핫 데이터를 쫓아내지 않아요. 결과적으로 RW 처리량과 지연 시간이 모두 개선될 수 있어요.
FIFO 압축을 언제 사용하나요 (When To Use FIFO Compaction)
사용 사례가 다음일 때 FIFO Compaction 사용을 고려해 주세요.
- 낮은 TTL을 가지며 (추가 처리 후) 다른 데이터의 원천이 되는 매우 많은 양의 원시 데이터.
- 블록 캐시(RAM/SSD)에 완전히 보관할 수 있는 데이터. 원시 데이터의 압축이 전혀 필요 없음.
다음 경우 FIFO 압축을 사용하지 마세요.
- Table/ColumnFamily에 MIN_VERSION > 0
- Table/ColumnFamily에 TTL = FOREVER (HColumnDescriptor.DEFAULT_TTL)
FIFO 압축 활성화 (Enabling FIFO Compaction)
테이블용:
HTableDescriptor desc = new HTableDescriptor(tableName);
desc.setConfiguration(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY,
FIFOCompactionPolicy.class.getName());
컬럼 패밀리용:
HColumnDescriptor desc = new HColumnDescriptor(family);
desc.setConfiguration(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY,
FIFOCompactionPolicy.class.getName());
HBase Shell에서:
create 'x',{NAME=>'y', TTL=>'30'}, {CONFIGURATION => {'hbase.hstore.defaultengine.compactionpolicy.class' => 'org.apache.hadoop.hbase.regionserver.compactions.FIFOCompactionPolicy', 'hbase.hstore.blockingStoreFiles' => 1000}}
region 분할은 여전히 지원되지만, 최적 성능을 위해서는 명시적으로 DisabledRegionSplitPolicy를 설정하거나 ConstantSizeRegionSplitPolicy와 매우 큰 최대 region 크기를 설정해 비활성화해야 해요. store의 blocking file(hbase.hstore.blockingStoreFiles)도 매우 큰 숫자로 늘려야 해요. FIFO 압축의 경우 테이블/컬럼 패밀리 구성에 sanity 검사가 있으며 blocking file의 최솟값은 1000이에요.