LLM WikiAccess-protected knowledge portal
← 스터디 홈
125편 · 약 16분

QuestDB 10.0: QWP 단일 이진 프로토콜로 ILP·PG Wire를 대체하고 Arrow 스트리밍 읽기까지 한 커넥션으로 처리하는 방법

요약

시계열 데이터베이스는 두 가지 상반된 프로토콜을 오래 유지해 왔다. 쓰기용 텍스트 프로토콜(InfluxDB ILP·Prometheus remote_write)읽기용 SQL 프로토콜(PG Wire·HTTP JSON)이다. 클라이언트는 이 둘을 별도 커넥션으로 관리하고, 각기 다른 인증·타임아웃·재시도 정책을 넣는다. 성능 면에서도 텍스트 파싱과 컬럼→행→컬럼 변환이 두 번씩 일어난다.

QuestDB가 2026년 8월 6일 릴리스한 10.0.0(“QWP release”)은 이 분리를 하나의 이진 컬럼형 프로토콜로 합친다. QWP(QuestDB Wire Protocol)는 WebSocket 위에서 이진·컬럼·파이프라인 방식으로 쓰기와 읽기를 함께 실어 나른다. 기존 HTTP 포트(9000)를 그대로 쓰고 서버 측 옵션 조정도 필요 없다. 벤치마크에서는 ILP 대비 초당 5.3M → 19M 행 수집(약 3.6배), 읽기는 Apache Arrow 스트림으로 초당 2억2천만 행을 보고한다.

이 글은 왜 QuestDB가 두 프로토콜을 하나로 합쳤는지, QWP가 어떻게 동작하는지, ILP·PG Wire와의 하위 호환은 어떻게 유지되는지, 그리고 Arrow·Parquet·Iceberg/DuckLake 등 주변 생태계와 어떻게 맞물리는지를 다룬다.


배경: 왜 지금 프로토콜을 다시 짜는가

QuestDB는 원래 두 개의 이질적인 프로토콜에 의존해 왔다.

  • ILP(InfluxDB Line Protocol): 쓰기 전용. 텍스트, 라인 단위, TCP/HTTP/UDP. IoT 센서·모니터링 이벤트의 대량 수집에 최적화. 파싱 비용이 크고, 컬럼형 저장소에 행 단위 텍스트가 들어와 다시 변환된다.
  • PG Wire(PostgreSQL wire protocol): 읽기 전용에 가깝게 사용. 표준 PostgreSQL 드라이버로 임의의 SQL을 실행하고 결과를 받는다. 행 지향 프로토콜이라 넓은 컬럼형 결과를 반환할 때 직렬화/역직렬화 비용이 크다.

이 조합은 세 가지 방향에서 압박을 받았다.

  1. AI/에이전트 워크로드의 실시간 분석. 에이전트가 쓴 결과를 즉시 다시 조회하는 루프가 흔해지면서 쓰기와 읽기가 자연스레 하나의 세션에 얽힌다. 별도 커넥션·별도 인증·별도 backpressure는 관리 부담이 크다.
  2. Arrow 생태계의 확산. Polars·DataFusion·pandas·Snowflake·Trino가 모두 Arrow로 직결한다. PG Wire의 텍스트 결과를 다시 Arrow로 변환하는 것은 낭비다.
  3. Kafka 없는 스트리밍. 대량 이벤트를 특정 목적지에 다이렉트로 넣고 싶다는 요구가 커졌다. HTTP 위에서 pipelined binary가 되면 프록시·게이트웨이 친화성이 유지된다.

QuestDB는 이 세 요구를 한 커넥션 = 양방향 컬럼 스트림이라는 답으로 정리했다. 이것이 QWP다.


QWP의 설계 결정

QWP를 이해할 때 중요한 다섯 가지 선택이 있다.

1) HTTP 포트 재사용

QWP는 WebSocket으로 동작하고, WebSocket은 HTTP 업그레이드로 시작한다. QuestDB는 QWP 트래픽을 기존 HTTP 포트 9000에 그대로 얹었다. 방화벽 규칙·L7 라우팅·리버스 프록시 설정을 추가로 만지지 않아도 된다. 로드밸런서는 HTTP만 알아도 되며, TLS 종단도 기존 경로를 재사용한다.

2) 이진 컬럼 포맷

