AWS 기술 블로그
JSON 데이터베이스로서의 PostgreSQL: 고급 패턴 및 모범 사례
JSON 데이터베이스로서의 PostgreSQL: 고급 패턴 및 모범 사례
이 글은 AWS Database Blog에 게시된 PostgreSQL as a JSON database: Advanced patterns and best practices by Ezat Karimi을 한국어 번역 및 편집하였습니다.
최신 애플리케이션에는 적응형 데이터 모델이 필요합니다. 예를 들어, 전자 상거래 제품 카탈로그는 다양한 속성과 끊임없이 변화하는 요구사항을 가지고 있습니다. 이러한 문제를 마이그레이션 없이 처리할 수 있는 데이터베이스를 어떻게 설계할 수 있을까요?
JSON은 데이터 형태에 맞춰 조정되므로 고정된 스키마의 필요성이 줄어듭니다. 애플리케이션이 성장함에 따라 데이터 모델도 자연스럽게 진화합니다.
PostgreSQL은 ACID 규정 준수 및 고급 인덱싱을 갖춘 JSON 기능을 제공하므로 JSON 워크로드를 위한 전용 NoSQL 솔루션의 매력적인 대안이 됩니다. Amazon Relational Database (Amazon RDS) for PostgreSQL, Amazon Aurora PostgreSQL-Compatible Edition 같은 완전 관리형 서비스를 통해 AWS에 배포하면 엔터프라이즈급 기능을 제공하고 다른 AWS 서비스와 원활하게 통합되며 기능을 더욱 향상시킬 수 있습니다.
PostgreSQL의 JSON 기능
PostgreSQL은 두 가지 기본 데이터 유형을 통해 강력한 JSON 지원을 제공합니다.
- JSON — JSON 데이터 유형은 각 작업 마다 구문 파싱을 통해 입력 텍스트의 정확한 사본을 저장하며, 인덱싱 기능이 부족하고 접근할 때마다 파싱이 다시 필요하다는 특징이 있습니다.
- JSONB — JSONB 데이터 유형은 더 효율적인 처리를 위해 최적화된 바이너리 형식으로 데이터를 저장하지만, 삽입 작업에서는 속도가 약간 느립니다. JSONB는 광범위한 기능을 제공합니다.
다음 표에서는 두 가지 데이터 유형을 비교 합니다.
| 항목 | JSON | JSONB |
| 저장 형식 | 텍스트 기반으로 공백 및 키 순서 유지 | 처리에 최적화된 바이너리 형식이며, 디스크 용량이 JSON보다 큼 |
| 삽입 속도 | 빠름(변환 과정이 없음) | 약간 느림(바이너리 변환 과정이 필요) |
| 쿼리 성능 | 느림(매 쿼리마다 파싱이 필요) | 대부분의 작업에서 매우 빠름 |
| 인덱싱 지원 | 함수형 인덱스 추가 가능 | 효율적인 검색을 위한 GIN 인덱스 지원 |
| 중복 키 처리 | 중복 키 유지 | 중복 키를 제거 (마지막 값만 유지) |
| 메모리 사용량 | 복잡한 작업의 경우 일반적으로 메모리 사용량이 더 많음 | 메모리 사용이 더 효율적 |
| 중첩 쿼리 | 깊은 중첩 구조에서는 효율성 저하 | 중첩 구조 쿼리에 최적화 |
| 주요 사용 사례 | 입력 데이터의 원본 형태 보존이 중요한 경우 | 성능이 중요한 대부분의 운영 환경 애플리케이션 |
실제 사용 사례
PostgreSQL은 트랜잭션 기반 객체 관계형 데이터베이스가 필요한 시스템에 유용하며, 확장 가능하고 강력한 데이터 유형, 풍부한 SQL 인터페이스, 고급 검색 기능을 제공합니다. 다음은 사용 사례의 예입니다.
- 웹 및 모바일 애플리케이션 — PostgreSQL은 확장성과 안정성 덕분에 많은 트래픽을 처리하고 강력한 트랜잭션 무결성이 필요한 동적 웹 사이트와 모바일 애플리케이션의 백엔드에서 널리 사용됩니다.
- 콘텐츠 관리 시스템 (CMS) — 많은 CMS 플랫폼에서 복잡한 콘텐츠 구조와 대규모 데이터베이스를 처리할 수 있는 PostgreSQL을 사용합니다.
- 과학 연구 및 데이터 분석 — PostgreSQL은 복잡한 데이터 유형과 고급 분석을 지원하므로 과학 연구와 데이터 분석에 적합합니다.
- 지리공간 애플리케이션 (GIS) — PostGIS 확장 기능을 통해 지도 및 위치 서비스 등의 애플리케이션을 위한 강력한 지리공간 데이터베이스가 제공됩니다.
- 고급 검색 기능이 필요한 애플리케이션 — PostgreSQL은 전문 검색, 지리공간 검색, 시맨틱 검색 등 정교한 검색 기능을 요구하는 애플리케이션에 탁월합니다. 구직 검색 엔진, 콘텐츠 추천, 제품 검색 시스템 등이 그 예입니다.
CMS 애플리케이션에서 PostgreSQL의 JSON 기능을 살펴보기 위해, 이 글에서는 벡터 검색 및 지리공간 검색 기능은 제외합니다. 전용 관계형 데이터베이스나 Amazon OpenSearch Service 같은 최적화된 데이터베이스 서비스가 이러한 전문 검색 기술을 위한 더 효율적인 플랫폼을 제공하기 때문입니다.
솔루션 개요
디지털 뉴스 매거진 CMS 애플리케이션을 만들어 보겠습니다. 뉴스 기사에는 구조화된 구성 요소 (titles, publication dates) 와 구조화되지 않은 가변 구성 요소 (기사 유형에 따라 다른 유형의 메타데이터) 가 모두 포함되어 있기 때문에, PostgreSQL의 JSON 기능을 활용하기에 완벽한 사례입니다.
전제조건
예제를 실행하려면 AWS 계정, PostgreSQL 17+, SQL query language에 대한 지식이 필요합니다. Aurora 데이터베이스 클러스터를 만드는 방법은 이 링크를 참조하시기 바랍니다.
데이터 구성하기
먼저 데이터베이스 구조를 설정합니다. 다음 테이블 정의는 PostgreSQL이 구조화된 데이터와 반구조화된 데이터를 모두 저장할 수 있는 방법을 보여줍니다.
PostgreSQL은 잠재적 행 위치의 비트맵을 생성하여 여러 독립 인덱스 스캔(GIN 인덱스 포함)의 결과를 결합할 수 있습니다. 이 예제에서 메타데이터 필드는 JSONB 유형입니다. 이는 편집자가 다양한 메타데이터 속성으로 기사를 자주 검색하고 필터링하는 CMS의 성능 향상에 도움이 됩니다. 우리가 생성한 GIN 인덱스는 태그별로 JSON 데이터를 필터링하는 쿼리 속도를 높여주며, 이는 뉴스 플랫폼에서 자주 사용되는 작업입니다. 다음으로, 디지털 매거진에 게재될 만한 샘플 기사를 삽입합니다.
이 예제에서 순수 관계형 접근법을 사용한다면 작성자, 카테고리, 태그, 지표별로 별도의 테이블이 필요하고, 외래 키를 사용하여 모든 항목을 연결해야 합니다. JSON을 사용하면 관련 데이터를 함께 보관하여 단일 쿼리로 전체 기사 정보를 쉽게 검색할 수 있습니다. 또한, 나중에 새 메타데이터 필드 (예: 비디오 기사의 “video_duration”, 인터뷰의 “interview_subject” 등)을 추가하더라도 데이터베이스 스키마를 변경하지 않고도 작업을 수행할 수 있습니다. 이러한 유연성은 진화하는 콘텐츠 유형에 적응해야 하는 CMS에 매우 중요합니다.
쿼리 패턴 및 최적화
이 섹션에서는 PostgreSQL이 JSON 데이터 쿼리를 위해 제공하는 검색 기능을 살펴보겠습니다.
JSON 경로 연산자로 쿼리
CMS에서 편집자는 특별 기능을 만들기 위해 “climate change”를 언급하는 모든 환경 관련 기사를 찾아야 할 수도 있습니다. 이 쿼리는 기존 SQL과 JSON 경로 연산자를 결합 합니다.
이 쿼리에서:
-
metadata->'categories' ? 'environment'는 포함 연산자 “?” 를 사용하여, categories 배열에 “environment”이 포함되어 있는지 확인합니다.
-
metadata->'tags' ? 'climate'는 마찬가지로 “climate change”가 tags 배열에 있는지 확인합니다.
-
metadata->>'author'는 author 필드를 텍스트로 추출합니다(note ->>).author자체가 JSON 객체이므로 전체 객체를 문자열로 반환합니다.
JSON을 테이블 형식 데이터로 변환하기
CMS 애플리케이션은 분석 목적으로 JSON 메타데이터에서 구조화된 데이터를 추출해야 하는 경우가 많습니다. JSON_TABLE은 JSON 데이터를 표 형식으로 표시하는 ANSI 사양 준수 함수입니다.
이 쿼리는 JSON_TABLE 함수와 PATH 및 NESTED PATH 표현식을 사용하여 JSON 데이터를 평면화하고 데이터 보고나 내보내기에 사용할 수 있는 JSON 데이터의 관계형 뷰를 제공합니다. 이전 버전의 PostgreSQL을 사용하는 경우 lateral 조인을 사용하여 비슷한 결과를 얻을 수 있습니다.
JSON 데이터를 사용한 전문 검색
CMS 애플리케이션은 JSON 데이터를 검색하기 위해 고급 검색 기술이 필요합니다. 이 섹션에서는 CMS 애플리케이션에서 PostgreSQL의 전문 검색 기능을 사용하는 방법을 살펴봅니다. CMS 애플리케이션의 경우, 독자는 기사 콘텐츠를 효율적으로 검색해야 합니다. PostgreSQL의 전문 검색 기능은 JSON 콘텐츠와 원활하게 작동합니다. 예를 들어, 다음 쿼리 참조하시기 바랍니다.
이 쿼리는 몇 가지 복잡한 검색 기능을 보여줍니다.
- 검색 결과에서 검색어 강조 표시
- 관련성 기준 기사 순위 지정
- 메타데이터 속성 기준 필터링
- 전문 검색과 구조화된 데이터 조건 결합
쿼리는 다음 구성 요소를 사용합니다.
- 전문 검색 연산자 —
@@연산자는tsvector가 tsquery와 일치하는지 확인합니다. - 텍스트 검색 함수 — 다음 함수를 사용합니다.
to_tsvector()는 텍스트를 검색 가능한 벡터로 변환하고, 어간 추출 및 불용어를 처리합니다.to_tsquery()는 Boolean 연산자를 사용하여 검색어를 파싱합니다.ts_headline()은 일치하는 단어를 굵게 표시된 발췌문을 강조 표시합니다.ts_rank()는 관련성을 기준으로 결과 순위를 매깁니다.
- JSON 포함 —
@>연산자는 왼쪽 JSON이 오른쪽 JSON 구조를 포함하는지 확인합니다.
더 많은 GIN 인덱싱 옵션
GIN 예제들에서 사용하는 jsonb_path_ops는 ?와 같은 존재 여부 확인에는 잘 작동하지만, 전체 경로 쿼리나 복합 키는 지원하지 않는다는 점을 고려 할 필요가 있습니다. 더 깊은 JSON 탐색을 사용하려면 jsonb_ops를 대안으로 사용하시기 바랍니다.
ajsonb_ops와 jsonb_path_ops GIN 인덱스의 기술적인 차이점은 전자는 데이터의 각 키와 값에 대해 독립적인 인덱스 항목을 생성하는 반면, 후자는 데이터의 각 값에 대해서만 인덱스 항목을 생성한다는 것입니다. 일반적으로 다음과 같은 경우에서는 각 인덱스 유형을 사용합니다.
jsonb_path_ops를 단순 포함 쿼리에 사용합니다(더 작고 빠름).- 복잡한 경로 쿼리와 복합 작업에
jsonb_ops를 사용합니다.
jsonb_path_ops 그리고 jsonb_ops에 대한 자세한 내용은 JSON 유형을 참조하시기 바랍니다.
이제 다음과 같은 SQL 문을 사용할 수 있습니다.
GIN 인덱스 최적화하기
이전 섹션에서 언급한 올바른 GIN 연산자 클래스 사용 규칙 외에도 일반적인 GIN 인덱스 최적화 전략을 고려합니다.
- 표현식과 부분 인덱스 사용
- 특정 키를 자주 쿼리하는 경우, 해당 키에만 표현식 인덱스를 생성합니다. 이 인덱스는 표준 B-트리 인덱스를 사용하므로 간단한 동등성 검사에 훨씬 빠릅니다.
- expression 인덱스와
WHERE절을 결합하여 데이터의 일부만 인덱싱합니다. 이 기능은 행의 일부만 인덱싱하면 되는 대형 테이블에 유용합니다.
- 대량 데이터 로드 최적화 – 대량 데이터 로드의 경우, 데이터를 먼저 로드한 다음 인덱스를 만드는 것이 더 효율적입니다. 단일 대량 작업으로 인덱스를 생성하는 것이
INSERT작업을 수행할 때마다 점진적으로 인덱스를 생성하는것보다 더 빠른 경우가 많습니다. gin_pending_list_limit최적화 — 이 구성 파라미터는 GIN 인덱스의 “pending list”의 크기를 제어합니다. 쓰기 트래픽이 많은 테이블에 중요합니다. 빠르고 빈번한 쓰기를 우선시하려면 이 파라미터의 크기를 줄이는것을 고려합니다.- 모니터링 및 튜닝
시맨틱 및 지리공간 검색 기능들
애플리케이션에 PostgreSQL의 강점과 함께 JSON 데이터를 사용한 이 두 가지 검색 기술이 필요한 경우, AWS는 상호 보완적인 아키텍처 패턴을 권장합니다. 일반적인 접근 방식으로는 PostgreSQL을 Amazon OpenSearch Service와 통합하여 각 서비스의 강점을 활용하는 맞춤형 검색 솔루션을 만드는 것이 있습니다. pull 또는 push 방식과 OpenSearch 통합 파이프라인을 사용하여 구현된 PostgreSQL과 OpenSearch의 통합은 OpenSearch에 데이터를 수집하는 완전 관리형 노코드 환경을 제공합니다.
백터 및 지리공간 검색에 Amazon OpenSearch Service와 같은 별도의 최적화된 서비스를 사용하면 PostgreSQL이 복잡한 쿼리 및 트랜잭션 무결성과 같이 뛰어난 작업을 더욱 효율적으로 처리할 수 있습니다. Amazon OpenSearch Service는 고차원 벡터 임베딩과 복잡한 지리공간 쿼리를 처리하도록 설계되었으므로 이러한 특정 워크로드에 더 높은 성능을 제공하는 선택이 됩니다.
정리
이 솔루션을 사용한 실험을 마치면 추가 비용이 발생하지 않도록 생성한 리소스를 정리합니다.
AWS 관련 이점
PostgreSQL에는 JSON 기능이 내장되어 있지만 Aurora PostgreSQL-Compatible과 Amazon RDS for PostgreSQL은 JSON 데이터 작업의 효율성을 크게 향상시키는 인프라 및 관리 기능을 제공합니다.
- 분산 스토리지 아키텍처 — Aurora의 스토리지 계층은 대용량 JSON 문서를 처리할 때 I/O 병목 현상을 줄여 읽기/쓰기 작업을 더 효율적으로 지원합니다. 스토리지는 수동 개입 없이 자동으로 확장되므로, JSON 데이터 증가를 예측할 수 없는 애플리케이션에 이상적입니다.
- ·Aurora 클러스터 캐시 관리 — 인스턴스 간 공유 버퍼 캐시는 자주 액세스하는 JSON 문서의 성능을 향상시킵니다. 인스턴스 재시작 시에도 버퍼 캐시가 지속되므로 유지 관리 이벤트 발생 후 성능 저하를 방지할 수 있습니다.
- 병렬 쿼리 처리 — PostgreSQL의 병렬 쿼리 처리는 여러 CPU 코어에 작업을 분산하여 일부 JSON 작업의 성능을 향상시킬 수 있습니다. 이 기능은 대용량 데이터 세트에 대한 분석 쿼리에 가장 효과적이지만, 복잡한 JSONB 경로 작업과 GIN 인덱스 조회의 경우 항상 병렬화의 이점을 얻을 수 있는 것은 아닙니다.
- 지연 시간을 최소화한 읽기 전용 복제본 — Aurora 복제는 지연을 최소화하면서 최대 15개의 읽기 전용 복제본을 지원하여 JSON 읽기 작업을 효과적으로 확장할 수 있도록 지원 합니다. Amazon Aurora Serverless v2는 다양한 JSON 읽기 작업을 처리할 수 있도록 동적으로 확장되며, 워크로드 변화에 따라 컴퓨팅 용량을 자동으로 조정합니다.
- 인스턴스 유형 유연성 — 다음 인스턴스 유형을 선택할 수 있습니다.
- 대용량 JSON 문서가 있는 애플리케이션을 위한 메모리 최적화 인스턴스로, in-memory 처리의 이점을 제공합니다.
- Burstable-performance 인스턴스는 CPU 사용량이 보통이고 간헐적으로 사용량이 급증하는 워크로드에 비용 효율적입니다.
- 읽기 최적화 인스턴스는 처리량이 높은 JSON 수집 워크로드에 적합합니다.
- CloudWatch Database Insights — Aurora PostgreSQL-Compatible과 Amazon RDS for PostgreSQL는 복잡한 JSON 작업의 병목 현상을 식별하는 데 특히 도움이 되는 자세한 모니터링을 제공합니다.
모범 사례
모범 사례를 따르면 AWS에 호스팅된 데이터베이스에서 성능과 무결성을 유지하면서 PostgreSQL의 JSON 기능을 효과적으로 사용할 수 있습니다.
- 전략적 데이터 모델링:
- 정확한 텍스트 보존이 필요하지 않은 경우 대부분의 사용 사례에 JSONB를 사용합니다.
- 성능 향상을 위해 자주 쿼리되는 필드를 일반 칼럼로 정규화합니다.
- 일부 구조화된 데이터는 칼럼에 저장하고, 가변 데이터는 JSON에 저장하는 부분 정규화를 고려합니다.
- 인덱싱 전략:
- 자주 쿼리되는 JSON 경로에 GIN 인덱스를 생성합니다.
- 특정 JSON 속성에 expression 인덱스를 사용합니다.
- 인덱스 사용을 모니터링하고 쓰기 속도를 늦추는 미사용 인덱스를 제거합니다.
- 쿼리 최적화:
- 최상의 성능을 위해 포함 연산자
@>및<@를 GIN 인덱스와 함께 사용합니다. - 전체 문서 추출을 방지하려면 경로 연산자
->,->>를 활용합니다. - 비교할 때 적절한 유형으로 값을 추출합니다.
- 최상의 성능을 위해 포함 연산자
- 성능 고려 사항:
- 관계형 칼럼에 더 적합한 데이터에 JSON을 과도하게 사용하지 않습니다.
- 포함 연산자만 사용하는 경우 GIN 인덱스에
JSONB_PATH_OPS를 사용합니다. - 개별 필드를 자주 업데이트하는 대신 JSON 문서 일괄 업데이트합니다.
- 전체 문서가 아닌 대상 경로 업데이트를 수행합니다.
- 쿼리 안티패턴 회피:
- 더 선택적인 필터를 사용할 수 있음에도 JSON 문서 전체를 스캔하지 않습니다.
- 특정 필드만 필요할 때 전체 JSON 문서 추출하지 않습니다.
- 적절한 인덱싱이나 타입 변환 없이 JSON 필드에 조인을 수행하지 않습니다.
- 스키마 진화:
- 데이터베이스 수준에서 JSON 스키마의 유효성을 검사 합니다. Check 제약 조건을 사용하여 JSON 스키마의 유효성을 검사할 수 있습니다. 예를 들어, 다음 테이블 정의를 살펴봅니다.
- PostgreSQL의 최신 버전은 간단한 유효성 검사를 수행하는 “
IS JSON”, “IS JSON ARRAY” 또는 “IS JSON OBJECT” 같은 여러 함수를 제공합니다. - JSON 스키마나 유사한 도구를 사용하여 애플리케이션 수준 유효성 검사를 수행할 수도 있습니다.
- 스토리지 고려 사항:
- 더 이상 사용되지 않는 필드를 제거하여 문서 bloat를 방지합니다.
- JSON 칼럼과 일반 칼럼 모두에 데이터를 저장하지 않습니다.
- AWS 관련 고려 사항:
- CloudWatch Database Insights를 사용하여 JSON 작업의 병목 현상을 식별합니다.
- JSON 스토리지 크기는 정규화된 테이블보다 덜 압축될 수 있으므로 이를 고려합니다.
결론
PostgreSQL의 JSON 기능은 AWS 관리형 서비스와 결합되어 NoSQL 데이터베이스에 대한 매력적인 대안을 제공합니다. 트랜잭션을 희생하지 않고도 스키마 유연성을 확보하고, 새로운 언어를 배우지 않고도 강력한 쿼리기능을 활용하며, 운영 오버헤드 없이 엔터프라이즈 기능을 활용할 수 있습니다.
JSON 워크로드를 PostgreSQL 플랫폼으로 마이그레이션하려면 Amazon RDS for PostgreSQL 또는 Amazon Aurora PostgreSQL-Compatible을 통해 비용을 최적화 합니다.