home

Postgresql의 json 데이터 타입

PostgreSQL의 json 데이터 타입

PostgreSQL은 일반 JSON 타입과 이진 저장 및 인덱싱이 가능한 jsonb 타입을 제공합니다. 이 jsonb 타입으로 데이터를 저장하면 쿼리 성능이 훨씬 뛰어나고 저장 공간 최적화도 가능합니다.

쿼리 성능이 뛰어난 이유는 바로 JIN 인덱스 덕분인데, 이 인덱스를 사용하면 JSONB 문서 내 복수의 키와 값에 대한 부분 인덱싱이 가능해, 전체 문서를 스캔하지 않고도 빠른 검색 가능하다고 합니다.

GIN 인덱스 작동 방식

  • jsonb 컬럼의 모든 키와 값 쌍을 추출해 인덱스 구조에 저장
  • 각 키와 값은 별도의 인덱스 엔트리로 관리
  • 키 존재 여부나 특정 키-값 포함 여부 검사에 매우 빠른 쿼리 성능을 제공

연산자 종류

  • containment 연산자 (@>)
    • 왼쪽 jsonb 값이 오른쪽 jsonb 값을 포함하는지 검사

        SELECT * FROM test WHERE data @> '{"field": "value"}';
      
    • data 컬럼의 JSON 값이 key “field”에 “value”를 포함하고 있으면 true를 반환합니다. 즉, 키-값 쌍 포함 여부를 체크합니다.

  • key existence 연산자 (?)
    • JSON 개체에서 특정 키의 존재 여부를 검사합니다.

        SELECT * FROM test WHERE data ? 'field';
      
    • ata JSON 객체의 최상위에 key “field”가 있으면 true를 반환합니다.

jsonb_path_ops란?

PostgreSQL jsonb에 대해 GIN 인덱스를 생성할 때 선택할 수 있는 operator class 중 하나로, 기본 값인 jsonb_ops에 비해 인덱스 크기가 훨씬 작고, containment 쿼리(@>)에 대해 더 빠른 성능을 제공합니다.

containment 쿼리에서 더 빠른 성능을 낼 수 있는 건 내부적으로 key와 값을 합쳐 해시한 단일 인덱스 엔트리를 만들기 때문입니다.

그런데 key existence 연산자 및 다른 연산자에는 적용되지 않기 때문에 키 존재 여부 검사를 자주 한다면 기본 값인 jsonb_ops이 적합!

GIN 인덱스를 실제로 사용하지 못하는 경우

많은 경우가 GIN 인덱스를 사용하지 못하는데, 대표적인 경우는 밑과 같습니다.

  • JSONB 내부의 중첩된 필드나 특정 경로 위치의 값을 직접 비교하는 쿼리
  • JSONB 내부 값의 범위 비교(>, <, >=, <= 등)나 타입 변환 후 비교
  • 정규 표현식이나 와일드카드를 사용하는 텍스트 매칭
  • JSON 값을 가져와서 함수나 연산을 수행하는 경우

그러면 단점은?

GIN 인덱스는 쓰기 작업 시 오버헤드가 크므로 자주 갱신되는 큰 JSONB 컬럼은 비효율적입니다.

또, 위와 같이 키-값 쌍의 포함 여부를 빠르게 판단하는데 최적화 되어있어, 세밀한 경로나 값의 범위, 또 특정 패턴을 판단하는 쿼리에는 제한적입니다.

그래서 단순히 JSON 데이터를 저장하고 검색을 하지 않는 경우에는 GIN 인덱스를 생성하는 걸 심사숙고 해봐야겠네요..!