ILP는 라인 하나가 하나의 행이다. QWP는 반대로 한 프레임이 여러 행의 컬럼 벡터를 담는다. 컬럼 벡터는 QuestDB 내부 저장 표현과 근접하게 설계돼 있어, 서버 쪽에서 열 단위 append 경로에 거의 그대로 들어간다. 텍스트 파싱과 행→열 재배치가 사라진다.

3) 양방향 파이프라인

같은 WebSocket 프레임 스트림 위로 요청과 응답이 파이프라인된다. 클라이언트는 쓰기 배치를 흘려보내면서 동시에 쿼리 응답 스트림을 소비할 수 있다. 이는 에이전트 워크로드(직전 쓰기를 즉시 조회)와 대시보드 워크로드(대량 쓰기 중 라이브 조회)에 유리하다.

4) 압축과 Arrow 정렬

QWP 프레임은 대체로 압축된다. 컬럼별 타입 정보가 프레임 헤더에 있어 압축기가 컬럼 특성에 맞게 선택된다. 읽기 응답의 컬럼 벡터는 Apache Arrow의 in-memory 표현과 호환되는 레이아웃으로 흐른다. 클라이언트가 별도 변환 없이 Polars·pandas·DataFusion에 곧바로 넣을 수 있다.

5) 클라이언트-사이드 store-and-forward

QWP 클라이언트는 서버 응답이 지연되거나 커넥션이 끊겨도 로컬 버퍼(메모리·디스크 옵션 선택)에 쓰기를 보관하고 재접속 시 정상 순서로 흘려보낸다. sf_durability=periodic으로 주기적 디스크 체크포인트를, request_durable_ack=on으로 서버 확정을 기다리는 커밋을 선택할 수 있다. 엔터프라이즈 배포에서는 RPO ≈ 0을 목표로 한다.


아키텍처 개요

QuestDB 10.0: 이진 컬럼 WebSocket 하나로 쓰기와 Arrow 읽기 통합 기존: ILP(쓰기) + PG Wire(읽기) 두 프로토콜 애플리케이션 쓰기 클라이언트 읽기 클라이언트 ILP 텍스트 (포트 9009) PG Wire 행 지향 (포트 8812) QuestDB 서버 두 포트, 별도 파서 • 텍스트→컬럼 변환 오버헤드 • 행→컬럼 결과 변환 낭비 • 두 커넥션 관리·인증 이중화 • Arrow 소비까지 추가 변환 QuestDB 10.0: QWP 단일 이진 프로토콜 (WebSocket over HTTP 9000) 애플리케이션 QWP 클라이언트 (단일 커넥션) Rust·C·C++·.NET·Python 쓰기: 컬럼 프레임 읽기: Arrow 스트림 QuestDB 10.0 서버 HTTP 포트 9000 단일 파서·단일 인증 컬럼 append 직결 • 텍스트 파싱 제거 • 압축 이진 컬럼 프레임 • 파이프라인·양방향 • 클라이언트 SF·HA 내장 • Arrow 그대로 응용 Parquet 파티션 스키마 진화 지원 오브젝트 스토리지 티어링 (Enterprise 4.0) Iceberg / DuckLake 카탈로그 복사 없이 등록 Trino·Spark 조회 벤치마크: 프로토콜 하나로 처리량과 지연 모두 개선 • 쓰기 처리량: ILP 5.3M rows/s → QWP 19M rows/s (약 3.6× / 네트워크 컬럼 이진) • 읽기 처리량: Arrow 스트림 220M rows/s (밀리초급 첫 배치 지연) • 저장 페이로드: 일반 행 기준 3–4× 압축(전송 대역 절감) 클라이언트 Store-and-Forward + HA 페일오버 내장 • 메모리 버퍼: 서버 지연·재접속 시 순서 보존 (프로세스 재시작 시 유지) • 디스크 체크포인트: sf_durability=periodic 로 주기 저장, 프로세스 크래시 후 재개 • 커밋 대기: request_durable_ack=on 로 서버 fsync 확정까지 대기 • 우선순위 엔드포인트 페일오버: 프라이머리를 따라다니며 클러스터 이동 시에도 무손실 → Enterprise 배포에서 RPO ≈ 0 목표. Kafka 없는 신뢰성 있는 수집 파이프의 대안. • ILP·PG Wire는 하위 호환용으로 유지 (기존 도구 계속 동작, QWP는 신규 표준)
QuestDB 10.0 QWP: 한 커넥션에 쓰기와 읽기를 실은 이진 컬럼 스트림

성능 숫자의 의미

