
ClickHouse에서 RocksDB로: Solana 아카이브 레이어를 재구축한 방법
목차
- Solana 아카이브란 무엇이며 왜 중요한가
- ClickHouse: 실용적인 첫 선택
- ClickHouse의 한계가 드러난 지점
- RocksDB: 데이터베이스가 아닙니다
- RocksDB가 ClickHouse의 문제를 해결하는 방법
- 그 위에 구축한 것
- 대규모 환경을 위한 RocksDB 최적화
- 데이터베이스 단위가 아닌 인덱스 단위로 조정하세요
- WAL이 실제로 필요한지 판단하세요
- 적중/누락 비율에 맞게 블룸 필터를 설정하세요
- 압축 가능한 것은 압축하고, 자주 접근하는 것은 압축하지 마세요
- 직접 I/O를 고려하세요
- 경합을 견디는 캐시를 선택하세요
- 다중 키 조회를 직렬화하지 말고 병렬화하세요
- 앞으로의 목표
300테라바이트가 넘는 Solana 아카이브 데이터를 ClickHouse에서 RocksDB로 이전했다고 말하면 거의 항상 같은 반응이 돌아옵니다. 대체 왜 그렇게 했나요?
페타바이트 규모의 분석 워크로드라면 ClickHouse가 당연한 선택이지만, RocksDB는 그렇지 않습니다.
ClickHouse는 세계 최대 규모의 데이터 소비 기업들이 신뢰하는 솔루션입니다.
예를 들어 Cloudflare는 초당 수억 건의 삽입을 처리하기 위해 1,000개가 넘는 복제본에서 ClickHouse를 실행합니다.
Anthropic은 Claude의 관측 가능성을 지원하기 위해 맞춤형 에어갭 ClickHouse 배포를 운영합니다. Uber, eBay, Bloomberg도 수년간 프로덕션에서 사용해 왔습니다.
반면 RocksDB는 문서가 많지 않은 임베디드 키-값 저장소입니다. 고객 대상 과거 데이터 서비스의 직접적인 기반보다는 주로 다른 데이터베이스(예: CockroachDB, TiKV, MyRocks) 내부의 엔진으로 사용됩니다.
하지만 이번 이전으로 압축 스토리지 사용량을 ~330TB에서 ~190TB로 줄였고, 가장 느린 쿼리의 롱테일 문제를 해결했습니다(예: getTransactionsForAddress 호출의 p95 지연 시간이 350ms에서 30ms로 감소). 또한 Helius가 요구하는 성능과 규모로는 ClickHouse에서 불가능했던 Solana 읽기 레이어 혁신의 기반도 마련했습니다.
이 글에서는 ClickHouse에서 마이그레이션한 이유, 그 과정에서 워크로드에 관해 배운 점, 그리고 대규모 프로덕션 환경에서 RocksDB를 사용하는 방법을 설명합니다.
Solana 아카이브란 무엇이며 왜 중요한가
근본적으로 Solana는 서로를 신뢰하지 않고도 통신을 통해 새로운 정보에 합의하는 노드의 네트워크입니다.
노드는 정해진 규칙을 준수하는 클라이언트(예: Agave, Firedancer)를 실행해 새로운 정보에 대한 합의를 지원하는 Solana상의 컴퓨터입니다.
검증인은 블록을 생성하고(즉, 트랜잭션 그룹을 Solana 원장에 추가하고) 다른 블록의 유효성에 투표해 네트워크를 보호하는 Solana 노드입니다.
RPC 노드는 블록 생성이나 투표에 참여하지 않는 검증인입니다. 대신 네트워크를 관찰하고 네트워크가 생성하는 모든 새로운 정보를 추적합니다. RPC를 사용하면 JSON-RPC 명세에 따라 네트워크에서 특정 데이터를 조회할 수 있습니다.
하지만 이러한 노드가 데이터를 영구히 보관하지는 않습니다.
Solana의 하드웨어 요구 사항을 충족하기 위해 오래된 블록, 트랜잭션, 계정 상태는 정리됩니다. 따라서 노드에는 Solana 기록의 가장 최근 상태만 남습니다.
1년 전의 트랜잭션 서명을 조회하거나, 특정 지갑과 지금까지 상호작용한 모든 서명을 가져오거나, 프로그램의 전체 실행 기록을 스캔하려면 문제가 됩니다.
아카이브는 일반적으로 제네시스 이후의 Solana 데이터를 저장하는 전체 레이어를 의미합니다. 체인이 생성하는 모든 블록, 트랜잭션, 계정 상호작용, 크로스 프로그램 호출(CPI), 트랜잭션 로그를 기록하고 인덱싱하며, 표준 단기 스토리지 기간인 ~2일(즉, 1 에포크)이 지나도 조회할 수 있게 합니다.
아카이브가 있기에 과거 데이터를 조회할 수 있습니다.
Solana에서 아카이브 데이터를 저장하는 기본 방식은 Google BigTable이었습니다. Anza는 RPC 제공업체가 필요할 때 접근해 과거 쿼리를 처리할 수 있는 BigTable 인스턴스를 운영합니다.
작동은 하지만 BigTable은 비싸고, 송신 비용 때문에 자체 복사본을 운영하기도 어렵습니다. 성능을 높이기 위해 직접 할 수 있는 엔지니어링 작업도 거의 없습니다. Google의 스토리지에 의존하면서 모든 비용 구조는 감수하고 제어권은 갖지 못합니다.
Old Faithful은 Triton One이 관리하는 도구 모음입니다. 원장 RocksDB 아카이브에서 콘텐츠 주소 지정 가능 아카이브(CAR)를 생성하고 Solana의 표준 RPC 및 gRPC 인터페이스를 통해 제공할 수 있습니다. Solana 아카이브 레이어의 탈중앙화를 향한 의미 있는 진전이며, 중복성과 검증 가능성을 갖춘 귀중한 기록 소스를 제공합니다.
하지만 개발자 경험과 성능 측면의 절충점 때문에 지연 시간에 민감한 워크로드에는 의미 있는 대안이 되기 어렵습니다. 예를 들어 팀이 바로 활용할 수 있는 인터페이스가 아니라 맞춤형 도구로 시작해야 하며, 성능과 규모보다 내구성 있는 아카이브에 최적화되어 있습니다.
아카이브의 핵심 문제는 N페타바이트의 원시 데이터가 있다는 점입니다. 5,000억 건이 넘는 트랜잭션, ~1.3조 행의 계정-트랜잭션 인덱스, 무작위 접근 패턴에서 어떻게 10ms 미만의 조회를 구현할 수 있을까요? 엔드투엔드 쿼리를 50ms 미만으로 처리하려면 무엇이 필요할까요? Google BigTable과 Old Faithful 모두 이에 대한 해답을 제공하지 못합니다.
더구나 맞춤형 필터링 및 정렬 옵션을 구축하거나 표준 JSON-RPC 메서드보다 풍부한 기능을 제공해 개발자 경험을 개선하려면 자체 아카이브 인덱스가 필요합니다.
그래서 직접 구축했습니다.
ClickHouse: 실용적인 첫 선택
ClickHouse는 새로운 아카이브 시스템 구축을 시작하기에 가장 실용적인 선택이었습니다. BigTable 기반보다 더 나은 제품을 가장 빠르게 출시할 수 있는 방법이었습니다.
ClickHouse는 훌륭한 도구와 문서를 갖춘 성숙한 컬럼형 데이터베이스입니다. 시계열 데이터를 잘 압축하며, 블록 데이터가 본질적으로 시간순인 Solana에 특히 유용합니다.
데이터베이스를 생성하고, SQL을 작성하고, 스키마를 반복 개선하고, 출시 속도에 맞게 최적화하기가 쉽습니다. 덕분에 고객에게 비교적 빠르게 제품을 제공할 수 있습니다.
ClickHouse가 모든 트래픽을 단독으로 처리한 것은 아닙니다. 데이터의 최신성에 따라 구성된 스토리지 스택의 최하위 계층이었습니다.
가장 최신 데이터, 즉 대략 최근 1~2분 또는 수백 개 슬롯의 데이터는 인메모리 저장소에 있었습니다. 최근 약 2주간의 데이터는 Postgres에, 나머지 Solana 기록은 ClickHouse에 저장했습니다.
세 데이터 소스 앞에는 스마트 라우터를 배치했습니다. 포인트 조회는 데이터가 있는 가장 최신 계층으로 보내고, 범위 쿼리는 여러 계층으로 분할한 뒤 하나의 결과로 다시 결합했습니다.
ClickHouse는 설계 목적에 맞는 워크로드에서는 비교적 잘 작동했습니다. 예를 들어 “이 슬롯 범위의 모든 블록을 가져와라” 또는 “이 계정의 시간대별 활동을 스캔하라” 같은 작업입니다.
시계열 쿼리는 문제없었습니다. 백필을 수행하고, 임시 분석을 실행하고, RPC 제공업체가 오랫동안 필요로 한 대부분의 질문에 답할 수 있었습니다. 수집 측에는 전혀 부담이 없었습니다. LaserStream을 통해 인덱스에 쓰는 양은 ~10MB/s에 불과했습니다.
ClickHouse를 선택한 것 자체가 실수는 아니었습니다. 워크로드가 앞으로도 ClickHouse가 대규모로 처리할 수 있는 형태를 유지할 것이라고 가정한 것이 실수였습니다.
ClickHouse의 한계가 드러난 지점
우리가 중요하게 보는 Solana 과거 데이터 메서드가 모두 시계열인 것은 아닙니다. Solana의 서명, 즉 범용 트랜잭션 식별자는 본질적으로 64바이트의 무작위 데이터입니다.
특정 서명에 대해 getTransaction를 호출하면 균등하게 무작위인 포인트 조회가 발생합니다. 활용할 수 있는 슬롯, 시간 범위, 지역성이 전혀 없습니다.
“이 계정과 상호작용한 모든 서명을 가져와라” 같은 다른 쿼리도 마찬가지입니다. 계정 키 역시 사실상 무작위이기 때문입니다. 기본 키가 UUID, 해시 또는 엔트로피가 높은 다른 식별자일 때마다 이 문제가 발생합니다.
컬럼형 엔진은 이 문제를 해결하도록 설계되지 않았습니다.
ClickHouse는 데이터를 정렬된 파트에 저장하고 희소 기본 인덱스를 사용합니다. 따라서 무작위 키 조회에서는 검색 범위를 크게 줄일 수 없습니다. 결국 원하는 것보다 많은 그래뉼을 읽게 됩니다. 단일 서명 조회도 이진 검색이나 CPU 오버헤드가 발생하기 전에 20개의 그래뉼, 즉 약 10,000개 행과 ~60회의 디스크 I/O에 접근할 수 있습니다.
I/O만 계산해도 조회 한 번에 약 5ms가 걸립니다. 저장소가 컬럼형이므로 트랜잭션 하나를 읽으려면 512행 그래뉼에서 ~15개 컬럼을 별도의 I/O로 읽어야 합니다.
예를 들어 전체 트랜잭션 세부 정보를 요청하는 getTransactionsForAddress 호출은 이러한 조회를 최대 100개까지 확장합니다. 바이트 하나를 직렬화하기도 전에 최소 지연 시간이 100ms에 가까워집니다.
대규모 getTransaction 배치(즉, 최대 1,000개)는 이 최소 시간을 거의 1초까지 끌어올렸습니다.
스캔을 빠르게 만드는 추상화(예: 벡터화 실행, 지연 구체화)도 이 문제를 완전히 해결하지는 못합니다. 조회는 이미 최대한 최적화되어 있었습니다. 빠진 인덱스가 있었던 것도, 추가할 수 있는 프로젝션이 있었던 것도 아닙니다.
간단히 말해, 우리가 요구한 작업에 근본적인 데이터 레이아웃이 맞지 않았습니다.
우리가 추가한 가장 비용이 큰 메서드(예: getTransactionsForAddress, getTransfersByAddress)는 ClickHouse가 가장 취약한 무작위 키 패턴이었습니다.
몇 밀리초면 끝나야 할 일부 쿼리에 2~3초가 걸렸습니다. 근본적으로 해결할 수 없는 워크로드의 성능 저하로 호출 알림을 받고 있었습니다.
기관과 엔터프라이즈 고객이 Helius를 대규모로 사용한다는 점을 고려하면 장기적으로 절대 용납할 수 없는 상황입니다.
이 워크로드에 맞춰 ClickHouse를 확장하려면 서버 수를 늘려야 했고, 우리 규모에서는 워크로드를 몇 배로 복제해야 했습니다. 하드웨어에 비용만 투입해서는 문제를 해결할 수 없었습니다.
Solana의 RPC 스택은 성숙하고 있습니다. 생태계는 표준 JSON-RPC 메서드만으로는 충분하지 않은 변곡점에 도달했습니다. 고객은 풍부한 과거 데이터 쿼리를 요구하지만, 누구도 BigTable 위에 이를 구축하려 하지 않았습니다.
그 한계를 넓히려면 스토리지 레이어를 바꿔야 했습니다.
RocksDB: 데이터베이스가 아닙니다
이름과 달리 RocksDB는 데이터베이스가 아닙니다. 라이브러리입니다.
전통적인 데이터베이스에서 말하는 쿼리 언어, 클라이언트/서버 프로토콜, SQL 엔진, 조인, 인덱스가 없습니다. “여기 키(바이트)가 있고 값(역시 바이트)이 있으니 디스크에 영구 저장한 뒤, 나중에 이 키의 값을 돌려달라”고 요청하는 인터페이스일 뿐입니다. 그게 전부입니다.
원시 구성 요소입니다. 스토리지 엔진을 구축하는 데 필요한 핵심 원시 구성 요소라고도 할 수 있습니다.
전통적인 데이터베이스에서 기대하는 모든 요소(예: 쿼리 플래너, 와이어 프로토콜, 복제, 관측 가능성)를 직접 구축해야 합니다.
단점처럼 들리지만, 이것이 무엇을 가능하게 하는지 이해하면 달라집니다. 디스크의 로그 구조 병합(LSM) 트리를 기반으로 하는 순수 키-값 저장소는 처리량이 높은 균등 무작위 키 조회에 정확히 맞는 구조입니다. 오버헤드를 더하는 쿼리 플래너도, 조정해야 할 컬럼형 레이아웃 가정도, 비용을 고려해야 할 SQL 프런트엔드도 없습니다.
바이트를 저장하고 다시 읽으며, 블룸 필터와 캐시를 적절한 위치에 배치해 무작위 읽기 비용을 낮게 유지하면 됩니다.
바로 이 지점에서 안티패턴이라는 관점은 무너집니다.
ClickHouse를 RocksDB로 교체한 것이 아닙니다. ClickHouse를 스토리지 엔진으로 RocksDB를 사용하는 맞춤형 데이터베이스로 교체했습니다. 둘은 의미상 완전히 다릅니다.
대부분의 프로덕션 데이터베이스가 내부적으로 사용하는 방식과도 같습니다. 예를 들어 CockroachDB, TiKV, MyRocks, Kafka Streams 상태 저장소는 모두 RocksDB 위에 구축되어 있습니다.
차이점은 이들이 먼저 다른 데이터베이스로 RocksDB를 감싸는 반면, 우리는 RocksDB를 중심으로 직접 데이터베이스를 구축하고 아카이브가 요구하는 정확한 접근 패턴에 맞게 조정했다는 것입니다.
RocksDB가 ClickHouse의 문제를 해결하는 방법
그렇다면 키-값 저장소는 컬럼형 엔진이 해결하지 못한 무작위 키 문제를 정확히 어떻게 해결할까요? 핵심은 바이트가 디스크에 저장되는 방식입니다.
RocksDB는 레벨 기반 컴팩션을 사용합니다. 새로운 쓰기는 정렬되지 않은 임시 공간인 레벨 0에 들어갑니다. 파티션 내부의 파트가 전역적으로 정렬되지 않는다는 점에서 ClickHouse와 사실상 같은 상황입니다.
하지만 레벨 1부터 N까지는 완전히 정렬된 런입니다. 데이터가 컴팩션되면 레벨이 전역 순서로 정렬되므로 균등 무작위 키에서도 최소/최대 메타데이터가 실제로 검색 범위를 줄입니다. 어떤 의미에서는 ClickHouse의 모든 데이터가 RocksDB의 레벨 0처럼 동작합니다.
백필에는 이 특성을 적극 활용합니다.
서명 인덱스를 구축할 때 전체 기록을 미리 정렬해 최하위 레벨에 바로 로드합니다. 이후에는 최신 데이터만 레벨 0에 들어가며 빠르게 아래로 병합됩니다.
그 결과 거의 모든 조회가 정렬된 단일 런에서 읽힙니다. RocksDB로 무작위 데이터를 실질적으로 정렬한 셈입니다.
그 위에 구축한 것
ClickHouse는 쿼리 엔진, 와이어 프로토콜, 클라이언트, 데이터 제공 방식 등 많은 기능을 기본 제공합니다. RocksDB에는 이런 기능이 전혀 없으며 단순히 바이트만 영구 저장합니다. 나머지는 모두 직접 구축해야 했습니다.
그래서 Solana 아카이브 접근 패턴에 특화된 자체 데이터베이스를 RocksDB 위에 구축했습니다.
현재 RocksDB에는 두 개의 인덱스가 있습니다.
- 서명 -> 위치(즉, 트랜잭션의 64바이트 서명을 해당 트랜잭션이 블록 안에서 위치한 슬롯과 위치에 매핑하는 서명 인덱스).
- 슬롯 -> 블록(즉, 위 위치를 트랜잭션 데이터에 매핑하는 슬롯-블록 인덱스).
중요한 점은 서명에서 트랜잭션 데이터로 바로 가는 경로가 없다는 것입니다. 바로 이 문제를 해결하기 위해 두 인덱스가 존재합니다.
서명은 본질적으로 무작위 64바이트 식별자입니다. 트랜잭션의 실제 위치를 알려주는 것은 슬롯과 해당 블록 내의 인덱스입니다. 따라서 트랜잭션을 가져오려면 두 번의 홉, 즉 서명을 특정 위치로 해석하는 단계와 그 위치에서 트랜잭션 세부 정보를 검색하는 단계가 필요합니다. 반면 블록을 가져올 때는 호출자가 이미 슬롯을 제공하므로 한 번의 홉만 필요합니다.
이 두 인덱스는 Helius가 제공하는 가장 무거운 메서드 중 일부를 지원합니다. 예를 들어 getBlock, getTransaction, getTransactionsForAddress가 있으며, 특히 details가 full로 설정된 경우에 중요합니다.
읽기 경로는 ClickHouse에서와 거의 같습니다. 요청이 들어오면 내부 클라이언트가 데이터베이스를 호출하고 결과가 반환됩니다. 달라진 것은 내부 엔진입니다. 각 메서드는 하드코딩하고 세밀하게 조정한 경로를 사용합니다. 사용자가 트랜잭션을 조회하면 전용 getTransaction 경로가 바이트로 곧바로 연결됩니다.
모든 레이어를 직접 제어하기 때문에 이 경로는 빠릅니다. 파일 및 네트워크 I/O에는 io_uring를 사용해 RocksDB에서 데이터를 스트리밍합니다. 데이터가 디스크에 압축되지 않은 상태로 있으면 사용자 공간을 우회하지 않고 디스크에서 네트워크 카드로 바로 복사합니다.
메서드는 하드코딩되어 있고 I/O는 엔드투엔드로 조정되어 있으며, 중간에 불필요한 요소가 없습니다.
그 효과는 프로덕션에서 분명하게 나타납니다.
최근에는 대부분의 네트워크 카드를 포화 상태로 유지하면서 getBlock 트래픽 150Gbit/s를 5~6시간 연속 처리했습니다. 문제도, 호출 알림도 없었으며 원시 성능도 그대로 유지했습니다.
전반적으로 다음과 같은 성과를 얻었습니다.
- 압축 스토리지는 ~330TB에서 ~190TB로 거의 절반 줄었습니다
getTransaction호출의 P95 지연 시간은 7ms에서 1ms로 감소했습니다getTransactionsForAddress호출의 P95 지연 시간은 350ms에서 30ms로 감소했습니다getBlock호출의 P95 지연 시간은 50ms에서 35ms로 감소했습니다
예상대로 getBlock 호출의 개선 폭이 가장 작았습니다. ClickHouse는 이미 트랜잭션을 512행 청크로 저장하므로 블록 크기 읽기에서 컬럼형 구조의 페널티가 분산됩니다. getBlock의 엔드투엔드 시간 중 상당 부분은 Base58 및 JSON 인코딩과 블록 재조립에 사용되므로 스토리지 엔진을 교체해도 이 과정은 빨라지지 않습니다.
io_uring, 비동기 Rust, RocksDB 같은 동기식 라이브러리가 높은 처리량의 네트워크 워크로드에서 함께 작동하도록 만든 방법은 향후 별도 글로 다룰 만한 주제입니다.
다만 다음 섹션에서는 RocksDB를 사용하며 유용하다고 확인한 몇 가지 최적화를 살펴봅니다.
대규모 환경을 위한 RocksDB 최적화
단 하나의 “빠른” RocksDB 구성은 없습니다. 적절한 설정은 조정하려는 인덱스의 접근 패턴에 전적으로 달려 있습니다.
서명 -> 위치 인덱스와 슬롯 -> 블록 인덱스는 같은 하드웨어의 동일한 프로세스에서 실행되지만, 조정 방식은 거의 정반대입니다.
따라서 사용자의 워크로드에 그대로 적용되지 않을 수 있는 구성 파일을 제공하기보다, 다른 환경에도 적용할 수 있는 절충점을 판단하는 방법을 설명하겠습니다.
데이터베이스 단위가 아닌 인덱스 단위로 조정하세요
각 인덱스는 고유한 옵션을 가진 별도의 컬럼 패밀리입니다. 하나는 균등 무작위 포인트 조회이고, 다른 하나는 순서대로 가져오는 크고 압축 가능한 값입니다. 두 인덱스를 똑같이 취급했다면 많은 성능 최적화 기회를 놓쳤을 것입니다. 아래에서 설명하는 거의 모든 결정은 “이 접근 패턴에는 X를 적용한다”로 이해해야 합니다.
WAL이 실제로 필요한지 판단하세요
아카이브 데이터는 다시 구축할 수 있습니다. LaserStream에서 스트리밍되며 체인 자체에서 파생되기 때문입니다.
Write Ahead Log (WAL)를 비활성화한 상태로 쓰기 때문에 필요하지 않은 선행 쓰기 로그의 내구성 비용을 지불하지 않습니다.
주의할 점은 WAL을 끄면 컬럼 패밀리 간 RocksDB의 기본 충돌 일관성도 비활성화된다는 것입니다. 따라서 다른 방식으로 일관성을 복원해야 합니다. 여기서 얻을 수 있는 교훈은 내구성 설정이 데이터의 복구 가능성과 일치해야 한다는 것입니다. 업스트림 소스에서 재구축할 수 있는 데이터와 기준 정보 시스템은 요구 수준이 완전히 다릅니다.
적중/누락 비율에 맞게 블룸 필터를 설정하세요
블룸 필터는 조회 시 키가 없다는 것을 저렴하게 판별해 메모리 비용을 정당화합니다. 즉, 누락일 때만 도움이 됩니다.
호출자가 서명을 가지고 해당 트랜잭션 데이터를 원하므로 서명 조회는 사실상 항상 적중합니다.
최하위 LSM 레벨에 데이터의 절대다수가 있습니다. 데이터 대부분이 단일 레벨에 있고 워크로드가 적중 중심이라면, 해당 레벨의 필터는 가장 많은 메모리를 사용하면서 가장 적은 작업을 수행합니다. 이런 상황이라면 그 레벨에 필터가 정말 필요한지 검토할 가치가 있습니다.
압축 가능한 것은 압축하고, 자주 접근하는 것은 압축하지 마세요
압축 여부는 인덱스별로 결정해야 합니다. 64바이트 서명처럼 엔트로피가 높은 데이터는 1.0 미만의 비율로 압축되지 않습니다. 압축하면 모든 단일 조회의 핵심 경로에 압축 해제 비용만 추가됩니다. 그래서 압축을 None로 설정했습니다.
반면 블록 데이터는 크고 반복적이므로 잘 압축됩니다. 따라서 슬롯 -> 블록 인덱스는 zstd를 사용합니다. 두 인덱스는 같은 데이터베이스를 사용하지만 서로 다른 선택으로 이점을 얻습니다. 이는 바이트의 압축 가능성과 인덱스가 지연 시간 또는 스토리지 중 무엇에 제약받는지에 따라 전적으로 결정됩니다.
이러한 인덱스별 분리는 포인트 조회 지연 시간을 해치지 않으면서 사용량을 ~330TB에서 ~190TB로 줄일 수 있었던 핵심 요인입니다.
직접 I/O를 고려하세요
읽기에 직접 I/O를 사용하며 플러시와 컴팩션에도 적용합니다. 이 규모에서는 OS 페이지 캐시와 자체 블록 캐시가 같은 RAM을 두고 경쟁합니다. 무작위 포인트 조회에서 이런 이중 캐싱은 대부분 낭비입니다. 예측 가능한 지연 시간을 제공하는 하나의 대형 블록 캐시를 직접 운영하는 편이 낫습니다.
다만 이는 워크로드에 따라 달라집니다.
직접 I/O는 스캔 중심이거나 리소스가 부족한 구성에서 성능을 저하시킬 수 있습니다. 무작정 도입하기보다 A/B 테스트를 진행하는 것이 좋습니다.
경합을 견디는 캐시를 선택하세요
자주 접근하는 키에 동시 부하가 집중되면 표준 공유 LRU 캐시가 잠금 경합의 병목이 됩니다. 자주 접근하는 인덱스에는 RocksDB의 HyperClockCache를 사용합니다. 여러 스레드가 인기 항목에 동시에 집중적으로 접근해도 훨씬 안정적으로 버팁니다.
다중 키 조회를 직렬화하지 말고 병렬화하세요
N개의 읽기를 직렬화하는 대신 RocksDB는 io_uring을 통해 여러 I/O를 동시에 실행하고 병렬로 완료할 수 있습니다. 여러 기본 포인트 조회로 확장되는 getTransactionsForAddress 같은 메서드에서는 키 수에 따라 증가하는 지연 시간과 거의 일정하게 유지되는 지연 시간의 차이를 만듭니다.
RocksDB는 모든 선택지를 제공하지만 데이터에 대한 결정을 대신 내려주지는 않습니다. 모든 작업에 적합한 단일 전역 구성을 찾는 대신, 접근 패턴을 이해하고 각 인덱스를 고유한 형태에 맞게 조정한 것이 성과의 원천입니다.
앞으로의 목표
현재 아카이브는 EWR, FRA, Tokyo 같은 대형 리전에서 실행됩니다. 스토리지 엔진이 이제 마이크로초 단위로 조회 결과를 반환하고 네트워크 카드를 포화시키면서, 더 이상 데이터베이스 문제로 밤중에 호출 알림을 받지 않습니다. 하지만 하나의 병목을 해결하면 다음 병목이 드러나기 마련입니다.
조회 비용이 사실상 0에 가까워지면 주요 비용은 소프트웨어에서 사용자와 머신 사이의 거리로 바뀝니다. 요청은 여전히 사용자가 있는 곳에서 백엔드가 있는 곳까지 갔다가 돌아와야 합니다. 이제는 물리 법칙과 싸우는 단계입니다.
Gatekeeper는 바로 이 문제를 해결합니다.
Gatekeeper는 Hyper 기반의 Rust로 작성한 자체 엣지 게이트웨이입니다. 사용자 가까이에서 연결을 종료하고, 사용 가능한 최단 경로를 통해 각 요청을 백엔드로 라우팅합니다. 이제 지연 시간 경쟁은 이곳에서 벌어집니다. 연결 풀링, TLS 및 소켓 조정, 근접성과 상태를 고려한 라우팅, 글로벌 인프라 전반의 무중단 배포를 통해 아카이브가 이미 마이크로초 단위로 제공하는 바이트까지의 경로에서 밀리초를 줄입니다.
단일 요청을 빠르게 만드는 것은 데이터베이스 문제입니다. 유지보수 시간도, 끊어진 연결도 없이 전 세계 어디에서든 모든 요청을 빠르게 만드는 것은 완전히 다른 문제입니다. 그리고 우리가 다음으로 집중하는 문제이기도 합니다.
이것이 Solana의 읽기 레이어를 혁신한다는 의미입니다. 조회에 응답하는 스토리지 엔진부터 답변이 최종 사용자에게 얼마나 빨리 도달할지 결정하는 엣지 게이트웨이까지 모든 레이어를 제대로 구축하는 것입니다.
아카이브는 결국 무작위 키 포인트 조회로 귀결됩니다. 구조적인 이유로 컬럼형 엔진을 무너뜨리는 접근 패턴입니다. 조정, 샤딩, 뷰 정렬 방식의 문제가 아닙니다. ClickHouse에서 마주한 제약은 구성에서 우연히 발생한 것이 아니라 이 선택에 내재되어 있습니다. Solana의 과거 데이터 워크로드는 가장 쉬운 접근 패턴이 아니라 가장 어려운 접근 패턴으로 정의됩니다.
글로벌 금융의 결제 레이어가 되려는 네트워크 규모에서 읽기 레이어는 이미 한계를 넘어선 기존 시스템을 더 잘 조정한 버전에 그쳐서는 안 됩니다. 풍부한 과거 데이터 쿼리에 의존하는 기관과 애플리케이션에는 기본 스택이 제공할 수 없는 메서드, 범위, 지연 시간이 필요합니다. Solana의 읽기 레이어는 처음부터 어려운 사례에 맞는 기반 위에 구축해야 합니다. 이것이 RocksDB에 건 선택이며, 다른 모든 요소에도 적용하는 기준입니다.
대규모 금융의 미래를 구축하는 데 관심이 있다면 저희와 함께하세요. Solana는 글로벌 금융의 결제 레이어가 되기 위해 빠르게 달리고 있으며, 아카이브는 훨씬 큰 퍼즐의 한 조각일 뿐입니다. LSM 컴팩션과 인덱스별 조정에 관심이 있거나, 비용으로 구매할 수 있는 최고 수준의 하드웨어에서 어려운 시스템 문제를 해결하고 싶다면 이곳이 잘 맞을 것입니다.
현재 엔지니어링 팀 전반에서 인재를 채용하고 있습니다. helius.dev/careers에서 모든 채용 공고를 확인하세요.
관련 아티클
Helius 구독하기
최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요


