- Authors
- Name
- 들어가며
- 1. HBase란?
- 2. HBase 아키텍처
- 3. 데이터 모델
- 4. RowKey 설계 전략
- 5. HBase Shell 핵심 명령어
- 6. Java API 코드 예시
- 7. 읽기/쓰기 성능 최적화
- 8. Region Split과 Compaction
- 9. 모니터링과 관리
- 10. HBase vs Cassandra vs MongoDB
- 11. 트러블슈팅
- 12. 운영 체크리스트
- 마무리
들어가며
HBase는 Google Bigtable 논문을 기반으로 만들어진 분산 NoSQL 데이터베이스입니다. HDFS 위에서 동작하며, 수십억 행과 수백만 컬럼을 처리할 수 있는 대규모 랜덤 읽기/쓰기에 최적화되어 있습니다. 시계열 데이터, 로그 분석, 실시간 서빙 레이어 등 대용량 데이터를 낮은 지연 시간으로 접근해야 하는 시나리오에서 많이 사용됩니다.
이 글에서는 HBase의 핵심 개념부터 실전 운영 노하우까지 체계적으로 다룹니다.
1. HBase란?
핵심 특성
| 특성 | 설명 |
|---|---|
| 분산 | HDFS 위에서 수평 확장 가능 |
| Column-Family 기반 | 컬럼 패밀리 단위로 데이터 저장 |
| Versioned | 셀마다 여러 버전(타임스탬프) 저장 가능 |
| 강한 일관성 | 단일 행에 대해 원자적 읽기/쓰기 보장 |
| 자동 샤딩 | Region 단위로 데이터 자동 분배 |
| 높은 처리량 | 수십만 QPS 처리 가능 |
언제 HBase를 사용하는가?
적합한 경우:
- 수십억 행 이상의 대규모 데이터
- 빠른 랜덤 읽기/쓰기가 필요한 경우 (< 10ms)
- 시계열 데이터 (IoT 센서, 로그, 메트릭)
- 넓은 테이블 (수백~수천 컬럼)
- HDFS와 통합이 필요한 경우
부적합한 경우:
- 수백만 행 이하의 작은 데이터셋
- 복잡한 JOIN, 트랜잭션이 필요한 경우
- 전문 검색 (Elasticsearch가 더 적합)
- Ad-hoc 분석 쿼리 (Hive, Spark SQL이 더 적합)
2. HBase 아키텍처
전체 구조
┌────────────────────────────────────────────────────────┐
│ Client │
│ (HBase Shell, Java API, REST, Thrift) │
└───────────────────────┬────────────────────────────────┘
│
┌───────▼───────┐
│ ZooKeeper │
│ (coordination│
│ & discovery)│
└───┬───────┬───┘
│ │
┌───────▼──┐ ┌─▼──────────┐
│ HMaster │ │ HMaster │
│ (Active) │ │ (Standby) │
└───────┬───┘ └────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌────▼─────┐ ┌────▼─────┐ ┌────▼─────┐
│RegionSvr │ │RegionSvr │ │RegionSvr │
│┌────────┐│ │┌────────┐│ │┌────────┐│
││Region A││ ││Region C││ ││Region E││
│├────────┤│ │├────────┤│ │├────────┤│
││Region B││ ││Region D││ ││Region F││
│└────────┘│ │└────────┘│ │└────────┘│
└──────────┘ └──────────┘ └──────────┘
│ │ │
└──────────────┼──────────────┘
│
┌──────▼──────┐
│ HDFS │
│ (Storage) │
└─────────────┘
구성 요소별 역할
| 구성 요소 | 역할 | 장애 시 영향 |
|---|---|---|
| HMaster | Region 할당, DDL 처리, 부하 분산 | DDL 불가, 자동 밸런싱 중단 (읽기/쓰기는 가능) |
| RegionServer | 데이터 읽기/쓰기 처리, Region 관리 | 해당 Region 접근 불가 (자동 복구) |
| ZooKeeper | HMaster 선출, RS 상태 추적, 메타 위치 | 전체 클러스터 중단 |
| HDFS | 실제 데이터 영구 저장 | 데이터 손실 위험 |
Region 내부 구조
┌─────────────── Region ──────────────────┐
│ │
│ ┌─── Column Family: cf1 ─────────────┐ │
│ │ ┌──────────┐ │ │
│ │ │ MemStore │ (메모리, 쓰기 버퍼) │ │
│ │ └──────────┘ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ HFile 1 │ │ HFile 2 │ │ │
│ │ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌─── Column Family: cf2 ─────────────┐ │
│ │ ┌──────────┐ │ │
│ │ │ MemStore │ │ │
│ │ └──────────┘ │ │
│ │ ┌──────────┐ │ │
│ │ │ HFile 1 │ │ │
│ │ └──────────┘ │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ WAL (Write-Ahead Log) │ │
│ └──────────────────────────────────┘ │
└──────────────────────────────────────────┘
쓰기 경로 (Write Path)
1. Client → RegionServer에 Put 요청
2. WAL(Write-Ahead Log)에 기록 (HDFS에 저장, 장애 복구용)
3. MemStore에 기록 (메모리)
4. Client에 성공 응답
5. MemStore가 가득 차면 → HFile로 Flush (HDFS에 저장)
읽기 경로 (Read Path)
1. Client → RegionServer에 Get 요청
2. Block Cache 확인 (메모리)
3. MemStore 확인 (메모리)
4. HFile 확인 (디스크, Bloom Filter로 빠르게 필터링)
5. 결과 병합 (최신 버전 반환)
3. 데이터 모델
논리적 데이터 모델
Table: user_activity
─────────────────────────────────────────────────────────────────
RowKey | Column Family: info | Column Family: stats
| name | email | login_count | last_login
─────────────────────────────────────────────────────────────────
user001 | 김영주 | yj@email.com | 142 | 2026-03-08
user002 | 박서준 | sj@email.com | 87 | 2026-03-07
user003 | 이하나 | hn@email.com | 256 | 2026-03-08
─────────────────────────────────────────────────────────────────
물리적 저장 구조
# 실제로는 Column Family 단위로 별도 저장
# Key-Value 형태: (RowKey, CF:Qualifier, Timestamp) → Value
# Column Family: info
(user001, info:name, t3) → "김영주"
(user001, info:name, t1) → "김영주(구)" # 이전 버전
(user001, info:email, t2) → "yj@email.com"
(user002, info:name, t4) → "박서준"
(user002, info:email, t4) → "sj@email.com"
# Column Family: stats (별도 HFile)
(user001, stats:login_count, t5) → 142
(user001, stats:last_login, t5) → "2026-03-08"
핵심 용어 정리
| 용어 | 설명 | 비유 (RDBMS) |
|---|---|---|
| Table | 데이터 컨테이너 | Table |
| Row | RowKey로 식별되는 행 | Row |
| Column Family | 컬럼 그룹 (물리적 저장 단위) | (없음) |
| Column Qualifier | CF 내의 컬럼명 | Column |
| Cell | (Row, CF:Qualifier, Timestamp)의 값 | Cell |
| Timestamp | 셀의 버전 (기본: 밀리초) | (없음) |
| Region | 테이블의 수평 분할 단위 | Partition |
4. RowKey 설계 전략
RowKey 설계는 HBase 성능의 80%를 결정합니다.
핫스팟 문제
# 잘못된 RowKey: 순차적 키
# → 모든 쓰기가 마지막 Region에 집중 (핫스팟)
RowKey: 20260308_000001
RowKey: 20260308_000002
RowKey: 20260308_000003
↓ 모든 쓰기가 한 Region에 집중!
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Region 1 │ │ Region 2 │ │ Region 3 │
│ (한가함) │ │ (한가함) │ │ (과부하!) │
└──────────┘ └──────────┘ └──────────┘
해결 전략 1: Salting (접두사 해싱)
// 원래 RowKey: "20260308_user001"
// Salt 적용: hash("20260308_user001") % NUM_REGIONS + "_" + 원래키
int numRegions = 10;
String originalKey = "20260308_user001";
int salt = Math.abs(originalKey.hashCode() % numRegions);
String saltedKey = String.format("%02d_%s", salt, originalKey);
// 결과: "07_20260308_user001"
// 데이터가 Region에 균등 분배됨
// Region 0: 00_xxx, Region 1: 01_xxx, ... Region 9: 09_xxx
장점: 쓰기 부하 균등 분배 단점: 범위 스캔 시 모든 Region을 스캔해야 함
해결 전략 2: Key Reversing (키 뒤집기)
// 도메인 기반 RowKey를 뒤집어서 분산
// 원래: "com.google.www" → 뒤집기: "www.google.com"
// 원래: "com.google.mail" → 뒤집기: "liam.elgoog.moc"
// 타임스탬프 뒤집기
long reverseTimestamp = Long.MAX_VALUE - System.currentTimeMillis();
String rowKey = userId + "_" + reverseTimestamp;
// 최신 데이터가 먼저 정렬됨 (가장 흔한 쿼리 패턴)
해결 전략 3: Hashing
// MD5 해시의 앞 N자리를 prefix로 사용
String hashPrefix = DigestUtils.md5Hex(userId).substring(0, 4);
String rowKey = hashPrefix + "_" + userId + "_" + timestamp;
// 결과: "a3f2_user001_20260308120000"
RowKey 설계 원칙 요약
| 원칙 | 설명 |
|---|---|
| 짧게 유지 | RowKey는 모든 Cell에 반복 저장되므로 길면 저장 낭비 |
| 읽기 패턴 고려 | 가장 빈번한 쿼리에 맞춰 설계 |
| 핫스팟 방지 | 순차적/단조증가 키 사용 금지 |
| 버전 역순 | 최신 데이터를 먼저 읽으려면 reverse timestamp |
| 복합키 구분자 | _ 또는 \x00 사용 |
용도별 RowKey 예시
# 시계열 데이터 (IoT 센서)
salt_deviceId_reverseTimestamp
예: 03_sensor042_9223370449055775807
# 사용자 활동 로그
userId_reverseTimestamp_activityType
예: user001_9223370449055775807_login
# 웹 페이지 크롤링
reversedDomain_path_timestamp
예: moc.elgoog_/search_20260308
# 메시징 시스템
chatRoomId_reverseTimestamp_messageId
예: room001_9223370449055775807_msg12345
5. HBase Shell 핵심 명령어
테이블 관리
# HBase Shell 시작
hbase shell
# 테이블 생성
create 'user_activity', \
{NAME => 'info', VERSIONS => 3, COMPRESSION => 'SNAPPY', BLOOMFILTER => 'ROW'}, \
{NAME => 'stats', VERSIONS => 1, COMPRESSION => 'SNAPPY', TTL => 2592000}
# 테이블 목록
list
# 테이블 상세 정보
describe 'user_activity'
# 테이블 비활성화/삭제
disable 'user_activity'
drop 'user_activity'
# 테이블 구조 변경 (비활성화 필요)
disable 'user_activity'
alter 'user_activity', {NAME => 'info', VERSIONS => 5}
alter 'user_activity', {NAME => 'logs'} # 새 CF 추가
enable 'user_activity'
# Pre-split 테이블 생성 (핫스팟 방지)
create 'events', 'data', SPLITS => ['10', '20', '30', '40', '50', '60', '70', '80', '90']
데이터 CRUD
# Put (삽입/업데이트)
put 'user_activity', 'user001', 'info:name', '김영주'
put 'user_activity', 'user001', 'info:email', 'yj@email.com'
put 'user_activity', 'user001', 'stats:login_count', '142'
# Get (단일 행 조회)
get 'user_activity', 'user001'
get 'user_activity', 'user001', {COLUMN => 'info:name'}
get 'user_activity', 'user001', {COLUMN => 'info:name', VERSIONS => 3}
get 'user_activity', 'user001', {TIMERANGE => [1709856000000, 1709942400000]}
# Scan (범위 스캔)
scan 'user_activity'
scan 'user_activity', {LIMIT => 10}
scan 'user_activity', {STARTROW => 'user001', STOPROW => 'user010'}
scan 'user_activity', {COLUMNS => ['info:name', 'stats:login_count']}
scan 'user_activity', {FILTER => "SingleColumnValueFilter('info','name',=,'binary:김영주')"}
# Delete
delete 'user_activity', 'user001', 'info:email' # 특정 컬럼 삭제
deleteall 'user_activity', 'user001' # 전체 행 삭제
# Count (주의: 대량 데이터에서는 느림)
count 'user_activity'
count 'user_activity', INTERVAL => 100000
# Truncate
truncate 'user_activity'
관리 명령어
# 클러스터 상태
status
status 'detailed'
status 'simple'
# Region 관리
list_regions 'user_activity'
# 수동 Region Split
split 'user_activity', 'user500'
# Major Compaction (수동 실행, 피크 시간 피하기)
major_compact 'user_activity'
# Flush
flush 'user_activity'
# Balancer 상태
balancer_enabled
balance_switch true
6. Java API 코드 예시
연결 설정
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
// Configuration 생성
Configuration config = HBaseConfiguration.create();
config.set("hbase.zookeeper.quorum", "zk1,zk2,zk3");
config.set("hbase.zookeeper.property.clientPort", "2181");
// Connection (스레드 세이프, 애플리케이션 수명과 동일)
Connection connection = ConnectionFactory.createConnection(config);
// Table (스레드 세이프하지 않음, 사용 후 close)
Table table = connection.getTable(TableName.valueOf("user_activity"));
CRUD 작업
// Put (쓰기)
Put put = new Put(Bytes.toBytes("user001"));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("김영주"));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("email"), Bytes.toBytes("yj@email.com"));
put.addColumn(Bytes.toBytes("stats"), Bytes.toBytes("login_count"), Bytes.toBytes(142));
table.put(put);
// Batch Put (대량 쓰기)
List<Put> puts = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
Put p = new Put(Bytes.toBytes(String.format("user%06d", i)));
p.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"),
Bytes.toBytes("User " + i));
puts.add(p);
}
table.put(puts);
// Get (읽기)
Get get = new Get(Bytes.toBytes("user001"));
get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
Result result = table.get(get);
byte[] value = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name"));
System.out.println("Name: " + Bytes.toString(value));
// Scan (범위 스캔)
Scan scan = new Scan();
scan.withStartRow(Bytes.toBytes("user001"));
scan.withStopRow(Bytes.toBytes("user100"));
scan.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
scan.setCaching(500); // RPC당 반환 행 수
scan.setBatch(10); // 한 행에서 반환할 컬럼 수
try (ResultScanner scanner = table.getScanner(scan)) {
for (Result r : scanner) {
String rowKey = Bytes.toString(r.getRow());
String name = Bytes.toString(r.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")));
System.out.println(rowKey + ": " + name);
}
}
// Delete
Delete delete = new Delete(Bytes.toBytes("user001"));
delete.addColumn(Bytes.toBytes("info"), Bytes.toBytes("email"));
table.delete(delete);
// 자원 해제
table.close();
connection.close();
BufferedMutator (대량 비동기 쓰기)
BufferedMutatorParams params = new BufferedMutatorParams(TableName.valueOf("user_activity"))
.writeBufferSize(5 * 1024 * 1024); // 5MB 버퍼
try (BufferedMutator mutator = connection.getBufferedMutator(params)) {
for (int i = 0; i < 1000000; i++) {
Put put = new Put(Bytes.toBytes(String.format("user%08d", i)));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"),
Bytes.toBytes("User " + i));
mutator.mutate(put);
}
mutator.flush(); // 버퍼에 남은 데이터 전송
}
7. 읽기/쓰기 성능 최적화
쓰기 최적화
| 방법 | 설명 | 효과 |
|---|---|---|
| WAL 비활성화 | put.setDurability(Durability.SKIP_WAL) | 빠르지만 데이터 손실 위험 |
| 배치 Put | table.put(List<Put>) | 네트워크 왕복 감소 |
| BufferedMutator | 비동기 버퍼링 쓰기 | 대량 로딩에 최적 |
| Pre-split | 테이블 생성 시 Region 미리 분할 | 초기 핫스팟 방지 |
| BulkLoad | MapReduce로 HFile 직접 생성 | 초대량 데이터 로딩 |
| 압축 | SNAPPY, LZ4 사용 | I/O 감소 |
읽기 최적화
| 방법 | 설명 | 효과 |
|---|---|---|
| Block Cache | LRU + Bucket Cache 설정 | 자주 읽는 블록 캐싱 |
| Bloom Filter | ROW 또는 ROWCOL 레벨 | 불필요한 HFile 읽기 방지 |
| Scan Caching | scan.setCaching(500) | RPC 호출 감소 |
| Column 지정 | 필요한 컬럼만 조회 | I/O 감소 |
| Coprocessor | 서버사이드 처리 | 네트워크 트래픽 감소 |
| Short-circuit Read | 로컬 DataNode 직접 읽기 | HDFS 오버헤드 제거 |
BulkLoad 예시
# 1. CSV를 HFile로 변환 (MapReduce)
hbase org.apache.hadoop.hbase.mapreduce.ImportTsv \
-Dimporttsv.separator=',' \
-Dimporttsv.columns='HBASE_ROW_KEY,info:name,info:email,stats:login_count' \
-Dimporttsv.bulk.output='/tmp/hbase-bulkload' \
user_activity \
/input/users.csv
# 2. HFile을 HBase에 로드
hbase org.apache.hadoop.hbase.tool.LoadIncrementalHFiles \
/tmp/hbase-bulkload \
user_activity
8. Region Split과 Compaction
Region Split
# Region이 일정 크기에 도달하면 자동 분할
# 기본 분할 정책: IncreasingToUpperBoundRegionSplitPolicy
# Region 크기 계산:
# min(r^2 * memstore.flush.size * 2, hbase.hregion.max.filesize)
# r = 같은 테이블의 같은 RS에 있는 Region 수
# 수동 설정
hbase.hregion.max.filesize = 10737418240 # 10GB
# 분할 과정:
# 1. 분할 지점(midpoint) 결정
# 2. 두 개의 새 Region(daughter) 생성
# 3. 기존 Region 비활성화
# 4. 메타 테이블 업데이트
# 5. 새 Region 활성화
# 6. 기존 Region 정리 (compaction 후)
Compaction
# Minor Compaction
# - 작은 HFile들을 더 큰 HFile로 병합
# - 삭제 마커(tombstone) 유지
# - 자동으로 수시 실행
# Major Compaction
# - 모든 HFile을 하나의 HFile로 병합
# - 삭제 마커 제거, 만료된 버전 제거
# - 디스크 I/O 많음 → 피크 시간 피하기
# - 기본 7일 주기 자동 실행
# 수동 Major Compaction
major_compact 'user_activity'
# Major Compaction 자동 실행 비활성화
# hbase-site.xml
# hbase.hregion.majorcompaction = 0
# → cron으로 비피크 시간에 수동 실행
<!-- hbase-site.xml Compaction 설정 -->
<configuration>
<!-- Minor Compaction 트리거 임계값 -->
<property>
<name>hbase.hstore.compactionThreshold</name>
<value>3</value> <!-- HFile 3개 이상이면 Minor Compaction -->
</property>
<!-- Major Compaction 주기 (0 = 비활성화) -->
<property>
<name>hbase.hregion.majorcompaction</name>
<value>604800000</value> <!-- 7일, 0으로 설정 시 비활성화 -->
</property>
<!-- MemStore Flush 크기 -->
<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value> <!-- 128MB -->
</property>
</configuration>
9. 모니터링과 관리
핵심 모니터링 메트릭
| 카테고리 | 메트릭 | 경고 기준 |
|---|---|---|
| RegionServer | GC Pause Time | > 5초 |
| RegionServer | Heap Used % | > 80% |
| RegionServer | Compaction Queue Size | > 10 (지속) |
| RegionServer | MemStore Size | Flush 임계값의 90% |
| Region | Request Count (R/W) | 특정 Region에 집중 |
| Region | Store File Count | > 10 (Compaction 필요) |
| HMaster | Dead RegionServers | > 0 |
| HMaster | RIT (Regions in Transition) | > 0 (지속) |
HBase Web UI
# HMaster UI: http://hmaster:16010
# RegionServer UI: http://regionserver:16030
# JMX 메트릭 확인
curl http://regionserver:16030/jmx?qry=Hadoop:service=HBase,name=RegionServer,sub=Server
운영 스크립트
#!/bin/bash
# HBase 클러스터 상태 체크 스크립트
echo "=== HBase Cluster Status ==="
echo "status" | hbase shell 2>/dev/null | grep -E "servers|dead|regions"
echo ""
echo "=== Region Distribution ==="
echo "status 'detailed'" | hbase shell 2>/dev/null | grep -E "regionserver|regions="
echo ""
echo "=== Table Sizes ==="
for table in $(echo "list" | hbase shell 2>/dev/null | grep -v "TABLE\|row(s)"); do
size=$(hdfs dfs -du -s -h /hbase/data/default/$table 2>/dev/null | awk '{print $1 $2}')
echo "$table: $size"
done
10. HBase vs Cassandra vs MongoDB
| 항목 | HBase | Cassandra | MongoDB |
|---|---|---|---|
| 데이터 모델 | Column-Family | Wide Column | Document (JSON) |
| 일관성 | 강한 일관성 | 튜닝 가능 (AP 기본) | 튜닝 가능 |
| 확장성 | 수평 확장 | 수평 확장 (뛰어남) | 수평 확장 (Sharding) |
| 쿼리 언어 | Scan/Get API | CQL (SQL-like) | MQL (JSON-like) |
| 2차 인덱스 | 제한적 | 기본 지원 | 풍부한 인덱스 |
| JOIN | 미지원 | 미지원 | $lookup (제한적) |
| 운영 복잡도 | 높음 (HDFS, ZK 필요) | 중간 | 낮음 |
| 적합한 워크로드 | 대규모 순차 스캔 + 랜덤 읽기 | 높은 쓰기 처리량 | 유연한 스키마, CRUD |
| 에코시스템 | Hadoop (Hive, Spark) | 독립적 | Atlas, Realm |
| 최대 데이터 규모 | PB급 | PB급 | TB~PB급 |
11. 트러블슈팅
Region Server 장애
# RegionServer 상태 확인
echo "status" | hbase shell
# 특정 RegionServer 로그 확인
tail -f /var/log/hbase/hbase-hbase-regionserver-hostname.log
# Region 재할당
hbase hbck -reassign <encoded-region-name>
# hbck2 (HBase 2.x)
hbase hbck -j /path/to/hbase-hbck2.jar assigns <encoded-region-name>
GC 튜닝
# hbase-env.sh
export HBASE_REGIONSERVER_OPTS="
-Xmx32g -Xms32g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+ParallelRefProcEnabled
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=65
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/var/log/hbase/gc-regionserver.log
"
핫스팟 Region 분리
# 특정 Region의 요청 수 확인
echo "status 'detailed'" | hbase shell | grep -A2 "requestsPerSecond"
# 핫스팟 Region 수동 분할
split 'ENCODED_REGION_NAME', 'split_key'
# 또는 테이블 레벨 분할
split 'user_activity', 'user500000'
RIT (Regions in Transition) 해결
# RIT 상태 확인
echo "status 'detailed'" | hbase shell | grep "transition"
# 강제 할당
hbase hbck -j /path/to/hbase-hbck2.jar assigns <region-encoded-name>
# 메타 테이블 복구
hbase hbck -j /path/to/hbase-hbck2.jar fixMeta
12. 운영 체크리스트
초기 설계 체크리스트
- RowKey 설계: 핫스팟 방지 전략 적용 (Salting, Hashing)
- Column Family 수 최소화 (2~3개 이하 권장)
- TTL 설정: 불필요한 데이터 자동 삭제
- VERSIONS 설정: 필요한 버전 수만 유지
- Pre-split: 예상 데이터 분포에 맞게 Region 분할
- Bloom Filter: ROW 또는 ROWCOL 설정
- 압축: SNAPPY 또는 LZ4 설정
- Coprocessor 필요 여부 검토
일상 운영 체크리스트
- RegionServer 힙 사용률 모니터링
- Compaction Queue 크기 확인
- GC Pause 시간 확인 (5초 이상 경고)
- Dead RegionServer 확인
- Region 핫스팟 확인
- HDFS 용량 확인
- ZooKeeper 상태 확인
정기 점검 체크리스트
- Major Compaction 비피크 시간에 실행
-
hbase hbck실행하여 테이블 무결성 확인 - Region 밸런싱 상태 확인
- 테이블별 디스크 사용량 추이 분석
- GC 로그 분석 및 튜닝
- 백업/복구 테스트 (Snapshot, ExportSnapshot)
- HBase, Hadoop 보안 패치 확인
마무리
HBase는 대규모 데이터를 낮은 지연 시간으로 처리해야 하는 시나리오에서 강력한 도구입니다. 하지만 RDBMS와는 완전히 다른 사고방식이 필요합니다.
핵심 정리:
- RowKey가 전부: 핫스팟 방지와 읽기 패턴에 맞는 설계가 핵심
- Column Family는 적게: 물리적 저장 단위이므로 2~3개 이하로 유지
- Compaction 관리: Major Compaction은 비피크 시간에 수동 실행
- 모니터링 필수: GC Pause, Region 분포, Compaction Queue 집중 감시
- 읽기/쓰기 패턴 분리: 대량 쓰기는 BulkLoad, 실시간 읽기는 Block Cache + Bloom Filter 활용