Skip to Content
Bloggp3 성능을 올렸는데 ClickHouse가 빨라지지 않는 이유

gp3 성능을 올렸는데 ClickHouse가 빨라지지 않는 이유

#ClickHouse#AWS#EBS#gp3

gp3의 throughput을 125 MiB/s에서 500 MiB/s로 올렸습니다.

그런데 ClickHouse 쿼리와 merge 시간은 거의 그대로입니다.

이 상황에서 볼륨을 하나 더 붙이거나 io2로 바꾸면 빨라질까요?

먼저 병목이 볼륨에 있는지, EC2 인스턴스의 EBS 경로에 있는지, 아니면 스토리지 밖에 있는지 나눠야 합니다.

gp3 설정값은 성능 보장이 아니라 사용할 수 있는 한도 하나를 올린 것입니다.

gp3에는 서로 다른 두 숫자가 있다

AWS의 gp3 공식 문서 를 기준으로 일반 리전의 현재 범위는 다음과 같습니다.

사양 확인 기준: 2026-09-15.

항목기본 포함프로비저닝 상한
IOPS3,00080,000
throughput125 MiB/s2,000 MiB/s
볼륨 크기1 GiB부터64 TiB

IOPS와 throughput은 같은 값이 아닙니다.

작은 랜덤 I/O가 많으면 IOPS가 먼저 닿을 수 있고, 큰 순차 읽기와 쓰기가 많으면 throughput이 먼저 닿을 수 있습니다.

AWS는 SSD 볼륨에서 I/O 크기를 최대 256 KiB 단위로 계산합니다.

예를 들어 평균 I/O가 16 KiB라면 3,000 IOPS가 낼 수 있는 이론적 처리량은 약 46.9 MiB/s입니다.

3,000 IOPS × 16 KiB ≈ 46.9 MiB/s

평균 I/O가 128 KiB라면 같은 IOPS에서도 약 375 MiB/s까지 계산됩니다.

다만 gp3 기본 throughput 125 MiB/s가 더 낮으므로 실제 상한은 125 MiB/s가 됩니다.

실효 처리량 상한 ≈ min(IOPS × 평균 I/O 크기, 볼륨 throughput, 인스턴스 EBS throughput)

한 숫자만 올려서는 다른 숫자에 막힐 수 있습니다.

볼륨보다 인스턴스가 먼저 막힐 수 있다

AWS EBS-optimized 인스턴스 문서 는 인스턴스의 EBS 성능이 인스턴스 한도와 연결된 볼륨 합계 중 더 작은 값으로 제한된다고 설명합니다.

gp3 한 개를 80,000 IOPS와 2,000 MiB/s로 설정해도 작은 인스턴스가 그 성능을 전달하지 못할 수 있습니다.

r7g 계열의 공개 사양을 예로 보면 이 차이가 잘 드러납니다.

아래 값은 2026년 7월 조사 스냅샷입니다. 실제 배포 리전에서는 describe-instance-types로 다시 확인해야 합니다.

인스턴스EBS baseline throughputEBS 최대 throughputbaseline / 최대 IOPS
r7g.xlarge156 MB/s1,250 MB/s6,000 / 40,000
r7g.2xlarge312 MB/s1,250 MB/s12,000 / 40,000
r7g.4xlarge625 MB/s1,250 MB/s20,000 / 40,000
r7g.8xlarge1,250 MB/s2,500 MB/s40,000 / 80,000

r7g.2xlarge에서 gp3 throughput을 300 MiB/s 정도로 설정하면 지속 EBS 경로와 비슷한 범위가 됩니다.

여기서 볼륨을 1,000 MiB/s로 올려도 인스턴스가 지속적으로 전달할 수 있는 값은 그보다 낮습니다.

버스트 최대값을 지속 성능으로 계산해서도 안 됩니다.

현재 리전과 인스턴스 타입의 값을 직접 보는 명령은 다음과 같습니다.

