JSON 문서를 직렬화된 B-tree로 직접 인코딩할 수 있음
이렇게 하면 인덱싱된 속도로 내부 필드를 바로 조회할 수 있고, 문서 자체가 이미 인덱싱되어 있으므로 파싱이 필요 없게 됨
이 포맷을 Lite³라 부름. 내가 직접 작업 중인 프로젝트임 GitHub 링크
정말 멋짐! 나는 Rkyv를 좋아하지만 Rust가 필요해서 작은 프로젝트에는 부담이 있었음
Lite³는 lite3_val_bytes로 바이너리 데이터를 지원하는 점이 특히 마음에 듦
Lite³가 PostgreSQL의 JSONB와 어떻게 다른지 궁금했음
JSONB는 배열 길이와 오프셋을 함께 인코딩해서 압축 효율과 속도 사이를 조절할 수 있음
Lite³는 in-place 업데이트가 가능하지만, 민감 데이터가 남지 않도록 주기적으로 “vacuum”이 필요함
JSONB는 재인코딩 없이는 업데이트가 어렵지만, Lite³는 구조 순회만으로 간단히 정리 가능함
압축성은 JSONB가 더 좋을 것 같지만, Lite³의 설계는 매우 영리한 접근이라 생각함
나도 ASN.1 컴파일러를 유지보수 중이라 이런 직렬화 포맷에 관심이 많음. Lite³ 덕분에 새로운 아이디어를 얻었음
Rust 버전의 구현이 있으면 정말 좋겠음
SQLite를 정말 좋아하지만, 분석용으로는 DuckDB를 더 자주 쓰고 있음
DuckDB는 SQLite처럼 단일 파일을 사용하면서도 대용량 데이터셋을 극도로 빠르게 처리함
M2 맥북에서 2천만 건 데이터를 다루는데도 매우 빠름
예를 들어 다음 쿼리로 JSON 파일을 바로 읽을 수 있음
SELECT avg(sale_price), count(DISTINCT customer_id)
FROM '/my-data-lake/sales/2024/*.json';
또한 JSON 타입 컬럼을 로드해 Postgres 스타일의 col->>'$.key' 문법도 사용 가능함
첫 번째 쿼리가 파일 시스템의 JSON 파일들을 즉석에서 인덱싱하는 건지 궁금함
DuckDB는 시각화 도구 pygwalker와 함께 쓰면 수백만 건의 데이터를 몇 초 만에 분석할 수 있음
다만 SQLite와 비교하는 건 약간 불공평함. SQLite는 시스템 구축용, DuckDB는 분석 전용으로 쓰는 게 맞음
여러 플랫폼에 배포할 때 DuckDB는 다소 까다로움
데이터셋을 압축하지 않고 저장하는 일은 절대 피해야 함. DuckDB는 다양한 압축 포맷을 지원함
JSON 성능을 위해 Generated Column을 쓰는 건 흔한 방식이라 생각했음
Postgres에서도 JSON 컬럼 안의 키를 외래키로 유지하기 위해 이렇게 사용한 적이 있음. 약간 ‘저주받은’ 방식이지만 잘 작동했음
Postgres라면 JSONB 내부 필드에 직접 인덱스를 걸 수 있지 않음?
예를 들어
CREATE INDEX idx_status_gin
ON my_table
USING gin ((data->'status'));
흥미롭지만, SQLite의 "Index On Expression"을 써도 되지 않음?
예를 들어 CREATE INDEX idx_events_type ON events(json_extract(data, '$.type'))
다만 JSON path 문법이 조금만 달라져도 인덱스를 사용하지 않을 수 있음. 반면 Virtual Generated Column은 항상 인덱스를 보장함
JSON path 구문이 조금만 달라도 인덱스가 깨질 수 있음
예를 들어 json_extract(data, "$.type")과 data -> '$.type'은 서로 다르게 인식됨
즉, WHERE 절의 표현식이 바뀌면 인덱스가 무용지물이 됨
단순하고 빠른 해결책임. 쿼리와 인덱스가 일치해야 한다는 건 언제나 사실임
표현식 인덱스 기능은 SQLite 3.9.0(2015-10-14)부터 추가된 비교적 새로운 기능임
개발자들이 일관된 스키마가 있음에도 데이터를 전부 JSON(B) 컬럼에 넣는 걸 자제했으면 함
인덱스 설정, 제약 조건 관리가 어려워지고, 실제 사용 시 오버헤드가 커짐
JSON 컬럼은 트리 구조처럼 테이블로 표현하기 어려운 데이터를 다룰 때 빛을 발함
예를 들어 Haskell+TypeScript 환경에서는 복잡한 중첩 구조를 JSON으로 직렬화하는 게 훨씬 편리함
JSON(B)는 서로 다른 타입의 데이터를 한 컬렉션에 담을 때 유용함
예: 여러 결제 프로세서의 결과를 하나의 테이블에 저장하거나, 클래스파이드 사이트에서 품목별로 다른 속성을 다룰 때
C#이나 JS/TS에서도 타입 검증 도구(Zod, OpenAPI 등)를 함께 쓰면 관리가 쉬움
단순한 JSON이라면 정규화가 낫지만, 복잡한 API 응답은 테이블로 풀면 JOIN 지옥이 됨
결국 유지보수성과 성능의 균형 문제임. 이 글의 핵심은 JSON에도 인덱스를 쉽게 걸 수 있다는 점임
센서 데이터처럼 트리 구조 전체를 한 번에 다루는 경우, JSON 컬럼이 훨씬 간단함
읽기 성능은 인덱스로 충분히 보완 가능함
완전한 정규화는 비효율적일 때가 많음
예를 들어 제품 가격 설정 시스템에서 고객별 특이한 할인 규칙을 JSON으로 표현하면 훨씬 유연함
JSON 대신 XML을 쓴다면, 이는 90~00년대의 문서형 데이터베이스(document store) 와 동일한 모델임
삽입·갱신 시 파싱하고, 쿼리 시 인덱스만 접근하는 구조였음
SQLite가 이런 기능을 기본으로 제공한다는 점이 정말 흥미로움
Hacker News 의견들
JSON 문서를 직렬화된 B-tree로 직접 인코딩할 수 있음
이렇게 하면 인덱싱된 속도로 내부 필드를 바로 조회할 수 있고, 문서 자체가 이미 인덱싱되어 있으므로 파싱이 필요 없게 됨
이 포맷을 Lite³라 부름. 내가 직접 작업 중인 프로젝트임
GitHub 링크
Lite³는
lite3_val_bytes로 바이너리 데이터를 지원하는 점이 특히 마음에 듦JSONB는 배열 길이와 오프셋을 함께 인코딩해서 압축 효율과 속도 사이를 조절할 수 있음
Lite³는 in-place 업데이트가 가능하지만, 민감 데이터가 남지 않도록 주기적으로 “vacuum”이 필요함
JSONB는 재인코딩 없이는 업데이트가 어렵지만, Lite³는 구조 순회만으로 간단히 정리 가능함
압축성은 JSONB가 더 좋을 것 같지만, Lite³의 설계는 매우 영리한 접근이라 생각함
나도 ASN.1 컴파일러를 유지보수 중이라 이런 직렬화 포맷에 관심이 많음. Lite³ 덕분에 새로운 아이디어를 얻었음
SQLite를 정말 좋아하지만, 분석용으로는 DuckDB를 더 자주 쓰고 있음
DuckDB는 SQLite처럼 단일 파일을 사용하면서도 대용량 데이터셋을 극도로 빠르게 처리함
M2 맥북에서 2천만 건 데이터를 다루는데도 매우 빠름
예를 들어 다음 쿼리로 JSON 파일을 바로 읽을 수 있음
또한 JSON 타입 컬럼을 로드해 Postgres 스타일의
col->>'$.key'문법도 사용 가능함다만 SQLite와 비교하는 건 약간 불공평함. SQLite는 시스템 구축용, DuckDB는 분석 전용으로 쓰는 게 맞음
여러 플랫폼에 배포할 때 DuckDB는 다소 까다로움
JSON 성능을 위해 Generated Column을 쓰는 건 흔한 방식이라 생각했음
Postgres에서도 JSON 컬럼 안의 키를 외래키로 유지하기 위해 이렇게 사용한 적이 있음. 약간 ‘저주받은’ 방식이지만 잘 작동했음
예를 들어 참고 블로그
JSON 스키마가 바뀌면 파싱이나 마이그레이션이 실패할 수 있음
최근 Claude Code가 제안한 최적화 예시를 통해 이 기법을 알게 되었음
SQLite의 새로운 기능을 몰라서 놓쳤던 부분이었는데, 성능 향상이 꽤 컸음
교훈은, 익숙한 도구라도 주기적으로 문서를 다시 읽어보는 게 좋다는 것임
2023년 HN에서 bambax의 댓글을 보고 블로그 글을 쓰게 되었음
원문 댓글 링크
JSON을 직접 투영하지 않고도 인덱스를 만들 수 있지만, 계산된 컬럼(computed column) 은 쿼리를 단순하게 만들어줌
MS-SQL 2025(v17) 이전에는 JSON 지원이 제한적이라 이 방식이 필수였음
HN에서 기사를 열었는데 내 댓글이 인용되어 있고, 그 댓글이 글의 주제라니 신기한 경험이었음
“고마워요, bambax!”라는 문구를 보고 미소가 나왔음. SQLite는 정말 멋진 도구임
흥미롭지만, SQLite의 "Index On Expression"을 써도 되지 않음?
예를 들어
CREATE INDEX idx_events_type ON events(json_extract(data, '$.type'))다만 JSON path 문법이 조금만 달라져도 인덱스를 사용하지 않을 수 있음. 반면 Virtual Generated Column은 항상 인덱스를 보장함
예시: recordlite 프로젝트
예를 들어
json_extract(data, "$.type")과data -> '$.type'은 서로 다르게 인식됨즉, WHERE 절의 표현식이 바뀌면 인덱스가 무용지물이 됨
개발자들이 일관된 스키마가 있음에도 데이터를 전부 JSON(B) 컬럼에 넣는 걸 자제했으면 함
인덱스 설정, 제약 조건 관리가 어려워지고, 실제 사용 시 오버헤드가 커짐
예를 들어 Haskell+TypeScript 환경에서는 복잡한 중첩 구조를 JSON으로 직렬화하는 게 훨씬 편리함
예: 여러 결제 프로세서의 결과를 하나의 테이블에 저장하거나, 클래스파이드 사이트에서 품목별로 다른 속성을 다룰 때
C#이나 JS/TS에서도 타입 검증 도구(Zod, OpenAPI 등)를 함께 쓰면 관리가 쉬움
결국 유지보수성과 성능의 균형 문제임. 이 글의 핵심은 JSON에도 인덱스를 쉽게 걸 수 있다는 점임
읽기 성능은 인덱스로 충분히 보완 가능함
예를 들어 제품 가격 설정 시스템에서 고객별 특이한 할인 규칙을 JSON으로 표현하면 훨씬 유연함
JSON 대신 XML을 쓴다면, 이는 90~00년대의 문서형 데이터베이스(document store) 와 동일한 모델임
삽입·갱신 시 파싱하고, 쿼리 시 인덱스만 접근하는 구조였음
SQLite가 이런 기능을 기본으로 제공한다는 점이 정말 흥미로움