인메모리 컴팩션

인메모리 컴팩션 (In-memory Compaction)

일명 Accordion이라 불리는 기능으로, 데이터가 RAM에 있는 동안 MemStore를 압축해서 쓰기 증폭과 디스크 사용량을 줄이는 기법이에요. HBase 2.0.0에서 소개된 신기능이에요.

출처: 문서

본문

개요 (Overview)

In-memory Compaction(A.K.A Accordion)은 hbase-2.0.0의 신기능이에요. Apache HBase 블로그의 Accordion: HBase Breathes with In-Memory Compaction에서 처음 소개됐어요. 블로그를 인용하면:

Accordion은 HBase가 기반한 설계 패턴인 LSM 원리 [Log-Structured-Merge Tree]를 MemStore에 다시 적용해서, 데이터가 아직 RAM에 있는 동안 중복(redundancy)과 기타 오버헤드를 제거해요. 이렇게 하면 HDFS로의 flush 빈도가 줄어들어 쓰기 증폭(write amplification)과 전체 디스크 사용량이 감소해요. flush가 적어지면 MemStore가 넘치는 상황에서 쓰기 작업이 지연되는 빈도도 줄어들어 쓰기 성능이 개선돼요. 디스크에 데이터가 적다는 것은 블록 캐시의 압박도 줄어들고, hit rate가 높아지며, 결과적으로 읽기 응답 시간도 개선된다는 뜻이에요. 마지막으로 디스크 쓰기가 적다는 것은 백그라운드에서 발생하는 컴팩션이 적다는 뜻이기도 해요. 즉, 생산적인(읽기·쓰기) 작업에서 빼앗기는 사이클이 줄어들어요. 종합하면, 인메모리 컴팩션의 효과는 시스템 전체를 더 빠르게 움직이게 만드는 촉매로 볼 수 있어요.

개발자 관점의 설명은 Accordion: Developer View of In-Memory Compaction에서 볼 수 있어요.

인메모리 컴팩션은 데이터 변경(churn)이 많은 경우, 즉 overwrite나 over-version이 데이터가 아직 메모리에 있는 동안 제거될 수 있을 때 가장 잘 동작해요. 쓰기가 전부 unique라면 쓰기 처리량이 떨어질 수 있어요(인메모리 컴팩션은 CPU를 소모해요). 운영 배포 전에 테스트하고 비교해 볼 것을 권장해요.

이 섹션에서는 Accordion을 활성화하는 방법과 사용 가능한 설정을 설명해요.

활성화 (Enabling)

인메모리 컴팩션을 활성화하려면 해당 동작을 원하는 각 컬럼 패밀리에 IN_MEMORY_COMPACTION 속성을 설정하세요. IN_MEMORY_COMPACTION 속성은 다음 네 가지 값 중 하나를 가질 수 있어요.

  • NONE: 인메모리 컴팩션 없음.
  • BASIC: 기본 정책으로 flush를 활성화하고 pipeline 최대 임계값에 도달할 때까지 flush 파이프라인을 유지한 뒤 디스크로 flush해요. 인메모리 컴팩션은 없지만, 데이터를 낭비가 심한 네이티브 ConcurrentSkipListMap 데이터 타입에서 더 컴팩트(그리고 효율적인) 데이터 타입으로 옮기면서 처리량을 높일 수 있어요.
  • EAGER: BASIC 정책에 더해 flush의 인메모리 컴팩션(hfile에 적용되는 온디스크 컴팩션과 유사)을 수행해요. 컴팩션 시 온디스크 규칙을 적용해서 버전, 중복, ttl 만료 셀 등을 제거해요.
  • ADAPTIVE: 적응형 컴팩션으로 워크로드에 맞춰 조정돼요. 데이터의 중복 셀 비율에 따라 인덱스 컴팩션이나 데이터 컴팩션을 적용해요. 실험적(Experimental) 기능이에요.

radish 테이블의 info 컬럼 패밀리에 BASIC을 활성화하려면 info 컬럼 패밀리에 속성을 추가하세요.

hbase(main):003:0> alter 'radish', {NAME => 'info', IN_MEMORY_COMPACTION => 'BASIC'} Updating all regions with the new schema... All regions updated. Done. Took 1.2413 seconds hbase(main):004:0> describe 'radish' Table radish is DISABLED radish COLUMN FAMILIES DESCRIPTION {NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536', METADATA => { 'IN_MEMORY_COMPACTION' => 'BASIC'}} 1 row(s) Took 0.0239 seconds

IN_MEMORY_COMPACTION 속성이 METADATA 맵의 일부로 표시되는 점에 주목하세요.

또한 hbase-site.xml 파일에 설정할 수 있는 전역 설정 hbase.hregion.compacting.memstore.type도 있어요. 이것으로 새 테이블 생성 시의 기본값을 설정할 수 있어요(컬럼 패밀리 Store 생성 시 먼저 컬럼 패밀리 설정에서 IN_MEMORY_COMPACTION 설정을 찾고, 없으면 hbase.hregion.compacting.memstore.type 값을 참조해요. 기본값은 NONE이에요).

기본적으로 새 HBase 시스템 테이블에는 NONE 인메모리 컴팩션이 설정돼요. 다른 값을 지정하려면 새 테이블 생성 시 hbase.systemtables.compacting.memstore.type을 BASIC / EAGER / ADAPTIVE로 설정하세요(주의: 시스템 테이블 생성 후 이 값을 설정해도 소급 적용되지 않아요. 테이블을 alter해서 인메모리 속성을 설정해야 해요).

인메모리 flush가 언제 발생하는지는 구성된 region flush 크기(테이블 디스크립터에 설정하거나 hbase.hregion.memstore.flush.size에서 읽음)를 컬럼 패밀리 수로 나눈 뒤 hbase.memstore.inmemoryflush.threshold.factor를 곱해서 계산해요. 기본값은 0.014예요.

파이프라인이 수행하는 flush 수는 memstore 크기 경계 내에 들어맞도록 모니터링되지만, hbase.hregion.compacting.pipeline.segments.limit를 설정해서 총 flush 수의 최대값을 지정할 수도 있어요. 기본값은 2예요.

컬럼 패밀리 Store가 생성되면 어떤 memstore 타입이 적용되는지 표시돼요. 이 글을 쓰는 시점에는 ConcurrentSkipListMap을 채운 뒤 디스크로 flush하는 구식 DefaultMemStore가 있고, 아니면 이 새로운 인메모리 컴팩션 기능을 제공하는 구현인 새로운 CompactingMemStore가 있어요. 다음은 family라는 이름의 컬럼 패밀리 Store가 CompactingMemStore를 사용하도록 설정된 RegionServer의 로그 라인이에요.

2018-03-30 11:02:24,466 INFO [Time-limited test] regionserver.HStore(325): Store=family, memstore type=CompactingMemStore, storagePolicy=HOT, verifyBulkLoads=false, parallelPutCountPrintThreshold=10

CompactingMemStore 클래스(org.apache.hadoop.hbase.regionserver.CompactingMemStore)에 TRACE 수준 로깅을 활성화하면 동작 상세를 볼 수 있어요.

더 알아보기 (Learn more)