aws ec2 describe-instance-types \ --instance-types r7g.2xlarge \ --query 'InstanceTypes[0].EbsInfo.EbsOptimizedInfo' \ --output table

여기서 확인할 필드는 BaselineIops, MaximumIops, BaselineThroughputInMBps, MaximumThroughputInMBps입니다.

ClickHouse는 왜 throughput에 민감한가

MergeTree는 INSERT로 작은 part를 만들고 백그라운드에서 여러 part를 더 큰 part로 합칩니다.

merge는 기존 part를 읽고, 정렬하고, 압축한 뒤 새 part를 다시 씁니다.

큰 범위의 순차 I/O와 CPU가 함께 필요합니다.

그래서 IOPS만 올린다고 merge가 같은 비율로 빨라지지 않습니다.

다음 중 하나가 남아 있을 수 있습니다.

  • EBS throughput 한도
  • EC2의 EBS baseline 한도
  • CPU의 압축·정렬 시간
  • 메모리와 page cache 부족
  • 너무 많은 작은 part
  • 동시에 실행되는 쿼리와 merge의 경합
  • 네트워크를 통한 replica fetch

작은 INSERT가 너무 많다면 더 빠른 디스크는 증상을 늦출 뿐입니다.

part 생성 속도가 merge 속도보다 빠른 구조를 먼저 고쳐야 합니다.

먼저 ClickHouse 안에서 확인한다

디스크 설정을 바꾸기 전에 part와 merge 상태를 봅니다.

SELECT database, table, count() AS active_parts, sum(rows) AS rows, formatReadableSize(sum(bytes_on_disk)) AS bytes FROM system.parts WHERE active GROUP BY database, table ORDER BY active_parts DESC;

partition별 part 수가 계속 증가하면 배칭과 partition key를 확인합니다.

현재 merge는 다음 쿼리로 볼 수 있습니다.

SELECT database, table, elapsed, progress, num_parts, formatReadableSize(total_size_bytes_compressed) AS compressed, formatReadableSize(memory_usage) AS memory FROM system.merges ORDER BY elapsed DESC;

replica fetch가 병목인지도 분리합니다.

SELECT database, table, queue_size, inserts_in_queue, merges_in_queue, part_mutations_in_queue, absolute_delay FROM system.replicas ORDER BY absolute_delay DESC;

merge가 없는데 쿼리만 느리다면 스토리지보다 실행 계획과 읽은 바이트, 캐시 적중을 봐야 합니다.

system.query_log에서 같은 query shape의 read_bytes, read_rows, query_duration_ms를 비교합니다.

SELECT normalized_query_hash, count() AS runs, quantile(0.5)(query_duration_ms) AS p50_ms, quantile(0.95)(query_duration_ms) AS p95_ms, formatReadableSize(avg(read_bytes)) AS avg_read FROM system.query_log WHERE event_time > now() - INTERVAL 1 HOUR AND type = 'QueryFinish' GROUP BY normalized_query_hash ORDER BY p95_ms DESC LIMIT 20;

AWS 지표를 같은 시간축으로 본다

ClickHouse 쿼리와 merge를 실행한 시간에 EBS CloudWatch 지표를 맞춥니다.

아래의 평균 지연과 exceeded check는 Nitro 기반 인스턴스에 연결한 볼륨에서 제공되는 지표입니다. 보이지 않는 환경에서는 VolumeReadBytes, VolumeWriteBytes, VolumeReadOps, VolumeWriteOps, VolumeTotalReadTime, VolumeTotalWriteTime으로 같은 축을 계산합니다.

지표해석
VolumeReadBytes, VolumeWriteBytes실제 볼륨 처리량
VolumeReadOps, VolumeWriteOpsIOPS 사용량
VolumeQueueLength처리하지 못한 I/O 대기
VolumeAvgReadLatency, VolumeAvgWriteLatency볼륨 응답 지연
VolumeIOPSExceededCheckprovisioned IOPS 한도 도달 여부
VolumeThroughputExceededCheckprovisioned throughput 한도 도달 여부

