Ken
들어가며
ClickHouse Cloud는 Stateless 아키텍처를 기반으로 설계되었습니다. Compute 노드가 데이터를 로컬에 저장하지 않고, Object Storage와 Distributed Cache를 통해 데이터에 접근하는 구조입니다. 이 아키텍처는 탁월한 확장성과 탄력성을 제공하지만, 한 가지 중요한 고려사항이 있습니다. 바로 쿼리 라우팅입니다.
HTTP 기반 연결에서는 각 요청이 로드 밸런서에 의해 다른 노드로 분산될 수 있습니다. 대부분의 경우 이는 문제가 되지 않지만, 특정 시나리오에서는 동일한 노드로 연결을 유지해야 하는 상황이 발생합니다. 이때 필요한 것이 Sticky Session (또는 Replica-aware Routing)입니다.
이 글에서는 ClickHouse Cloud의 Sticky Session 개념, 작동 원리, 활용 사례, 그리고 구현 방법까지 상세히 다룹니다.
- 들어가며
- 1. 왜 Sticky Session이 필요한가?
- 1.1 ClickHouse Cloud의 Stateless 아키텍처 이해
- 1.2 문제 상황: 노드별 독립 캐시
- 1.3 실제 시나리오에서의 영향
- 2. Sticky Session의 작동 원리
- 2.1 Replica-aware Routing 개요
- 2.2 호스트명 패턴
- 2.3 Ring Hash Load Balancing
- 3. 연결 방식별 Sticky 동작
- 3.1 Native TCP 연결 (포트 9440)
- 3.2 HTTP 연결 (포트 8443)
- 3.3 세션 ID를 통한 논리적 세션
- 4. Sticky Session 활용 사례
- 4.1 Cache Locality 최적화
- 4.2 Memory Table 워크로드
- 4.3 복잡한 ETL 파이프라인
- 4.4 Query Cache 최적화
- 5. 제약사항 및 주의사항
- 5.1 격리(Isolation)를 보장하지 않음
- 5.2 Private Link 환경에서의 추가 설정
- 5.3 부하 분산에 미치는 영향
- 5.4 활성화 방법
- 6. 구현 가이드: 클라이언트별 설정
- 6.1 Python (clickhouse-connect)
- 6.2 Python (clickhouse-driver, Native Protocol)
- 6.3 Node.js (@clickhouse/client)
- 6.4 Java (clickhouse-jdbc)
- 6.5 curl / HTTP 직접 호출
- 7. 모니터링 및 검증
- 7.1 연결된 노드 확인
- 7.2 캐시 상태 모니터링
- 7.3 연결 일관성 테스트
- 8. Best Practices
- 8.1 Sticky 접두사 네이밍 전략
- 8.2 Fallback 전략
- 8.3 Memory 테이블 사용 시 주의사항
- 결론
- 참고 자료
1. 왜 Sticky Session이 필요한가?
1.1 ClickHouse Cloud의 Stateless 아키텍처 이해
ClickHouse Cloud의 현대적인 아키텍처를 이해하면 Sticky Session의 필요성이 명확해집니다.
ClickHouse Cloud는 완전한 Stateless Compute 모델을 채택했습니다. 핵심 구성요소를 살펴보면 다음과 같습니다:
SharedMergeTree 엔진: 모든 테이블 데이터는 Object Storage(S3 등)에 저장됩니다. Compute 노드는 데이터를 로컬 디스크에 저장하지 않습니다.
Shared Catalog: 데이터베이스 메타데이터도 중앙 Keeper에 저장되어, 어떤 노드에서든 동일한 스키마와 테이블 정의에 접근할 수 있습니다.
Distributed Cache: Object Storage의 데이터를 캐싱하는 공유 캐시 레이어입니다. 한 노드가 읽은 데이터를 다른 노드도 활용할 수 있습니다.
Userspace Page Cache: 각 노드의 메모리에서 Hot 데이터를 캐싱합니다. 이 부분은 노드별로 독립적입니다.
1.2 문제 상황: 노드별 독립 캐시
Distributed Cache 덕분에 대부분의 데이터 캐시는 노드 간 공유됩니다. 하지만 여전히 노드별로 독립적인 요소들이 있습니다:
Userspace Page Cache (메모리 캐시): 가장 빠른 캐시 레이어지만, 각 노드의 RAM에만 존재합니다. 동일한 쿼리가 다른 노드로 라우팅되면 메모리 캐시를 재구축해야 합니다.
Query Cache: 쿼리 결과를 캐싱하는 기능으로, 노드별로 독립적으로 저장됩니다. 같은 쿼리가 다른 노드로 가면 캐시 미스가 발생합니다.
Memory Table Engine: RAM에 데이터를 저장하는 테이블로, 노드 간 복제가 되지 않습니다. 다른 노드로 라우팅되면 데이터가 보이지 않습니다.
Temporary Tables: 세션 기반 임시 테이블도 특정 노드의 메모리에만 존재합니다.
1.3 실제 시나리오에서의 영향
시나리오 1: 대시보드 쿼리 반복 실행
사용자가 대시보드를 열면 동일한 쿼리들이 반복적으로 실행됩니다. 만약 각 요청이 다른 노드로 분산된다면, 메모리 캐시와 Query Cache의 혜택을 받지 못해 응답 시간이 일관되지 않습니다.
시나리오 2: Memory 테이블 사용
Lookup 테이블이나 임시 계산 결과를 Memory 테이블에 저장한 경우, 쿼리가 다른 노드로 라우팅되면 해당 데이터에 접근할 수 없습니다.
시나리오 3: 세션 기반 작업
CREATE TEMPORARY TABLE로 생성한 임시 테이블이나, 세션 변수를 사용하는 복잡한 워크플로우에서는 동일 세션이 동일 노드에서 처리되어야 합니다.
2. Sticky Session의 작동 원리
2.1 Replica-aware Routing 개요
ClickHouse Cloud의 Sticky Session은 공식적으로 Replica-aware Routing이라 불립니다. Envoy Proxy의 Ring Hash Load Balancing을 활용하여 구현되었습니다.
기본 원리는 다음과 같습니다:
- 클라이언트가 특별한 패턴의 호스트명을 사용하여 연결합니다
- Envoy Proxy가 호스트명을 기반으로 해시값을 계산합니다
- 계산된 해시값에 따라 특정 ClickHouse 서버가 선택됩니다
- 동일한 호스트명을 사용하는 한, 항상 같은 서버로 연결됩니다
2.2 호스트명 패턴
ClickHouse Cloud 서비스의 기본 호스트명이 다음과 같다고 가정합니다:
abcxyz123.us-west-2.aws.clickhouse.cloud
Sticky Session을 사용하려면 다음 패턴의 호스트명을 사용합니다:
*.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud
예시:
user1.sticky.abcxyz123.us-west-2.aws.clickhouse.clouddashboard-team.sticky.abcxyz123.us-west-2.aws.clickhouse.cloudbatch-job-001.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud- 부분에 임의의 문자열을 지정할 수 있으며, 이 문자열이 라우팅 해시의 기반이 됩니다. 동일한 접두사를 사용하면 항상 동일한 노드로 연결됩니다.
2.3 Ring Hash Load Balancing
Envoy의 Ring Hash 알고리즘은 Consistent Hashing을 기반으로 합니다:
- 각 ClickHouse 서버가 해시 링 상의 여러 지점에 매핑됩니다
- 요청의 호스트명으로 해시값을 계산합니다
- 해시값에 가장 가까운 서버가 선택됩니다
- 서버 추가/제거 시에도 대부분의 기존 매핑이 유지됩니다
이 방식은 완벽한 세션 고정을 보장하지는 않지만, 서비스 변경이 없는 한 일관된 라우팅을 제공합니다.
3. 연결 방식별 Sticky 동작
3.1 Native TCP 연결 (포트 9440)
Native Protocol을 사용하는 TCP 연결은 기본적으로 Sticky한 특성을 가집니다.
# clickhouse-client는 TCP 연결 유지
clickhouse-client \
--host=abcxyz123.us-west-2.aws.clickhouse.cloud \
--port=9440 \
--secure \
--user=default \
--password=your_password
TCP 연결이 유지되는 동안 동일한 노드와 통신합니다. 이는 clickhouse-client나 Native Protocol을 사용하는 드라이버(Python의 clickhouse-driver, Go의 clickhouse-go 등)에서 자연스럽게 작동합니다.
장점:
- 별도 설정 없이 Sticky 연결 확보
- 연결 재사용으로 오버헤드 감소
- Progress 패킷, 압축 등 추가 기능 지원
제약:
- 연결이 끊어지면 다른 노드로 재연결될 수 있음
- HTTP 기반 도구/라이브러리와 호환 불가
3.2 HTTP 연결 (포트 8443)
HTTP 인터페이스는 각 요청이 독립적입니다. Keep-Alive를 사용하더라도, 로드 밸런서 레벨에서 요청이 분산될 수 있습니다.
기본 HTTP 연결 (Non-Sticky):
# 각 요청이 다른 노드로 라우팅될 수 있음
curl "https://abcxyz123.us-west-2.aws.clickhouse.cloud:8443" \
--user "default:password" \
--data-binary "SELECT hostName()"
Sticky HTTP 연결:
# 동일한 접두사 사용으로 같은 노드 보장
curl "https://my-session.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud:8443" \
--user "default:password" \
--data-binary "SELECT hostName()"
3.3 세션 ID를 통한 논리적 세션
HTTP에서도 session_id 파라미터를 사용하면 논리적 세션을 유지할 수 있습니다:
그러나 세션 ID만으로는 물리적 노드 고정이 보장되지 않습니다. 세션 ID는 ClickHouse 내부에서 논리적 세션을 식별하는 용도이며, 로드 밸런서의 라우팅에는 영향을 주지 않습니다. 완전한 세션 일관성을 위해서는 Sticky 호스트명과 세션 ID를 함께 사용해야 합니다.
4. Sticky Session 활용 사례
4.1 Cache Locality 최적화
가장 일반적인 활용 사례는 캐시 재사용률 향상입니다:
이 패턴을 사용하면:
- 같은 팀의 쿼리가 동일 노드에서 실행됨
- 해당 노드의 Userspace Page Cache가 팀별 워크로드에 최적화됨
- Query Cache 적중률 향상
4.2 Memory Table 워크로드
Memory 테이블을 사용하는 워크플로우에서는 Sticky Session이 필수입니다:
-- 세션 1: 데이터 준비 (노드 A)
CREATE TABLE lookup_cache (
key String,
value Float64
) ENGINE = Memory;
INSERT INTO lookup_cache VALUES ('rate_usd_krw', 1350.5);
-- 세션 2: 데이터 조회 (반드시 노드 A여야 함)
SELECT * FROM lookup_cache; -- Sticky 없이는 빈 결과 가능
Sticky 호스트명을 사용하면 두 쿼리가 동일 노드에서 실행되어 Memory 테이블 데이터에 정상 접근할 수 있습니다.
4.3 복잡한 ETL 파이프라인
여러 단계로 구성된 ETL 작업에서 중간 결과를 임시 테이블에 저장하는 경우:
4.4 Query Cache 최적화
반복되는 쿼리의 Query Cache 적중률을 높이려면:
-- Query Cache 활성화된 쿼리
SELECT
toStartOfHour(event_time) as hour,
count() as events
FROM events
WHERE date = today()
GROUP BY hour
SETTINGS use_query_cache = true;
이 쿼리가 Sticky Session 없이 여러 노드로 분산되면, 각 노드에서 별도로 Query Cache를 구축해야 합니다. Sticky Session을 사용하면 한 노드에서 캐시된 결과를 재사용할 수 있습니다.
5. 제약사항 및 주의사항
5.1 격리(Isolation)를 보장하지 않음
중요: Replica-aware Routing은 캐시 재사용 최적화를 위한 기능이지, 워크로드 격리를 보장하지 않습니다.
다음 상황에서 라우팅이 변경될 수 있습니다:
- 서버 재시작 (버전 업그레이드, 크래시 복구 등)
- 수직 확장 (Vertical Scaling)
- 수평 확장/축소 (Horizontal Scaling)
- 서비스 유지보수
Hash Ring이 변경되면, 동일한 호스트명이 다른 서버로 매핑될 수 있습니다. 따라서 데이터 영속성이 필요한 경우 Memory 테이블 대신 SharedMergeTree 또는 다른 영구 저장 엔진을 사용해야 합니다.
5.2 Private Link 환경에서의 추가 설정
Private Link를 사용하는 환경에서는 Sticky 호스트명 패턴에 대한 DNS 설정을 수동으로 추가해야 합니다:
# 예시: Route53 또는 내부 DNS 설정
*.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud → Private Link Endpoint IP
잘못된 설정은 로드 불균형을 초래할 수 있으므로 주의가 필요합니다.
5.3 부하 분산에 미치는 영향
Sticky Session을 과도하게 사용하면 특정 노드에 부하가 집중될 수 있습니다:
안티패턴:
# 모든 요청에 동일한 sticky 접두사 사용 - 한 노드에 부하 집중
host = "all-traffic.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud"
권장 패턴:
# 논리적 그룹별로 다른 접두사 사용
hosts = [
"shard-1.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud",
"shard-2.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud",
"shard-3.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud",
]
# 요청을 적절히 분산
5.4 활성화 방법
Replica-aware Routing은 현재 Private Preview 상태입니다. 활성화하려면 ClickHouse Cloud 지원팀에 문의해야 합니다.
6. 구현 가이드: 클라이언트별 설정
6.1 Python (clickhouse-connect)
6.2 Python (clickhouse-driver, Native Protocol)
6.3 Node.js (@clickhouse/client)
6.4 Java (clickhouse-jdbc)
6.5 curl / HTTP 직접 호출
7. 모니터링 및 검증
7.1 연결된 노드 확인
현재 어떤 노드에 연결되어 있는지 확인하는 쿼리:
-- 호스트명 확인
SELECT hostName();
-- 더 상세한 정보
SELECT
hostName() as host,
version() as version,
uptime() as uptime_seconds,
getMacro('replica') as replica_id;
7.2 캐시 상태 모니터링
-- Query Cache 상태
SELECT
metric,
value,
description
FROM system.metrics
WHERE metric LIKE '%QueryCache%';
-- 메모리 사용량
SELECT
metric,
formatReadableSize(value) as size
FROM system.asynchronous_metrics
WHERE metric IN (
'MarkCacheBytes',
'UncompressedCacheBytes',
'QueryCacheBytes'
);
7.3 연결 일관성 테스트
여러 요청이 동일 노드로 라우팅되는지 테스트:
#!/bin/bash
STICKY_HOST="test.sticky.abcxyz123.us-west-2.aws.clickhouse.cloud"
for i in {1..10}; do
curl -s "https://${STICKY_HOST}:8443" \
--user "default:password" \
--data-binary "SELECT hostName()"
echo ""
done
모든 출력이 동일한 호스트명이면 Sticky Session이 정상 작동하는 것입니다.
8. Best Practices
8.1 Sticky 접두사 네이밍 전략
효과적인 부하 분산과 캐시 최적화를 위한 네이밍 전략:
패턴 | 용도 | 예시 |
{team}-{env} | 팀별, 환경별 분리 | analytics-prod, ml-staging |
{app}-{shard} | 애플리케이션 샤딩 | api-shard1, api-shard2 |
{job}-{id} | 배치 작업 분리 | etl-job-20251225, import-batch-001 |
{tenant} | 멀티테넌트 격리 | customer-abc, customer-xyz |
8.2 Fallback 전략
Sticky Session이 불안정할 때를 대비한 Fallback:
8.3 Memory 테이블 사용 시 주의사항
Memory 테이블을 ClickHouse Cloud에서 사용할 때:
- 반드시 Sticky Session 사용: 데이터 일관성을 위해 필수
- 영속성 기대 금지: 노드 재시작 시 데이터 손실
- 대안 고려: Dictionary, Buffer 테이블, 또는 SharedMergeTree 사용 검토
- 크기 제한: Circular Buffer 설정으로 메모리 관리
-- Memory 테이블에 크기 제한 설정
CREATE TABLE bounded_cache (
key String,
value String,
ts DateTime DEFAULT now()
) ENGINE = Memory
SETTINGS max_rows_to_keep = 100000;
결론
ClickHouse Cloud의 Sticky Session (Replica-aware Routing)은 Stateless 아키텍처의 이점을 유지하면서도, 특정 워크로드에서 필요한 연결 일관성을 제공하는 유용한 기능입니다.
핵심 포인트를 정리하면:
- 목적: 캐시 재사용률 향상, Memory 테이블 접근, 세션 일관성 유지
- 구현:
.sticky.{service-host}패턴의 호스트명 사용 - 제약: 완벽한 격리 보장 안 됨, 서비스 변경 시 라우팅 변경 가능
- 활성화: ClickHouse Cloud 지원팀에 문의 필요 (Private Preview)
- 대안: Native TCP 연결은 기본적으로 Sticky 특성 제공
Sticky Session은 "필요할 때만 사용"하는 것이 좋습니다. 대부분의 워크로드는 기본 로드 밸런싱으로 충분하며, Distributed Cache 덕분에 캐시 효율도 높습니다. Memory 테이블이나 Temporary 테이블을 사용하는 특수한 경우에만 Sticky Session을 활용하는 것을 권장합니다.
참고 자료
- ClickHouse Docs: Replica-aware Routing
- ClickHouse Blog: Building a Distributed Cache for S3
- ClickHouse Blog: Stateless Compute Architecture
- Envoy Proxy: Ring Hash Load Balancing