TiDB — TiKV와 TiFlash로 구현하는 HTAP
TiDB의 HTAP 아키텍처를 핵심 구성요소(TiDB SQL 노드, TiKV 행 스토리지, TiFlash 열 스토리지, PD 조율자)별로 설명하고, OLTP와 OLAP 쿼리를 같은 데이터베이스에서 처리하는 방식을 코드와 함께 다룹니다.
지난 글에서 CockroachDB의 Raft 기반 분산 아키텍처를 살펴봤다. 이번에는 다른 접근 방식을 택한 분산 SQL, TiDB를 다룬다. TiDB의 핵심 차별점은 HTAP(Hybrid Transactional and Analytical Processing)이다. 같은 데이터에 대해 OLTP와 OLAP 쿼리를 별도 데이터 이동 없이 동시에 처리한다.
TiDB 핵심 구성요소
TiDB 클러스터는 크게 세 가지 역할로 나뉜다.
TiDB SQL 노드: MySQL 5.7 프로토콜과 호환되는 SQL 레이어다. Stateless 설계라 수평 확장이 자유롭고, 실패 시 다른 TiDB 노드가 즉시 대체한다. 쿼리 파싱, 최적화, 실행 계획 생성을 담당한다.
PD (Placement Driver): 클러스터 메타데이터를 관리하고, 글로벌 트랜잭션 타임스탬프(TSO)를 발급한다. TiKV의 Region(데이터 조각) 스케줄링과 부하 분산도 PD가 담당한다.
TiKV: 행 기반(row-oriented) 분산 키-값 스토리지다. 데이터를 96MB 크기의 Region으로 나눠 클러스터에 분산하고, 각 Region마다 Raft 그룹으로 복제한다. OLTP 쿼리의 기본 스토리지다.
TiFlash: 열 기반(columnar) 스토리지로, TiKV의 Raft Learner로 동작한다. TiKV에 쓰인 데이터를 비동기적으로 복제받아 열 형식으로 저장한다. 대규모 집계와 범위 스캔에 최적화돼 있다.
HTAP: 같은 데이터, 두 가지 엔진
전통적인 OLTP + OLAP 아키텍처는 ETL로 분리된 데이터 웨어하우스가 필요하다. TiDB는 TiFlash가 TiKV의 Raft Learner로서 실시간으로 복제를 받기 때문에, 추가 파이프라인 없이 분석 쿼리를 신선한 데이터로 실행할 수 있다.
-- TiFlash 활성화 (테이블별 설정)
ALTER TABLE orders SET TIFLASH REPLICA 2;
-- 복제 상태 확인
SELECT TABLE_NAME, REPLICA_COUNT, AVAILABLE
FROM information_schema.tiflash_replica
WHERE TABLE_NAME = 'orders';
-- 옵티마이저가 자동으로 TiFlash 선택 (분석 쿼리)
SELECT product_id, SUM(amount) AS revenue
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY product_id
ORDER BY revenue DESC
LIMIT 20;
-- EXPLAIN 출력에 "cop[tiflash]" 확인
옵티마이저는 쿼리 패턴을 분석해 포인트 조회는 TiKV, 집계·범위 스캔은 TiFlash로 자동 라우팅한다. 힌트(READ_FROM_STORAGE)로 명시적으로 지정할 수도 있다.
Percolator 기반 분산 트랜잭션
TiDB는 Google Percolator 논문을 기반으로 한 낙관적 트랜잭션 모델을 사용한다. 모든 읽기는 TSO에서 받은 타임스탬프 기준의 스냅샷을 읽는다. 쓰기는 Two-Phase Lock 방식으로 진행되지만, TiKV 내에서 분산 처리된다.
-- TiDB 트랜잭션 (MySQL 호환)
BEGIN;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;
COMMIT;
-- 비관적 잠금 모드 (MySQL과 동일한 동작)
SET tidb_txn_mode = 'pessimistic';
BEGIN;
-- FOR UPDATE 즉시 락 획득
SELECT * FROM inventory WHERE id = 100 FOR UPDATE;
UPDATE inventory SET qty = qty - 1 WHERE id = 100;
COMMIT;
기본값은 낙관적 모드지만, 쓰기 충돌이 많은 워크로드에는 비관적 모드가 성능상 유리할 수 있다.
Region과 핫스팟 문제
TiKV의 Region은 기본 96MB이며, 크기 초과 시 자동 분할된다. 순차 INSERT가 많으면 마지막 Region에 쓰기가 집중되는 핫스팟이 발생한다. TiDB는 이를 해결하기 위해 AUTO_RANDOM PK를 제공한다.
-- 핫스팟 방지: AUTO_RANDOM (내부적으로 값 샤딩)
CREATE TABLE orders (
id BIGINT AUTO_RANDOM PRIMARY KEY,
user_id BIGINT NOT NULL,
amount DECIMAL(12,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- AUTO_RANDOM은 값을 랜덤하게 배분해 쓰기가 여러 Region에 분산됨
-- AUTO_INCREMENT와 달리 last_insert_id()가 다를 수 있음에 주의
TiDB vs CockroachDB 선택 기준
TiDB를 선택할 때: MySQL을 사용하고 있고 분산 확장이 필요한 경우, 별도 데이터 파이프라인 없이 OLTP+OLAP를 함께 처리해야 하는 경우, 중국·아시아 기업 생태계와 연동이 필요한 경우.
CockroachDB를 선택할 때: PostgreSQL 생태계를 선호하는 경우, 글로벌 멀티 리전 배포에서 행 단위 지역 핀닝이 필요한 경우, 강한 일관성(Serializable)이 기본값으로 필요한 경우.
두 시스템 모두 Raft를 사용하는 분산 SQL이지만, TiDB는 HTAP과 MySQL 호환성, CockroachDB는 멀티 리전과 PostgreSQL 호환성에서 강점을 보인다.
지난 글: CockroachDB — 분산 SQL의 실전 구현
다음 글: YugabyteDB — PostgreSQL과 Cassandra의 분산 결합
읽어주셔서 감사합니다. 😊