HBase는 Row Key라는 단일 인덱스로 데이터를 검색한다.
데이터의 위치와 분산은 Row Key에 의해 결정되므로, Row Key는 검색 성능뿐만 아니라 클러스터의 전체 상태에도 직접적인 영향을 미친다.
따라서 Row Key는 활용 목적에 맞게 신중하게 정의해야 하며, 클러스터에 부정적인 영향을 줄 수 있는 설계 방식은 피해야 한다.
HBase의 Row Key는 사전순(lexicographical)으로 정렬된다.
이 덕분에 특정 범위의 데이터를 빠르게 검색하거나, 관련된 row들을 물리적으로 가까운 위치에 저장할 수 있다.
하지만 이 특성이 Row Key를 잘못 설계했을 때 성능 병목(Hotspotting)으로 이어질 수 있다.
Hotspot은 다수의 클라이언트 요청이 특정 RegionServer에 집중되는 현상이다.
즉, 클러스터 전체 자원이 고르게 활용되지 못하고 일부 노드만 과부하 상태에 빠진다.
클러스터를 완벽하고 고르게 활용할 수 있도록 데이터 액세스 패턴을 설계하는 것이 중요하다.
예를 들어:
user_00000001
, user_00000002
, … 처럼 순차 증가 값을 Row Key 앞부분에 두면2025-09-01-00:00:00
같은 시간 기반 prefix를 쓰면즉, Row Key의 “앞부분”이 비슷하거나 동일하게 몰리면 Hotspot이 발생한다.
Row Key 분산 설계
논리적 인접성과 물리적 분산의 균형
Salting 은 주로 암호화 할 때 사용되는 용어/기법이다. 랜덤한 숫자나 문자를 붙이는 것을 말한다.
원래 사용하려고 했던 rowkey 앞에 정해진 문자 (e.g. 알파벳)을 랜덤하게 붙이는 방식이다.
이 방법은 통해 데이터가 random 하게 Region 에 고르게 퍼지는 것을 기대할 수 있다.
예 - original keys
foo0001
foo0002
foo0003
foo0004
예 - salting 적용
a-foo0003
b-foo0001
c-foo0003
c-foo0004
d-foo0002
Salting 방법은 write 에 대한 throughput 을 높일수는 있지만, read 에서는 여러 리전에 걸쳐 읽어야 하므로 성능이 떨어질 수 있다.
또한 random 하게 부여한 salt 값을 read 할 때 어떻게 알게 할 것인지는 추가로 고민이 필요하다.
Range scan 이 필요한 데이터에는 적합하지 않다. 단일 데이터를 위주로 조회 (예: user 정보와 같은 metadata 종류)하는 경우에 사용하는 것이 적합하다.
Row key 에 들어갈 원래 값을 Hashing 한 값을 row key로 사용하는 방법이다.
주어진 Row가 항상 동일한 접두사로 "salt"되어 로드를 RegionServer에 분산시키고, 읽기를 예측 가능하다는 것이 장점이다.
결정론적 해시를 사용하면 클라이언트가 전체 Row Key를 재구성하고 Get 작업을 사용하여 해당 Row을 정상적으로 검색할 수 있다.
Hasing 은 또한 더 적은 수의 용량으로 더 많은 경우의 수를 표현할 수 있다는 장점도 있다.
fixed-width row key
또는 numeric row key
를 least significant digit 을 앞에 두어 revesre 하는 것이다.
장점: 일부 분산 효과, 단순한 random 효과
단점: 숫자 기반 ordering 깨짐 → scan 연산 주의 필요
단방향으로 증가하는 rowkey는 HBase Row Key의 Anti-Pattern 이다.
가장 대표적인 예로 timestamp 를 맨 앞에 위치시키는 방식이 있다.
Facebook의 타임라인을 예로 들어보자.
사용자가 소비하는 콘텐츠는 대부분 최신 데이터이다.
즉, 지난 1시간, 지난 1일 등 최근 시점의 timestamp가 Row Key 맨 앞에 오면,
해당 시점에 해당하는 Row들이 특정 Region에 몰리게 된다.
결과적으로 읽기 요청이 모두 동일한 RegionServer에 집중되어 Hotspot이 발생한다
이는 HBase에서 발생할 수 있는 최악의 운영 시나리오다.
문제는, timestamp 기반 Row Key가 무조건 나쁘다는 것이 아니다.
Range Scan이 필요한 경우(예: 최신 데이터 순 조회) timestamp는 매우 유용하다.
핵심은 Row Key의 앞부분에 분산 요소(prefix)를 넣는 것이다.
예시
user_id : timestamp : 기타정보
user_id
내에서는 timestamp 순서가 유지되므로 타임라인 조회도 가능HBase에서 Value는 단독으로 존재하지 않고, 항상 해당 Cell의 좌표 정보(coordinates = Row, Column name, Timestamp)와 함께 저장되고 전송된다.
coordinates = Row Key + Column Name + Timestamp
즉, Cell Value가 시스템을 통과할 때는 Value 자체뿐 아니라 좌표 정보(coordinates)가 반드시 포함된다.
문제는 좌표 정보(coordinates)의 크기가 Value 크기와 비교해 상대적으로 클 경우 발생한다.
Row Key & Column Name 최소화
StoreFile 블록 크기 조정
압축 사용 시 주의
대부분의 경우 이러한 비효율성은 치명적이지 않다.
그러나 Column Family, Attributes, Row Key 설계 패턴은 데이터 전체에 걸쳐 수십억 번 반복되는 coordinates 구조에 영향을 주므로, 초기 설계를 잘하는 것이 장기적인 효율성에 큰 기여를 한다.
대부분은 키 사이즈 자체를 줄이는 것이 성능 향상에 가장 좋다.
HBase는 Row Key를 사전순(lexicographical order)으로 정렬한다.
이 특성을 활용하여 최신 데이터를 항상 앞쪽에 위치하도록 설계하는 방법이 Reverse Timestamp이다.
Row Key의 끝부분에 Reverse Timestamp 값을 붙인다.
Long.MAX_VALUE - timestamp
예
rowkey = user_id + ":" + (Long.MAX_VALUE - timestamp)
이렇게 하면 동일한 user_id 안에서 최신 데이터가 가장 작은 값으로 평가되어 Row Key 순 정렬 시 앞쪽에 위치한다
최신 데이터 우선 조회
과거 버전 유지 가능
Range Scan 최적화
Reverse Scan 지원 여부
Scan.setReversed(true)
API가 제공된다.Row Key 길이 증가
Long.MAX_VALUE - timestamp
는 64bit 숫자이므로 Row Key가 늘어나고, coordinates 크기 문제가 발생할 수 있다(→ 4.3 참고).범용성 부족
Reverse scan 을 지원하는 버전에서는 굳이 Reverse Timestamp 를 사용하지 않더라도 된다.