숫자 자체보다 그 숫자가 어디서 나오는가를 보는 게 중요하다.

쓰기: 5.3M → 19M rows/s

같은 하드웨어, 같은 워크로드 기준으로 ILP는 초당 5.3M 행이 최대치였고 QWP는 19M 행까지 올린다. 이 3.6배는 두 가지 요인의 합이다.

  1. 파싱 제거: ILP는 라인 단위 텍스트 파싱과 타입 추론이 필요하다. QWP 프레임은 이미 타입이 부여된 컬럼 벡터라 서버 파싱이 사실상 사라진다.
  2. 행→열 재배치 제거: ILP 행은 서버 안에서 컬럼 저장을 위해 재배치된다. QWP는 컬럼 그대로 오기 때문에 append 커서에 곧장 넣는다.

이 두 요인은 CPU 시간뿐 아니라 GC·캐시 미스도 함께 줄인다. 벤치마크에서 서버 CPU 사용률이 동일 처리량 기준으로 크게 낮다고 저자들은 보고한다.

읽기: 220M rows/s

Apache Arrow 스트리밍은 컬럼 벡터를 그대로 클라이언트에 흘려보낸다. PG Wire는 행 단위 이진 인코딩(binary format)을 지원하지만 여전히 행 방향으로 직렬화되고 클라이언트에서 다시 열 지향 자료구조로 재구성해야 한다. QWP는 이 왕복이 없다.

초당 2억 행이라는 숫자는 벤치마크 환경(로컬 저장, 짧은 파티션, 좁은 스키마)에서의 최상단이지만, 실무적으로 중요한 것은 첫 배치 지연이 밀리초급이라는 점이다. 대시보드 로딩·에이전트 응답 루프에서 이 값이 실제 사용자 경험을 결정한다.

페이로드 압축

QWP 프레임은 컬럼별 특성에 맞는 압축을 선택한다. 정수·타임스탬프에는 델타+비트팩 계열이, 문자열에는 사전+RLE가 유리하다. 실측에서 일반적인 행 기준 페이로드가 3–4배 작다. 5G 셀룰러·비대칭 업스트림·클라우드 이그레스 비용이 큰 환경에서 이 절감은 프로토콜 전환의 실질적 이유가 된다.


하위 호환: ILP·PG Wire는 남는다

QuestDB 10.0은 기존 두 프로토콜을 없애지 않는다. 세 가지 이유가 있다.

  • 기존 IoT/Telegraf/InfluxDB 클라이언트. ILP는 시계열 세계의 사실상 표준이고, 수많은 에이전트가 이 프로토콜만 안다.
  • PostgreSQL 도구 생태계. psql·pgAdmin·JDBC/ODBC·다양한 BI 도구가 PG Wire를 통해 붙는다.
  • 점진적 마이그레이션. QuestDB는 프로덕션 시계열 시스템의 코어에 있는 경우가 많다. 프로토콜 교체는 배포 창을 필요로 하므로 강제 이관은 리스크가 크다.

결과적으로 10.0에서는 세 프로토콜이 공존한다. QWP는 신규 표준이고 새 통합·에이전트·Arrow 소비 시나리오에 권장된다. ILP와 PG Wire는 기존 도구·기존 통합에 그대로 유효하다.


Parquet·Iceberg·DuckLake와의 연결

10.0은 프로토콜만 바꾼 게 아니다. 저장·티어링·카탈로그까지 함께 손봤다.

  • Parquet 파티션 1급 시민: 파티션을 Parquet 파일로 유지하며 스키마 진화를 지원. QuestDB가 쓴 파일은 Arrow/Parquet 생태계가 그대로 읽는다.
  • 오브젝트 스토리지 티어링(Enterprise 4.0): 오래된 파티션을 S3/GCS/Azure Blob으로 내리고, 여전히 SQL로 조회 가능. 콜드 데이터의 저장 비용을 크게 낮춘다.
  • Iceberg/DuckLake 등록: 티어링된 파티션은 데이터 이동 없이 Iceberg 또는 DuckLake 카탈로그에 등록해 Trino·Spark·DataFusion에서 함께 조회할 수 있다.

이 조합의 함의는 명확하다. QuestDB는 핫 시계열 저장소를, 카탈로그와 오브젝트 스토리지는 콜드 분석 계층을 담당한다. 두 계층이 하나의 저장 표현(Parquet)과 하나의 통신 표현(Arrow)으로 만나기 때문에, 지금까지 ETL·역-ETL로 옮겨 다니던 데이터가 자연스럽게 하나의 시스템 안에 머문다.


