요약
DuckDB 2.0은 쿼리를 고치지 않아도 빨라지지만, 속도를 다 얻으려면 데이터 모양을 맞춰야 합니다.
마더덕 블로그에 실린 글입니다. 필자는 올가을 나올 DuckDB 2.0 알파판과 1.5.5를 노트북 한 대와 S3에서 비교했습니다. 엔진 개발자가 아니라 테이블과 파이프라인을 만드는 사람의 눈으로 달라진 점을 정리했습니다.
왜 중요한가
- S3 읽기와 재귀 CTE 개선은 쿼리를 한 줄도 바꾸지 않아도 효과가 납니다.
- 작은 파일이 잔뜩 쌓인 데이터 레이크나 값 형식이 들쭉날쭉한 JSON은 2.0으로도 빨라지지 않습니다.
- JSON을 문자열 대신 VARIANT로 두면 스키마를 미리 정하지 않아도 일반 열에 가까운 속도를 얻습니다.
핵심 내용
- S3의 2.2GB Parquet 파일 읽기는 전용 스레드가 미리 내려받아 두는 방식 덕에 18.8초에서 7.7초로 줄었습니다.
- 재귀 CTE(부모·자식 관계를 단계별로 따라가는 SQL 구문)는 매 단계 표 전체를 다시 읽지 않고 방금 찾은 행만 따라갑니다.
- 커밋 2만 개짜리 git 이력 탐색은 1.8~16초에서 0.10초로 줄었지만, 조직도처럼 얕은 계층은 효과가 적습니다.
- VARIANT는 자주 나오는 필드를 실제 하위 열로 나눠 저장해, JSON 문자열보다 약 6배 빠르고 2.7배 작습니다.
- 같은 필드에 숫자와 문자열이 섞이면 하위 열로 나뉘지 않고, 리스트 형 변환은 알파판에서 오히려 JSON보다 느립니다.
HN 반응
- 시각화는 좋지만 문장에 LLM이 쓴 티가 난다는 지적이 가장 많았고, 필자가 영어 원어민이 아니라 어색할 뿐이라는 반론도 나왔습니다.
- 트리거 같은 큰 기능을 짧게 넘겼다는 불만과, 재귀 CTE의 원리는 DuckDB 공식 블로그가 더 잘 설명한다는 보충이 이어졌습니다.
저도 시각화는 정말 마음에 드는데, 글투에서 LLM 냄새가 진하게 나요.
이런 식이에요.
회사에서 이런 문체를 해독하려다 보면 머리가 어질어질해지는데1, 다른 곳에서까지 보게 되니 싫네요. 제가 틀렸다면 죄송합니다. 하지만 맞다면, 글쓴이께서는 글을 LLM한테 맡기지 마세요. 독자 건강에 해롭습니다2.
AI 티 나는 부분이 눈에 띄긴 했지만, 전반적으로는 글이 그렇게 나쁘지 않았습니다. 다만 군데군데 횡설수설하는 느낌은 있었습니다.
"구제해 주지는 않는다(does not rescue it)"는 표현은 사람이라면 저렇게 쓰지 않습니다.
저는 그 테이블을 모르는데요.
이 문장도 있습니다.
이건 정말 읽어내기 어렵습니다.
재귀 CTE를 다룬 부분은 글이 잘 안 짜여 있고 최적화를 어떻게 했는지도 설명하지 않았습니다. 재귀 CTE가 어떻게 개선됐는지는 이 글에 나와 있습니다. https://duckdb.org/2026/08/25/how-duckdb-runs-recursive-ctes-faster
저는 이 부분이 이해가 안 가요. LLM은 사람이 쓴 글로 학습한다면서요. 그런데 왜 이렇게 비현실적인 문장이 나오는 걸까요? 티가 확 나게 하려고 회사들이 일부러 그러는 건가요?
사람이 쓴 글만으로 학습하는 게 아닙니다. RLHF를 거치고, 검증 가능한 보상을 쓰는 강화학습을 거치면서 언어 자체도 달라집니다.
글을 잘 쓰게 만드는 건 정말 어렵습니다. 좋은 글인지 아닌지를 검증할 확실한 방법이 없으니까요. 당신과 저는 구분할 수 있지만, 우리의 판단을 그대로 담은 검증기를 만들 수는 없습니다.
앞으로 개선할 방법을 찾을 수도 있겠지만, 지금으로서는 LLM이 풀기 가장 어려운 문제 가운데 하나인 것은 분명합니다.
제 생각에는 LLM이 마음 이론(theory of mind)도 약한 것 같은데, 이것 역시 학습시키기 어려운 부분일 겁니다.
왜 어렵습니까? 20세기 중반 영어 문체로, 전문가답게, 낚시성 글투는 쓰지 말고 쓰라고 시키면 됩니다.
어쨌든 다른 LLM은 이런 문제가 자동으로 생기지 않을 수도 있고, 그런 프롬프트가 아예 필요 없을 수도 있습니다. 이건 Claude의 문제입니다.
20세기 중반이 얼마나 오래전인지 모르시는 것 같네요.
문체가 얼마나 오래됐는지가 무슨 상관입니까? 디킨스처럼, 킹 제임스 성경처럼, 심지어 카이사르의 라틴어처럼 쓰라고 시킬 수도 있는데, 그건 훨씬 더 오래됐잖아요.
공정하게 말하면, 그런 오래된 문체 중에는 독자를 멀어지게 할 만한 것도 있지만, 20세기 중반 문체는 사실상 지금 우리 글과 비슷합니다. 강조하자면 이건 그 시대의 영어가 아니라 그 시대의 문체를 적용한다는 이야기입니다.
그 프롬프트가 실제로 먹힌다는 걸 모르시는 것 같네요. '문체(style)'라는 단어가 효과를 냅니다. 낚시성 글을 너무 좋아하시나 봐요.
자기회귀 모델(호스팅되는 건 사실상 전부 그렇습니다)은 다음 토큰 예측이라는 성질 때문에 그렇습니다. LLM은 문장 구조를 미리 정해 버리고는 나머지를 짐작해서 써야 합니다. 샘플러는 각 다음 토큰의 확률 말고는 LLM의 "의도"를 알 길이 없고, LLM도 그 확률을 낳은 자신의 이전 "의도"를 알 길이 없습니다. 학습을 더 시킨다고 해결될지는 모르겠습니다. 확산 언어 모델 연구를 더 하는 식으로, 근본적인 아키텍처 전환이 필요하지 않을까 싶습니다.
그건 꽤 센 주장 같습니다. 그게 심각한 문제라면, 요즘 LLM은 연쇄 추론(chain-of-thought) 덕분에 문장 구조를 다듬어 볼 자기만의 비공개 메모장이 있잖아요.
오히려 그 반대입니다. 원어민에게는 부자연스럽겠지만 글쓴이는 원어민이 아닌 것 같습니다. 영어를 제2외국어로 쓰는 사람은 모국어 표현을 문자 그대로 옮긴 표현을 쓰는 일이 드물지 않고, 그런 표현은 뜻은 통해도 원어민에게는 이상하게 들릴 수 있습니다.
글쓴이는 제가 보기에도 영어가 모국어가 아닌 사람 같은데, 노골적인 LLM 문체도 섞여 있습니다. LLM을 번역이나 초안 작성에 썼을 수도 있습니다. 예를 들면 이렇습니다.
첫 문장은 순전히 비원어민 영어 같고, 두 번째 문장은 LLM 같습니다.
여기서도 두 번째 문장은 LLM 같습니다.
이 부분은 비원어민의 흔적이 전혀 남지 않은 LLM 글로 읽힙니다.
종합하면 요즘 흔한 패턴 같습니다. 글 맨 앞에는 사용자 입력이 가장 많이 담겨 있고, 나머지는 대부분 생성된 글이죠. 걸리는 점은 글쓴이가 비원어민으로 보인다는 것뿐인데, 그래도 곳곳에 LLM 티가 납니다.
DuckDB 공식 글에도 LLM 티 나는 부분이 많다니 웃기네요.
최근에 Claude가 이런 문체로 쓴, 도무지 이해가 안 가는 글을 내놓은 적이 셀 수 없이 많습니다. 그래서 일부를 다시 써 달라고 하면, 처음에 자기가 한 말이 사실 딱히 맞지 않았다고 털어놓더군요.
이제는 이런 문체를 사람이 대충 썼다는 신호일 뿐 아니라 LLM이 대충 했다는 신호로도 읽게 됩니다. 몇 문장짜리 프롬프트 하나로 4문단 이상을 생성할 때 가장 흔한 것 같고, 출력이 길수록 확률이 나빠집니다. 결과로 나온 문단을 하나씩 파고들어 읽을 수 있게 다듬어 달라고 하면, 훨씬 공들여 깊이 파고듭니다.
저도 여러 번 겪었습니다. 제가 잘 이해하지 못하는 말(복잡하거나, 전문용어거나, 맥락에 안 맞거나, 어쨌든 말이 안 되는 것)을 해서 설명해 달라고 하면, 자기가 틀렸다는 걸 알아챕니다. 그리고 이런 경험을 하는 사람이 점점 많아지는 모양입니다. 왜 계속 이런 일이 생기는 걸까요?
적어도 Claude가 하는 말을 제대로 이해하는 것이 중요하다는 점은 분명해집니다. 이해가 안 된다면 틀렸을 가능성이 높으니까요. Claude가 하는 말을 이해하지 못한다고 해서 자신이 어리석다고 생각하지 마세요.
정말 읽을 수가 없어요. AI 요약본으로 대충 훑으라는 뜻인가 봐요. 그러면 시각화는 영영 못 보게 될 텐데, 아쉽네요.
좋은 글이네요!
2.0에서 재귀 CTE 성능이 훨씬 좋아진 건 사실이지만, 그래프 데이터용으로 이미 아주 훌륭한 DuckDB 확장이 있다는 것도 잊지 마세요. 일반적인 RDBMS에게는 꽤 까다로운 부류의 쿼리를 이 확장이 얼마나 우아하게 처리하는지 다룬 2025년 글이 있습니다. https://duckdb.org/2025/10/22/duckdb-graph-queries-duckpgq
시각화가 훌륭하네요. 참고로 새 C++ 확장 API도 확장을 개발하고 배포하는 쪽에서 보면 더 빠를 겁니다.
더 많은 데이터베이스 엔진이 Umbra / CedarDB처럼 태스크 기반 설계를 썼으면 좋겠습니다.
세상에 있는 DB 엔진 대부분은 아직도 교환(exchange) 연산과 부실한 비동기 I/O 관리를 곁들인 "n개 스레드" 방식의 병렬성을 쓰는 것 같습니다.
DuckDB가 이 부분을 개선하고 있긴 하지만, 어떤 면에서는 이미 수십 년 된 R&D(와 구현!)를 뒤쫓는 중입니다.
이런 시스템을 만드는 개발자들에게 제가 즐겨 던지는 "수사적 도전"이 있습니다. 코어가 1,024개이고 네트워크와 스토리지 대역폭도 그에 맞게 갖췄지만 지연 시간은 상당히 큰 컴퓨터를 드린다면, 쿼리 하나로 이런 시스템을 100% 가동 상태로 유지할 수 있겠습니까?
거의 모든 소프트웨어의 답은 "아니오"입니다.
예를 들어 SQL Server는 쿼리 하나당 하드웨어 스레드를 64개까지만 씁니다: https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-max-degree-of-parallelism-server-configuration-option?view=sql-server-ver17#considerations
GPU 코드는 이 수준에 다가가고 있지만, CPU 코드는 컴퓨터 과학의 이 최전선에서 한참 뒤처져 있습니다.
데이터베이스만의 문제가 아닙니다! 파일을 병렬로 (압축) 해제할 수 있습니까? 해시를 병렬로 검증할 수 있습니까? CPU 와 I/O 태스크 병렬성을 함께 써서 스토리지에서 업로드/다운로드할 수 있습니까? 이 모든 작업을 겹쳐서, 굳이 기다릴 필요가 없는 일은 아무것도 기다리지 않게 만들 수 있습니까?
이건 중요합니다! 생물정보학 코드로 테스트를 해 봤는데, 대부분이 타르 구덩이(tar pit)에 빠져 있었습니다. 상당수는 CPU 코어를 아무리 쏟아부어도 수백만 IOPS의 최신 SSD나 수백 기가비트 처리량의 최신 네트워크까지 확장하지 못했습니다.
추신: AMD의 Zen 6 세대 EPYC 9006 프로세서는 2소켓 시스템당 코어 512개, 스레드 1,024개를 갖출 예정이니 가설 속 이야기가 아닙니다: https://www.amd.com/en/products/processors/server/epyc/9006-series/amd-epyc-9996.html
말씀하신 두 데이터베이스 모두 오픈 소스가 아닙니다. 말만 많고 볼 건 없네요.
해시가 파일 전체를 대상으로 한다면 암호학적 해시 연산은 병렬화할 수 없습니다.
청크 단위로 해시하거나, 다른 종류의 해시를 쓰면 가능할지도 모르겠지만요.
할 수 있습니다: https://en.wikipedia.org/wiki/Merkle_tree
리프 노드와 그 위의 모든 노드를 병렬로 계산할 수 있습니다.
노드 크기가 보통 4 KB쯤이라면, 4 MB 이상인 파일은 어떤 것이든 코어 1,024개를 다 쓸 수 있습니다.
다음 주피터 노트북에서 써 볼 생각입니다.
AI가 쓴 글이라도 괜찮다 싶었는데, 2.0에 트리거가 추가됐다는 사실을 글이 느린 연결에서의 S3 파일 접근용 워커 최적화에 비하면 별것 아닌 것으로 취급하는 대목에서 마음이 돌아섰어요. 아니죠. 릴리스 노트는 제가 직접 읽겠습니다.
저도 똑같은 댓글을 달려던 참이었어요. 글 전체를 S3 이야기와 재귀 CTE 설명으로 채워 놓고, 트리거는 대충 넘어간다고요?
...Atlas와 Fable이 번갈아 가며 (소스 코드와 런타임을) 오가면서 병목으로 의심되는 곳을 추적했기 때문이죠.
제 생각에는 충분히 타당한 기법입니다. 작년 기술로도 LLM은 사소한 세부 사항을 종합하는 데 정말 뛰어났습니다. 컴퓨터와 프로그래밍에 관해 기록된 모든 것에 대한 백과사전식 지식에, 임시 실험을 돌리고 테스트 인프라를 구축하는 무한한 능력까지 더해져서 디버깅과 성능 최적화에 아주 능숙했습니다.