중간 크기 객체 저장하기
중간 크기 객체 저장하기 (Storing Medium-sized Objects (MOB))
HBase의 MOB 기능은 중간 크기 객체(100KB~10MB)를 별도로 저장해 쓰기 증폭(write amplification)을 줄여 성능을 유지하는 방법이에요. MOB로 컬럼을 구성하는 방법, MOB 아키텍처, 압축, 캐시 설정, 업그레이드 고려 사항과 트러블슈팅을 살펴볼게요.
출처: 문서
본문
데이터는 다양한 크기로 오며, 이미지나 문서 같은 바이너리 데이터를 포함해 모든 데이터를 HBase에 저장하는 것이 이상적이에요. HBase가 기술적으로는 100KB보다 큰 cell 크기의 바이너리 객체를 처리할 수 있지만, HBase의 일반 읽기·쓰기 경로는 100KB보다 작은 값에 최적화되어 있어요. 이 임계값을 넘는 많은 수의 객체(여기서는 중간 객체, 즉 MOB라 함)를 다룰 때는 분할(split)과 압축(compaction)으로 인한 쓰기 증폭 때문에 성능이 저하돼요. MOB를 사용할 때 객체는 이상적으로 100KB에서 10MB 사이여야 해요(faq 참고). HBase 2는 성능, 일관성, 낮은 운영 오버헤드를 유지하기 위해 MOB에 대한 특수 내부 처리를 추가했어요. MOB 지원은 HBASE-11339의 작업으로 제공돼요.
MOB를 활용하려면 HFile 버전 3을 사용해야 해요. 선택적으로 각 RegionServer의 MOB 파일 리더 캐시 설정을 구성하고(Configure the MOB Cache 참고), 그런 다음 특정 컬럼이 MOB 데이터를 담도록 구성해 주세요. HBase MOB 지원을 활용하기 위해 클라이언트 코드는 변경할 필요 없어요. 이 기능은 클라이언트에 투명해요.
MOB용 컬럼 구성하기 (Configuring Columns for MOB)
테이블 생성이나 변경 중에 HBase Shell 또는 Java API를 통해 컬럼이 MOB를 지원하도록 구성할 수 있어요. 관련 속성 두 가지는 boolean IS_MOB와 MOB_THRESHOLD로, 이는 객체가 MOB로 간주되는 바이트 수예요. IS_MOB만 필수예요. MOB_THRESHOLD를 지정하지 않으면 기본 임계값 100 KB가 사용돼요.
HBase Shell로 컬럼에 MOB 구성하기 (Configure a Column for MOB Using HBase Shell)
hbase> create 't1', {NAME => 'f1', IS_MOB => true, MOB_THRESHOLD => 102400}
hbase> alter 't1', {NAME => 'f1', IS_MOB => true, MOB_THRESHOLD => 102400}
Java API로 컬럼에 MOB 구성하기 (Configure a Column for MOB Using the Java API)
...
HColumnDescriptor hcd = new HColumnDescriptor("f");
hcd.setMobEnabled(true);
...
hcd.setMobThreshold(102400L);
...
MOB 테스트 (Testing MOB)
MOB 기능 테스트를 돕기 위해 org.apache.hadoop.hbase.IntegrationTestIngestWithMOB 유틸리티가 제공돼요. 이 유틸리티는 다음과 같이 실행해요.
$ sudo -u hbase hbase org.apache.hadoop.hbase.IntegrationTestIngestWithMOB \
-threshold 1024 \
-minMobDataSize 512 \
-maxMobDataSize 5120
- threshold는 cell이 MOB로 간주되는 임계값이에요. 기본은 1 kB이며 바이트로 표현돼요.
- minMobDataSize는 MOB 데이터 크기의 최솟값이에요. 기본은 512 B이며 바이트로 표현돼요.
- maxMobDataSize는 MOB 데이터 크기의 최댓값이에요. 기본은 5 kB이며 바이트로 표현돼요.
MOB 아키텍처 (MOB architecture)
이 섹션은 HBase에서 MOB의 초기 GA 구현을 다룬 HBASE-11339와, RegionServer 전반에 걸쳐 MOB 유지 관리를 병렬화해 개선한 HBASE-22749에서 찾을 수 있는 정보에서 파생되었어요. 자세한 내용은 초기 작업 중 생성된 디자인 문서의 마지막 버전인 "HBASE-11339 MOB GA design.pdf"와 분산 mob 압축 기능의 디자인 문서인 "HBASE-22749 MOB distributed compaction.pdf"를 참고해 주세요.
개요 (Overview)
MOB 기능은 구성된 컬럼 패밀리에 대해 구성된 임계값보다 큰 값을 일반 region 밖에 저장해 분할, 병합, 그리고 가장 중요하게는 일반 압축을 피함으로써 전체 IO 부하를 줄여요.
cell이 region에 처음 기록되면 값 크기와 관계없이 WAL과 memstore에 저장돼요. MOB를 사용하도록 구성된 컬럼 패밀리의 memstore가 결국 플러시되면 두 개의 hfile이 동시에 기록돼요. 임계값 크기보다 작은 값을 가진 cell은 일반 region hfile에 기록돼요. 임계값보다 큰 값을 가진 cell은 특수 MOB hfile에 기록되고, 일반 region HFile에도 MOB 참조 cell이 기록돼요. Region Server가 MOB 활성 memstore를 플러시하고 주어진 일반 region HFile을 닫을 때, 그 안의 cell들이 참조하는 각 특수 MOB hfile을 나열하는 메타데이터를 덧붙여요.
MOB 참조 cell은 기반이 되는 cell과 같은 키를 가져요. 참조 cell의 값은 두 가지 메타데이터로 구성돼요: 실제 값의 크기와 원래 cell을 담고 있는 MOB hfile. HBase에 원래 기록된 태그 외에도 참조 cell은 두 개의 추가 태그를 앞에 붙여요. 첫 번째는 cell이 MOB 참조라는 것을 나타내는 마커 태그예요. 나중에 참조 cell만 특별히 스캔하는 데 사용할 수 있어요. 두 번째는 MOB hfile을 기록할 때의 namespace와 table을 저장해요. 이 태그는 일련의 HBase 스냅샷 연산 후 MOB 시스템이 MOB hfile에서 기반 값을 찾는 방법을 최적화하는 데 사용돼요(ref HBASE-12332). 태그는 HBase 서버 내에서만 사용 가능하며 기본적으로 RPC를 통해 전송되지 않는다는 점을 주의해 주세요.
주어진 테이블의 모든 MOB hfile은 요청을 직접 서빙하지 않는 논리적 region 내에서 관리돼요. 이 MOB hfile들이 플러시나 MOB 압축에서 생성될 때, namespace, table, mob 논리적 region, 컬럼 패밀리에 특정한 hbase 루트 디렉터리 아래의 전용 mob 데이터 영역에 배치돼요. 일반적으로 그 경로는 다음과 같은 구조예요.
%HBase Root Dir%/mobdir/data/%namespace%/%table%/%logical region%/%column family%/
기본 구성에서 default namespace의 'some_table'이라는 예시 테이블과 'foo'라는 MOB 활성 컬럼 패밀리가 있다면 이 HDFS 디렉터리는 다음과 같아요.
/hbase/mobdir/data/default/some_table/372c1b27e3dc0b56c3a031926e5efbe9/foo/
이 MOB hfile들은 HBase Master와 개별 Region Server 전반의 특수 chores가 유지 관리해요. 특히 그 chores는 TTL 적용과 압축을 담당해요. 이 압축은 주로 HDFS의 총 파일 수를 제어하는 문제인데, MOB 데이터의 운영 가정은 거의 갱신되거나 삭제되지 않는다는 것이기 때문이에요.
압축 과정의 결과로 주어진 MOB hfile이 더 이상 필요하지 않으면, Master의 chore가 일반 hfile처럼 아카이브로 이동하는 작업을 담당해요. 테이블의 mob region은 모든 일반 region과 독립적이므로 일반 아카이브 저장 영역에서 그것들과 공존할 수 있어요.
/hbase/archive/data/default/some_table/372c1b27e3dc0b56c3a031926e5efbe9/foo/
일반 region에서 필요 없는 아카이브 파일을 결국 삭제하는 것과 같은 hfile 정리 chores가 이 MOB hfile들도 처리해요. 그래서 MOB 활성 테이블의 스냅샷이 있다면, 정리 시스템은 그 MOB 파일들이 스냅샷이나 스냅샷의 클론이 필요로 하는 동안 아카이브 영역에 남아 있도록 보장해요.
MOB 압축 (MOB compaction)
MOB 활성 컬럼 패밀리의 memstore가 플러시를 수행할 때마다 HBase는 MOB 임계값을 넘는 값을 MOB 전용 hfile에 쓸 거예요. 일반 region 압축이 발생하면 Region Server는 이 MOB 파일들에 대한 참조를 유지하면서 일반 데이터 파일을 다시 쓰며, MOB 파일은 다시 쓰지 않아요. MOB 값에 대한 일반 클라이언트 조회는 투명하게 원래 값을 받는데, Region Server 내부가 참조 데이터를 사용해 특정 MOB 파일에서 값을 꺼내기 때문이에요. 이 간접(indirection)은 많은 수의 MOB hfile을 쌓아도 특정 MOB cell을 검색하는 전체 시간에 영향을 주지 않는다는 것을 의미해요. 따라서 일반 hfile만큼 자주 MOB hfile을 압축할 필요가 없어요. 결과적으로 HBase는 Region Server가 자체적으로 수행하는 주기적 압축의 일부로 MOB hfile을 다시 쓰지 않음으로써 IO를 절약해요.
하지만 MOB cell의 삭제와 갱신이 빈번하면 이 간접은 공간을 낭비하기 시작해요. 특정 MOB hfile의 공간 사용을 멈추는 유일한 방법은 여전히 참조를 보유한 cell이 없도록 하는 것이에요. 그러려면 현재 값을 새 MOB hfile에 기록했는지 확인해야 해요. HDFS처럼 백엔드 파일시스템이 존재할 수 있는 파일 수에 제한이 있다면, MOB cell의 삭제나 갱신이 없어도 결국 MOB hfile이 충분히 많아져서 이를 합치(병합)해야 해요.
주기적으로 master의 chore가 region server들이 새 MOB 파일을 다시 쓰는 것도 처리하는 특수 major compaction을 수행하도록 조정해요. 모든 압축처럼 Region Server는 MOB 임계값보다 작은 cell과 새로 다시 쓴 MOB 파일에 대한 참조를 보유한 cell을 모두 담은 갱신된 hfile을 생성할 거예요. 이 재작성은 region의 모든 활성 cell을 살펴볼 수 있는 장점이 있으므로, 여러 개의 작은 MOB 파일은 region당 단일 MOB 파일로 끝나야 해요. 이 chore는 기본적으로 매주 실행되며 hbase.mob.compaction.chore.period를 원하는 주기(초)로 설정해 구성할 수 있어요.
<property>
<name>hbase.mob.compaction.chore.period</name>
<value>2592000</value>
<description>Example of changing the chore period from a week to a month.</description>
</property>
기본적으로 주기적 MOB 압축 조정 chore는 클러스터에서 수행되는 작업량을 최대화하기 위해 모든 region이 병렬로 압축을 수행하도록 유지하려 시도해요. 이 압축이 기본 파일시스템에서 생성하는 IO 양을 튜닝해야 한다면, hbase.mob.major.compaction.region.batch.size를 0보다 큰 정수로 설정해 허용되는 동시 region 수준 압축 요청 수를 제어할 수 있어요. 구성을 0으로 설정하면 모든 region을 병렬로 시도하는 기본 동작을 얻게 돼요.
<property>
<name>hbase.mob.major.compaction.region.batch.size</name>
<value>1</value>
<description>Example of switching from "as parallel as possible" to "serially"</description>
</property>
MOB 파일 아카이빙 (MOB file archiving)
결국 더 이상 필요 없는 MOB hfile이 생길 거예요. 클라이언트가 값을 덮어쓰거나, MOB 재작성 압축이 더 새롭고 더 큰 MOB hfile에 대한 참조를 저장할 거예요. 주어진 MOB cell은 현재 region이나 이전 시점에 존재했던 부모 region에 원래 기록되었을 수 있으므로, 개별 Region Server는 MOB hfile을 아카이빙할 때를 결정하지 않아요. 대신 Master의 주기적 chore가 아카이빙할 MOB hfile을 평가해요.
MOB HFile은 다음 조건 중 하나에서 아카이빙 대상이 돼요.
- 컬럼 패밀리의 TTL보다 오래된 모든 MOB HFile
- 컬럼 패밀리의 모든 region에 대한 일반 hfile에서 참조가 없는, "너무 최근(too recent)" 임계값보다 오래된 모든 MOB HFile
MOB HFile이 두 번째 기준을 충족하는지 판단하려면 chore가 주어진 테이블의 각 MOB 활성 컬럼 패밀리에 대한 일반 HFile에서 메타데이터를 추출해요. 그 메타데이터는 일반 HFile 영역에 저장된 참조를 충족하는 데 필요한 MOB HFile의 완전한 집합을 열거해요.
cleaner chore의 주기는 hbase.master.mob.cleaner.period를 양의 정수 초로 설정해 구성할 수 있어요. 기본적으로 매일 실행돼요. 매우 공격적인 TTL이나, 이후에 높은 비율의 비-MOB 압축을 수반하는 매우 높은 비율의 MOB 갱신이 없다면 튜닝할 필요가 없어야 해요.
MOB 최적화 작업 (MOB Optimization Tasks)
쓰기 증폭 추가 제한 (Further limiting write amplification)
MOB 워크로드에 갱신이나 삭제가 거의 없다면, 쓰기 증폭량을 제한하도록 최적화하는 MOB 압축을 선택할 수 있어요. 이는 압축 과정 중 MOB 파일을 무시하도록 크기 임계값을 설정함으로써 이루어져요. 주어진 region이 MOB 압축을 거칠 때 현재 실제 값을 보유한 MOB 파일의 크기를 평가하고, 그 파일이 임계값을 초과하면 값을 다시 쓰는 것을 건너뛰어요.
이 모드의 쓰기 증폭 상한은 "Write Amplification" = logₖ(M/S)로 근사할 수 있어요. 여기서 K는 압축 선택의 파일 수, M은 MOB 파일 크기의 구성 가능한 임계값, S는 처음에 MOB 파일을 생성하는 memstore 플러시의 최소 크기예요. 예를 들어 압축당 5개 파일, 임계값 1 GB, 플러시 크기 10MB가 주어지면 쓰기 증폭은 log₅(1GB/10MB) = log₅(100) ≈ 2.86이에요.
HDFS처럼 파일 수에 제한이 있는 기본 파일시스템을 사용하고 예상 데이터셋 크기를 안다면, 최대 파일 크기를 선택해 이 제한에 접근하되 그 안에 머물러 쓰기 증폭을 최소화할 수 있어요. 예를 들어 페타바이트를 저장할 것으로 예상하고 HDFS 인스턴스에 100만 파일의 보수적인 제한이 있다면, 1PB/1M = 1GB로 MOB 파일당 기가바이트의 목표 제한을 얻어요.
이 압축 모드를 선택하려면 hbase.mob.compaction.type을 optimized로 설정해야 해요. 이 모드의 기본 MOB 크기 임계값은 1GB로 설정돼요. hbase.mob.compactions.max.file.size를 양의 정수 바이트로 설정해 변경할 수 있어요.
<property>
<name>hbase.mob.compaction.type</name>
<value>optimized</value>
<description>opt-in to write amplification optimized mob compaction.</description>
</property>
<property>
<name>hbase.mob.compactions.max.file.size</name>
<value>10737418240</value>
<description>Example of tuning the max mob file size to 10GB</description>
</property>
추가로, 이 모드에서 압축 과정은 최대 파일 임계값을 넘는 MOB 파일을 쓰지 않도록 하려 할 거예요. 추가 MOB 값을 MOB hfile에 기록하면서 추가 데이터가 hfile을 최대 파일 크기보다 크게 만드는지 확인해요. MOB 값의 hfile이 한계에 도달하면 MOB hfile이 MOB 저장 영역에 커밋되고 새 것이 생성돼요. 참조 cell이 있는 hfile은 메타데이터에서 필요로 하는 MOB hfile의 완전한 집합을 추적해요.
region 압축을 완료하는 총 시간에 주의하세요.
쓰기 증폭 최적화 압축 모드를 사용할 때 단일 region을 압축하는 최대 시간을 주시해야 해요. 1시간에 가까워지면 아래 트러블슈팅 섹션인 Adjusting the MOB cleaner's tolerance for new hfiles를 읽어 주세요. 거기서 논의된 조정을 하지 않으면 데이터 손실로 이어질 수 있어요.
MOB 캐시 구성 (Configuring the MOB Cache)
언제든지 HFiles 수에 비해 많은 수의 MOB 파일이 있을 수 있으므로, MOB 파일은 항상 열려 있지 않아요. MOB 파일 리더 캐시는 가장 최근에 사용된 MOB 파일을 열어 두는 LRU 캐시예요. 각 RegionServer에서 MOB 파일 리더 캐시를 구성하려면 RegionServer의 hbase-site.xml에 다음 속성을 추가하고, 환경에 맞게 구성을 커스터마이즈한 뒤 RegionServer를 재시작하거나 rolling restart해 주세요.
예시 MOB 캐시 구성 (Example MOB Cache Configuration)
<property>
<name>hbase.mob.file.cache.size</name>
<value>1000</value>
<description>
Number of opened file handlers to cache.
A larger value will benefit reads by providing more file handlers per mob
file cache and would reduce frequent file opening and closing.
However, if this is set too high, this could lead to a "too many opened file handers"
The default value is 1000.
</description>
</property>
<property>
<name>hbase.mob.cache.evict.period</name>
<value>3600</value>
<description>
The amount of time in seconds after which an unused file is evicted from the
MOB cache. The default value is 3600 seconds.
</description>
</property>
<property>
<name>hbase.mob.cache.evict.remain.ratio</name>
<value>0.5f</value>
<description>
A multiplier (between 0.0 and 1.0), which determines how many files remain cached
after the threshold of files that remains cached after a cache eviction occurs
which is triggered by reaching the `hbase.mob.file.cache.size` threshold.
The default value is 0.5f, which means that half the files (the least-recently-used
ones) are evicted.
</description>
</property>
MOB 파일 수동 압축 (Manually Compacting MOB Files)
주기적 chore가 압축을 트리거하기를 기다리는 대신 MOB 파일을 수동으로 압축하려면 major_compact HBase shell 명령을 사용해 주세요. 이 명령은 첫 번째 인자로 테이블 이름이 필요하고, 두 번째 인자로 컬럼 패밀리를 받아요. MOB 데이터를 포함하는 컬럼 패밀리와 함께 사용하면 이 operator 요청은 MOB 데이터가 압축되게 해요.
hbase> major_compact 't1'
hbase> major_compact 't2', 'c1'
같은 요청은 Admin.majorCompact Java API로도 할 수 있어요.
MOB 트러블슈팅 (MOB Troubleshooting)
새 hfiles에 대한 MOB cleaner의 허용 범위 조정 (Adjusting the MOB cleaner's tolerance for new hfiles)
MOB cleaner chore는 해당하는 일반 hfile의 참조 메타데이터를 놓치지 않도록, chore 시작 1시간 전보다 더 최근에 생성된 모든 MOB hfile을 무시해요. 이 안전 검사가 없으면 cleaner chore가 진행 중인 플러시나 압축의 MOB hfile을 보고 MOB 데이터를 조기에 아카이브할 수 있어요. 이 기본 버퍼는 일반적인 사용에 충분해야 해요.
쓰기 증폭 최적화 MOB 압축을 사용하고 기본 파일시스템 성능과 데이터 형태의 조합이 단일 region의 major compaction을 완료하는 데 1시간 이상 걸릴 수 있다면 허용 범위를 조정해야 해요. 예를 들어 MOB 데이터가 분포되어 가장 큰 region이 MOB 데이터 재작성을 포함한 압축 사이에 80GB의 MOB 데이터를 추가하고, HDFS 클러스터가 단일 파일에 20MB/s만 쓸 수 있다면, optimized 압축을 수행할 때 Region Server는 첫 번째 1GB MOB hfile을 쓰는 데 약 1분이 걸리며, 압축 끝에 새 참조 hfile을 커밋하기 전에 나머지 79개의 1GB MOB hfile을 쓰는 데 1시간 7분이 더 걸려요. 이 예시를 고려하면 더 큰 허용 범위 창이 필요해요.
Region Server 플러시 연산이 MOB hfile과 그것을 참조하는 일반 hfile을 모두 커밋하는 데 필요한 두 번의 HDFS 이동 연산에 1시간 이상 걸린다면 허용 범위를 조정해야 해요. 정상적으로 구성되고 건강한 HDFS와 HBase에서는 이런 지연이 발생하지 않아야 해요.
cleaner의 "too recent" 창은 hbase.mob.min.age.archive를 양의 정수 밀리초로 설정해 제어돼요.
<property>
<name>hbase.mob.min.age.archive</name>
<value>86400000</value>
<description>Example of tuning the cleaner to only archive files older than a day.</description>
</property>
HBase Shell을 통한 MOB 메타데이터 검색 (Retrieving MOB metadata through the HBase Shell)
MOB 시스템의 실패를 트러블슈팅하는 동안 scan에 특수 속성을 지정해 HBase shell을 통해 내부 정보 중 일부를 검색할 수 있어요.
hbase(main):112:0> scan 'some_table', {STARTROW => '00012-example-row-key', LIMIT => 1,
hbase(main):113:1* CACHE_BLOCKS => false, ATTRIBUTES => { 'hbase.mob.scan.raw' => '1',
hbase(main):114:2* 'hbase.mob.scan.ref.only' => '1' } }
MOB 내부 정보는 기반 cell 값의 크기에 대한 4바이트와, 기반 cell 값을 담고 있는 MOB HFile의 이름인 UTF8 문자열로 저장돼요. 기본적으로 이 직렬화된 구조 전체가 HBase shell의 binary string converter를 통과한다는 점을 주의해 주세요. 즉, 값 크기를 구성하는 바이트는 ASCII 문자에 해당하지 않는 한 대부분 이스케이프된 인쇄 불가능한 바이트 값(예: '\x03')으로 기록될 가능성이 높아요.
구체적인 예를 살펴볼게요.
hbase(main):112:0> scan 'some_table', {STARTROW => '00012-example-row-key', LIMIT => 1,
hbase(main):113:1* CACHE_BLOCKS => false, ATTRIBUTES => { 'hbase.mob.scan.raw' => '1',
hbase(main):114:2* 'hbase.mob.scan.ref.only' => '1' } }
ROW COLUMN+CELL
00012-example-row-key column=foo:bar, timestamp=1511179764, value=\x00\x02|\x94d41d8cd98f00b204
e9800998ecf8427e19700118ffd9c244fe69488bbc9f2c77d24a3e6a
1 row(s) in 0.0130 seconds
이 경우 처음 4바이트는 \x00\x02|\x94이며, 이는 바이트 [0x00, 0x02, 0x7C, 0x94]에 해당해요. (세 번째 바이트가 ASCII 문자 '|'로 출력된 것에 주목하세요.) 정수로 디코딩하면 기반 값 크기인 162,964바이트를 얻어요.
나머지 바이트는 HFile 이름 'd41d8cd98f00b204e9800998ecf8427e19700118ffd9c244fe69488bbc9f2c77d24a3e6a'를 제공해요. 이 HFile은 이 특정 테이블의 지정된 MOB 저장 영역에 저장되었을 가능성이 높아요. 하지만 이 테이블이 복원된 스냅샷에서 온 것이라면 파일이 아카이브 영역에 있을 수도 있어요. 게다가 테이블이 다른 테이블의 클론된 스냅샷에서 온 것이라면 파일은 그 소스 테이블의 활성 또는 아카이브 영역에 있을 수 있어요. 위 MOB 참조 cell 설명에서 언급했듯이, Region Server는 MOB HFile을 찾을 때 올바른 원래 테이블의 mob 및 archive 영역을 보는 것을 최적화하기 위해 서버 측 태그를 사용해요. 스캔은 클라이언트 측이므로 그 태그를 검색할 수 없어서, 테이블의 계보(lineage)를 이미 알고 있거나 모든 테이블을 검색해야 해요.
HBase 슈퍼유저 권한을 가진 사용자로 인증되었다고 가정하면 검색할 수 있어요.
$> hdfs dfs -find /hbase -name \
d41d8cd98f00b204e9800998ecf8427e19700118ffd9c244fe69488bbc9f2c77d24a3e6a
/hbase/mobdir/data/default/some_table/372c1b27e3dc0b56c3a031926e5efbe9/foo/d41d8cd98f00b204e9800998ecf8427e19700118ffd9c244fe69488bbc9f2c77d24a3e6a
컬럼 패밀리를 MOB 밖으로 옮기기 (Moving a column family out of MOB)
컬럼 패밀리에서 MOB를 비활성화하려면, 기능을 끄기 전에 HBase에 MOB 시스템 밖으로 데이터를 마이그레이션하라고 지시해야 해요. 이 작업을 하지 않으면 HBase는 실제 값을 해석해야 한다는 것을 알지 못하므로 애플리케이션에 내부 MOB 메타데이터를 반환할 거예요.
다음 절차는 클러스터 중단 없이 기반 데이터를 안전하게 마이그레이션할 거예요. 구성 설정이 적용되고 region이 다시 로드될 때 클라이언트는 여러 번의 재시도를 보게 될 거예요.
절차: MOB 유지 관리 중지, MOB 임계값 변경, 압축을 통한 데이터 재작성
hbase.mob.compaction.chore.period를 0으로 설정해 Master의 MOB compaction chore를 끄세요. 이 구성 변경을 적용하려면 HBase Masters의 rolling restart가 필요해요. 그러려면 활성 master의 장애 조치가 최소 한 번 필요하며, HBase 관리 연산을 수행하는 클라이언트에게 재시도를 유발할 수 있어요.
마이그레이션 기간 동안 HBase shell을 통해 테이블에 대해 MOB 압축 요청이 없도록 확인해 주세요.
MOB 크기 임계값 변경 (Change the MOB size threshold)
마이그레이션 중인 컬럼 패밀리의 MOB 크기 임계값을 그 컬럼 패밀리에 존재하는 가장 큰 cell보다 큰 값으로 변경하려면 HBase shell을 사용해 주세요. 예를 들어 'some_table'이라는 테이블과 'foo'라는 컬럼 패밀리가 주어지면 "우리가 저장하는 것보다 큰" 값으로 기가바이트 하나를 임의로 선택할 수 있어요.
hbase(main):011:0> alter 'some_table', {NAME => 'foo', MOB_THRESHOLD => '1000000000'}
Updating all regions with the new schema...
9/25 regions updated.
25/25 regions updated.
Done.
0 row(s) in 3.4940 seconds
아직 데이터를 수집 중이라면 이 임계값이 기록할 수 있는 어떤 cell 값보다 큰지 확인해야 한다는 점에 주의하세요. MAX_INT가 안전한 선택이에요.
테이블에서 major compaction 수행
구체적으로는 MOB 압축이 아니라 "일반(normal)" 압축을 수행하는 것이에요.
hbase(main):012:0> major_compact 'some_table'
0 row(s) in 0.2600 seconds
major compaction 종료 모니터링 (Monitor for the end of the major compaction)
압축은 비동기로 처리되므로 shell을 사용해 먼저 압축 시작을 보고, 그다음 종료를 봐야 해요.
HBase는 먼저 "MAJOR" 압축이 발생한다고 말해야 해요.
hbase(main):015:0> @hbase.admin(@formatter).instance_eval do
hbase(main):016:1* p @admin.get_compaction_state('some_table').to_string
hbase(main):017:2* end
"MAJOR"
압축이 끝나면 결과는 "NONE"을 출력해야 해요.
hbase(main):015:0> @hbase.admin(@formatter).instance_eval do
hbase(main):016:1* p @admin.get_compaction_state('some_table').to_string
hbase(main):017:2* end
"NONE"
mobrefs 유틸리티를 실행해 MOB cell이 없는지 확인해 주세요. 특히 이 도구는 Hadoop MapReduce 작업을 시작하며, 모든 데이터를 성공적으로 다시 썼을 때 입력 레코드가 0인 job 카운터를 보여줘요.
$> HADOOP_CLASSPATH=/etc/hbase/conf:$(hbase mapredcp) yarn jar \
/some/path/to/hbase-shaded-mapreduce.jar mobrefs mobrefs-report-output some_table foo
...
19/12/10 11:38:47 INFO impl.YarnClientImpl: Submitted application application_1575695902338_0004
19/12/10 11:38:47 INFO mapreduce.Job: The url to track the job: https://rm-2.example.com:8090/proxy application_1575695902338_0004/
19/12/10 11:38:47 INFO mapreduce.Job: Running job: job_1575695902338_0004
19/12/10 11:38:57 INFO mapreduce.Job: Job job_1575695902338_0004 running in uber mode : false
19/12/10 11:38:57 INFO mapreduce.Job: map 0% reduce 0%
19/12/10 11:39:07 INFO mapreduce.Job: map 7% reduce 0%
19/12/10 11:39:17 INFO mapreduce.Job: map 13% reduce 0%
19/12/10 11:39:19 INFO mapreduce.Job: map 33% reduce 0%
19/12/10 11:39:21 INFO mapreduce.Job: map 40% reduce 0%
19/12/10 11:39:22 INFO mapreduce.Job: map 47% reduce 0%
19/12/10 11:39:23 INFO mapreduce.Job: map 60% reduce 0%
19/12/10 11:39:24 INFO mapreduce.Job: map 73% reduce 0%
19/12/10 11:39:27 INFO mapreduce.Job: map 100% reduce 0%
19/12/10 11:39:35 INFO mapreduce.Job: map 100% reduce 100%
19/12/10 11:39:35 INFO mapreduce.Job: Job job_1575695902338_0004 completed successfully
19/12/10 11:39:35 INFO mapreduce.Job: Counters: 54
...
Map-Reduce Framework
Map input records=0
...
19/12/09 22:41:28 INFO mapreduce.MobRefReporter: Finished creating report for 'some_table', family='foo'
데이터가 성공적으로 마이그레이션되지 않았다면 이 보고서는 0이 아닌 입력 레코드 수와 mob cell 수를 모두 보여줘요.
$> HADOOP_CLASSPATH=/etc/hbase/conf:$(hbase mapredcp) yarn jar \
/some/path/to/hbase-shaded-mapreduce.jar mobrefs mobrefs-report-output some_table foo
...
19/12/10 11:44:18 INFO impl.YarnClientImpl: Submitted application application_1575695902338_0005
19/12/10 11:44:18 INFO mapreduce.Job: The url to track the job: https://busbey-2.gce.cloudera.com:8090 proxy/application_1575695902338_0005/
19/12/10 11:44:18 INFO mapreduce.Job: Running job: job_1575695902338_0005
19/12/10 11:44:26 INFO mapreduce.Job: Job job_1575695902338_0005 running in uber mode : false
19/12/10 11:44:26 INFO mapreduce.Job: map 0% reduce 0%
19/12/10 11:44:36 INFO mapreduce.Job: map 7% reduce 0%
19/12/10 11:44:45 INFO mapreduce.Job: map 13% reduce 0%
19/12/10 11:44:47 INFO mapreduce.Job: map 27% reduce 0%
19/12/10 11:44:48 INFO mapreduce.Job: map 33% reduce 0%
19/12/10 11:44:50 INFO mapreduce.Job: map 40% reduce 0%
19/12/10 11:44:51 INFO mapreduce.Job: map 53% reduce 0%
19/12/10 11:44:52 INFO mapreduce.Job: map 73% reduce 0%
19/12/10 11:44:54 INFO mapreduce.Job: map 100% reduce 0%
19/12/10 11:44:59 INFO mapreduce.Job: map 100% reduce 100%
19/12/10 11:45:00 INFO mapreduce.Job: Job job_1575695902338_0005 completed successfully
19/12/10 11:45:00 INFO mapreduce.Job: Counters: 54
...
Map-Reduce Framework
Map input records=1
...
MOB
NUM_CELLS=1
...
19/12/10 11:45:00 INFO mapreduce.MobRefReporter: Finished creating report for 'some_table', family='foo'
이런 일이 발생하면 MOB 압축이 비활성화되었는지, 충분히 큰 MOB 임계값을 선택했는지 확인하고 major compaction 단계를 다시 수행해야 해요.
컬럼 패밀리에 MOB 기능 비활성화 (Disable the MOB feature for the column family)
mobrefs 보고서가 MOB 시스템에 더 이상 데이터가 저장되지 않음을 보여주면 컬럼 패밀리 구성을 변경해 MOB 기능을 비활성화해도 안전해요.
hbase(main):017:0> alter 'some_table', {NAME => 'foo', IS_MOB => 'false'}
Updating all regions with the new schema...
8/25 regions updated.
25/25 regions updated.
Done.
0 row(s) in 2.9370 seconds
MOB 기능은 컬럼 패밀리를 변경하고 major compaction을 수행한 후에만 컬럼 패밀리에서 비활성화돼요. 컬럼 패밀리를 변경한 후 major compaction을 수행하기 전에는 MOB cell이 여전히 MOB 저장소에 존재해요.
컬럼 패밀리가 더 이상 MOB 기능 활성화를 표시하지 않으면 MOB 유지 관리 chores를 다시 시작해도 안전해요. 구성 파일에서 제거해 hbase.mob.compaction.chore.period에 기본값을 사용하거나, 이 프로세스를 시작하기 전에 가졌던 커스텀 값으로 복원할 수 있어요.
잔여 MOB 데이터 정리 (Clean up residual MOB data)
컬럼 패밀리에서 MOB 기능이 비활성화되면 이 컬럼 패밀리에 특정한 MOB 저장 영역에서 데이터를 찾는 내부 HBase 프로세스는 없을 거예요. 값이 HBase의 데이터 영역으로 다시 쓰인 압축 프로세스 이전의 데이터가 여전히 그곳에 존재해요. HBase 슈퍼유저로 HDFS에서 이 잔여 데이터를 직접 확인할 수 있어요.
$ hdfs dfs -count /hbase/mobdir/data/default/some_table
4 54 9063269081 /hbase/mobdir/data/default/some_table
이 데이터는 잡스러운(spurious) 것이며 회수될 수 있어요. 일단 격리(sideline)하고, 애플리케이션의 테이블 보기를 확인한 다음, 삭제해야 해요.
MOB 임계값보다 큰 데이터 값이 비-MOB hfile에 저장된 것으로 나타남
벌크 로드와 WAL split-to-HFile은 MOB 임계값을 고려하지 않고 데이터를 일반 hfile(/hbase/data 디렉터리 아래)에 기록해요.
이것은 기능상 문제를 일으키지 않으며, 다음 압축 중에 그러한 데이터는 MOB hfile로 기록될 거예요.
MOB 업그레이드 고려 사항 (MOB Upgrade Considerations)
일반적으로 MOB 기능으로 저장된 데이터는 HBase 업그레이드를 거쳐 투명하게 계속해서 올바르게 작동해야 해요.
"distributed MOB compaction" 기능이 있는 버전으로 업그레이드 (Upgrading to a version with the "distributed MOB compaction" feature)
HBASE-22749인 "Distributed MOB compactions" 작업 이전에, HBase는 Master가 MOB hfile의 모든 압축 유지 관리를 조정했어요. MOB 데이터 관리를 중앙 집중화하면 공간 최적화가 가능했지만, Region Server와 그 관리를 안전하게 조정하면 데이터 손실을 일으키는 엣지 케이스가 발생했어요(ref HBASE-22075).
HBASE-22749를 포함하는 HBase 버전으로 업그레이드하는 MOB 기능 사용자는 다음 변경 사항을 알아야 해요.
- MOB 시스템은 더 이상 "MOB Compaction Policies" 설정을 허용하지 않아요.
- MOB 시스템은 더 이상 해당 압축 정책에 따라 원래 cell 타임스탬프의 날짜(일별 등)로 MOB 값을 그룹화하려 하지 않아요.
- MOB 시스템은 더 이상 MOB 저장 영역에 _del 접미사의 특수 파일을 통해 개별 cell 삭제를 추적할 필요가 없어요. 업그레이드 후에는 이 파일들을 격리해야 해요.
- 기본 구성에서 MOB 시스템은 MOB 저장 값을 압축하는 데 훨씬 적은 시간이 걸려야 해요. 이는 HBase가 MOB 저장 값을 압축할 때 기본 파일시스템에 훨씬 더 큰 부하를 가한다는 사실의 직접적인 결과예요. 추가 부하는 region server 수의 배수(대략적인 크기)여야 해요. 즉, region server 3개와 master 2개의 클러스터에서 기본 구성은 Master가 처리하는 MOB 압축과 비교할 때 MOB 데이터를 다시 쓰는 major compaction 동안 HDFS에 3배의 부하를 가하고, 약 3배 더 빨라야 해요.
- MOB 시스템이 테이블에 MOB 데이터에 대한 참조를 가진 hfile이 있지만 참조 hfile에 아직 필요한 파일 수준 메타데이터가 없다고(즉, HBASE-22749 이전의 MOB 기능 사용에서) 감지하면, 그 테이블의 어떤 MOB hfile도 아카이브하지 않겠다고 거부할 거예요. Region Server가 수행하는 주기적 압축의 일반적인 과정은 기존 hfiles를 MOB 참조로 갱신하지만, 주어진 테이블이 필요한 압축을 거치기 전까지 운영자는 MOB 기능이 사용하는 저장 공간이 증가하는 것을 보게 될 것으로 기대해야 해요.
- "MOB" 유형으로 압축을 수행하는 것은 더 이상 MOB hfile만 특별히 압축하는 특수 처리가 없어요. 대신 경고를 발행하고 테이블의 압축을 수행할 거예요. 예를 들어 HBase shell을 다음과 같이 사용하면 Master 로그에 경고가 표시된 후 'example' 테이블 전체 또는 'big' 컬럼 각각에 대해 major compaction이 수행돼요. hbase> major_compact 'example', nil, 'MOB' hbase> major_compact 'example', 'big', 'MOB' Java API의 admin.majorCompact(TableName.valueOf("example"), CompactType.MOB)를 직접 사용하는 것도 마찬가지예요.
- 이와 유사하게 테이블이나 region에서 major compaction을 수동으로 수행하는 것도 해당 테이블 또는 region에 대해 MOB 저장 값을 압축하는 것을 처리해요.
다음 구성 설정은 폐기되고 다음과 같이 교체됐어요.
- hbase.master.mob.ttl.cleaner.period가 hbase.master.mob.cleaner.period로 교체됨
다음 구성 설정은 더 이상 사용되지 않아요.
- hbase.mob.compaction.mergeable.threshold
- hbase.mob.delfile.max.count
- hbase.mob.compaction.batch.size
- hbase.mob.compactor.class
- hbase.mob.compaction.threads.max
더 알아보기 (Learn more)
HBase 아키텍처, Regions, HDFS 등 MOB와 데이터 저장 구조 관련 문서를 이어서 보시길 권해요.