새 기능들: Live Views와 그 밖의 변화

프로토콜 이외에도 10.0은 몇 가지 운영자용 기능을 추가했다.

Live Views (Beta)

Live Views는 윈도 함수 결과를 밀리초 단위 지연으로 증분 유지한다. Materialized View가 집계(GROUP BY) 결과에 적합하다면, Live View는 롤링 카운트·리드 랭킹·오더북 파생값 같은 실시간 윈도 계산을 위한 것이다. 대시보드가 매번 큰 윈도를 재계산하지 않고 유지된 결과를 조회하도록 만들 수 있다. 아직 베타이므로 프로덕션 도입 시에는 재계산 부담·저장 크기·정확도 경계를 실측해야 한다.

브레이킹 체인지 목록

이관 시 반드시 확인해야 할 항목들:

  • SHOW PARTITIONS가 뒤에 seqTxnisRemotelyServed 두 컬럼을 추가한다. 파싱 로직이 컬럼 개수에 의존하면 깨진다.
  • pg_class.relkindinformation_schema.tables.table_type이 이제 객체 종류별 값을 정확히 리포트한다. 뷰가 TABLE로 오는 상황을 가정한 코드는 조정 필요.
  • HTTP/CSV의 EXPLAIN 결과가 HTML 이스케이프 없이 순수 텍스트로 반환된다.
  • 오버플로우가 발생하는 범위 윈도 프레임은 컴파일 타임에 거부된다. 이전에는 런타임 이상 동작이었다.
  • 쿼리 취소·타임아웃·연결 끊김 처리가 모든 경로에서 일관되게 지원된다.

서빙·데이터 엔지니어 관점의 함의

게이트웨이·프록시 배포에서의 이득

WebSocket 위에서 동작하는 이진 프로토콜은 L7 프록시(Envoy·Nginx·AWS ALB) 뒤에서 통상적으로 처리 가능하다. TLS 종단·JWT 인증·rate limit 같은 기존 게이트웨이 정책을 그대로 얹을 수 있다. ILP는 TCP·UDP 모드에서 이런 정책 적용이 까다로웠고, PG Wire는 커스텀 TCP 파서를 프록시가 갖고 있어야 했다.

Kafka를 뺄 수 있는가

Store-and-Forward 클라이언트와 서버 확정 커밋(request_durable_ack=on)이 결합되면 경량 이벤트 파이프에서 Kafka를 빼는 것이 가능해진다. 조건은 세 가지다.

  1. 이벤트가 단일 목적지(QuestDB)로만 흘러도 되는 경우
  2. 소비자가 Kafka 컨슈머 그룹 시맨틱을 필요로 하지 않는 경우
  3. 재처리 정책이 QuestDB의 파티션 재조회로 충분한 경우

이 조건을 만족한다면 Kafka 없이도 무손실 수집이 가능하다. 반면 여러 컨슈머가 fan-out 되는 이벤트 버스나 스트림 조인이 필요한 경우엔 여전히 Kafka·Redpanda가 필요하다.

AI/에이전트 워크로드

에이전트가 직전 쓰기를 즉시 조회하는 패턴은 QuestDB의 자연스러운 사용처가 되고 있다. 도구 호출 결과를 이벤트로 쌓아 두고, 다음 결정에 필요한 최근 윈도우를 SQL로 조회한다. QWP가 있으면 같은 커넥션·같은 인증으로 이 왕복이 처리된다. Arrow 결과를 그대로 Polars에 넣어 계산까지 이어갈 수 있어, 에이전트 프레임워크의 로컬 분석 층에 붙이기가 훨씬 단순해진다.


도입 체크리스트

  • [ ] 현재 시스템에서 ILP·PG Wire·HTTP JSON 트래픽 비중을 계량했는가 (마이그레이션 우선순위 판단)
  • [ ] 사용 중인 클라이언트 라이브러리에 QWP 지원이 도착했는지 확인했는가 (Rust·C·C++·.NET·Python 순서로 릴리스)
  • [ ] SHOW PARTITIONS 컬럼 수 변경에 의존하는 스크립트가 있는가
  • [ ] pg_class.relkind 값의 정확한 리포팅으로 깨지는 뷰 감지 로직이 있는가
  • [ ] Store-and-Forward의 디스크 체크포인트 위치와 용량 한계를 설정했는가
  • [ ] QWP의 TLS 종단이 기존 게이트웨이 정책과 어떻게 상호작용하는지 검증했는가
  • [ ] Live Views 도입 후보 워크로드(윈도 계산이 반복되는 대시보드/알림)를 식별했는가
  • [ ] Parquet 파티션 티어링과 Iceberg/DuckLake 카탈로그 등록 정책을 정의했는가
  • [ ] 기존 ILP·PG Wire 클라이언트의 EOL 계획을 세웠는가 (강제 이관 아님, 단계적 이관)