인스턴스 계층에서도 EBS bandwidth와 IOPS 제한 지표를 확인합니다.

볼륨의 exceeded 지표는 조용한데 인스턴스 EBS 제한에 닿았다면 gp3를 더 올려도 변화가 없습니다.

반대로 인스턴스에 여유가 있고 VolumeThroughputExceededCheck가 반복된다면 throughput 상향이 직접적인 개선이 될 수 있습니다.

RAID0로 볼륨을 늘리면 해결될까

여러 gp3를 RAID0로 묶으면 볼륨 합계의 IOPS와 throughput을 늘릴 수 있습니다.

하지만 인스턴스 EBS 한도는 그대로입니다.

그리고 볼륨 하나가 실패해도 배열 전체를 잃을 수 있어 운영 복잡도와 실효 내구성이 나빠집니다.

비교단일 gp3다중 gp3 RAID0
볼륨 성능 상한한 볼륨 한도볼륨 수만큼 합산 가능
인스턴스 한도적용똑같이 적용
확장Elastic Volumes로 단순배열과 파일시스템 재구성 고려
장애 영향한 볼륨한 볼륨 장애가 배열 전체에 영향 가능
Kubernetes 운영단일 PVCRAID 구성과 노드 부팅 절차 필요

단일 gp3가 이미 인스턴스 baseline을 채운다면 RAID0는 답이 아닙니다.

더 큰 인스턴스로 EBS 경로를 넓히거나 로컬 NVMe를 검토해야 합니다.

반대로 현재 부하가 125 MiB/s에도 못 미친다면 볼륨 수를 늘릴 이유가 없습니다.

io2로 가야 하는 시점

io2는 낮은 지연과 높은 IOPS, 더 높은 볼륨 내구성이 필요한 워크로드에 맞습니다.

ClickHouse의 대규모 scan과 merge가 throughput 중심이라면 gp3의 throughput을 조정하는 쪽이 먼저일 수 있습니다.

io2를 검토할 질문은 다음과 같습니다.

  1. 작은 랜덤 I/O의 latency가 실제 SLA를 깨는가?
  2. gp3의 80,000 IOPS를 넘는 요구가 있는가?
  3. 인스턴스가 그 IOPS와 throughput을 전달할 수 있는가?
  4. 볼륨 내구성 향상이 복제와 백업을 포함한 전체 RPO에 어떤 차이를 만드는가?

비싼 볼륨으로 바꾸기 전에 첫 세 질문에 측정값으로 답해야 합니다.

튜닝 순서

실제로는 아래 순서가 가장 덜 헷갈립니다.

  1. 느린 작업이 query인지 merge인지 replication fetch인지 구분합니다.
  2. 같은 시간의 ClickHouse part·merge·query 지표를 모읍니다.
  3. EBS 볼륨의 IOPS·throughput·latency·queue를 확인합니다.
  4. EC2 인스턴스의 EBS baseline과 maximum을 확인합니다.
  5. 볼륨 한도라면 필요한 축만 올립니다.
  6. 인스턴스 한도라면 볼륨 변경 대신 인스턴스나 저장 구조를 검토합니다.
  7. part 폭증이라면 배칭과 partition 설계를 먼저 고칩니다.

gp3의 큰 최대 숫자는 그 성능을 언제나 얻는다는 뜻이 아닙니다.

ClickHouse가 실제로 요구하는 I/O 모양과 EC2가 전달할 수 있는 경로가 모두 맞아야 합니다.

볼륨 설정을 올렸는데 빨라지지 않았다면 실패한 튜닝이라기보다, 다음 병목을 드러낸 측정으로 보는 편이 맞습니다.

자료 확인 기준: 2026-09-15. AWS 사양과 CloudWatch 지표 이름은 리전, 볼륨 상태, 모니터링 방식에 따라 실제 콘솔과 API에서 다시 확인합니다.