한계와 열린 문제

다중 목적지 fan-out

QWP는 여전히 점대점 프로토콜이다. 하나의 이벤트를 여러 목적지로 신뢰성 있게 흘려보내려면 Kafka 같은 브로커가 여전히 필요하다. QuestDB는 이 지점을 자기 역할이 아니라고 명시적으로 선을 그었다.

표준화

QWP는 QuestDB 전용이다. Apache Arrow Flight SQL·ADBC 같은 다중 벤더 표준이 이미 존재하는 상황에서, QWP의 성능 이득이 벤더 락인을 감수할 만큼 큰지는 사용 사례에 따라 다르다. 특히 이미 Arrow Flight SQL로 여러 시스템을 연결한 조직에는 QWP 도입 시점을 신중히 잡아야 한다.

툴체인 성숙도

10.0은 프로토콜이 도착한 첫 릴리스다. 클라이언트 라이브러리·프록시 지원·모니터링 통합이 점진적으로 채워지는 중이다. 첫 6개월은 초기 도입자 리스크가 있다.

관측성

QWP 트래픽의 관측성이 얼마나 세분화되어 있는지가 운영 관점에서 중요한데, 릴리스 노트만으로는 아직 세부가 명확하지 않다. 프레임 단위 지표·에러 카운터·backpressure 신호가 Prometheus로 노출되는지, 서버 측 슬로우 쿼리 로깅과 어떻게 연결되는지가 실전에서 확인해야 할 부분이다.


정리

QuestDB 10.0은 시계열 데이터베이스가 핫 이벤트 저장소 + 실시간 분석 표면이라는 하나의 시스템으로 정의될 수 있음을 보여준다. QWP는 이 정의를 프로토콜 층에서 실현한다. 한 커넥션·한 인증·한 파서로 쓰기와 Arrow 읽기를 함께 처리하며, 기존 HTTP 포트 위에서 프록시·인증 스택과 자연스럽게 어우러진다.

성능 숫자(5.3M → 19M rows/s 쓰기, 220M rows/s Arrow 읽기)는 인상적이지만, 더 중요한 것은 아키텍처 단순화의 부수 효과다. 두 프로토콜을 유지하며 발생하던 클라이언트 이중 관리·인증 이중화·Arrow 재변환이 사라지고, 그 자리를 store-and-forward·HA 페일오버·Parquet 티어링·Iceberg 등록 같은 실전 기능이 채운다.

여전히 QWP는 벤더 특화이고 표준화되지 않았지만, 시계열 도메인에 국한하면 이 트레이드오프는 명확하다. 에이전트·실시간 분석·Kafka 회피 이 세 축의 워크로드에서 10.0은 실질적으로 새로운 기본값을 제시한다. ILP·PG Wire를 계속 유지한 결정 덕분에 이관은 강제되지 않지만, 새로 만드는 파이프에는 QWP를 기본으로 두는 것이 자연스러운 시점이 왔다.


References

  • QuestDB 10.0 announcement — "QWP, one binary streaming protocol for writes and Arrow reads": https://questdb.com/blog/questdb-10-release/
  • QuestDB 10.0.0 GitHub release notes: https://github.com/questdb/questdb/releases/tag/10.0.0
  • QWP vs ILP ingestion benchmark: https://questdb.com/blog/qwp-vs-ilp-ingestion-benchmark/
  • QWP protocol deep dive: https://questdb.com/blog/questdb-qwp-binary-wire-protocol/
  • QuestDB Wire Protocol (QWP) implementation PR #6800: https://github.com/questdb/questdb/pull/6800
  • QuestDB release notes index: https://questdb.com/release-notes/
  • Apache Arrow columnar format spec: https://arrow.apache.org/docs/format/Columnar.html
  • Apache Iceberg spec: https://iceberg.apache.org/spec/
  • DuckLake catalog format: https://ducklake.select/