1P by GN⁺ | ★ favorite | 댓글 1개
  • PostgreSQL 16은 쿼리 플래너/옵티마이저에 10가지 개선을 추가해 DISTINCT, 집계, 조인, 윈도 함수, 파티션 테이블 쿼리의 실행 계획 선택 폭을 넓힘
  • SELECT DISTINCT, ORDER BY/DISTINCT 집계, Merge Join 이후 처리에서 부분 정렬된 입력을 더 적극적으로 활용해 전체 정렬보다 적은 메모리로 결과를 만들 수 있음
  • UNION ALL 내부의 Memoize, Right Anti Join, FULL/RIGHT 조인의 병렬 해시 조인 지원은 반복 조회와 큰 해시 테이블 생성 비용을 줄이는 데 초점을 둠
  • 윈도 함수는 불필요한 RANGE 처리와 끝까지 실행해야 하는 WindowAgg를 줄이며, 일부 함수는 조건에 따라 조기 중단이 가능해짐
  • 모든 개선은 기본 활성화되어 있어, PostgreSQL 16 업그레이드 전후로 실제 워크로드의 EXPLAIN과 실행 시간을 비교해 볼 가치가 있음

PostgreSQL 16 플래너 개선의 범위

  • PostgreSQL 16은 쿼리 플래너에 여러 개선을 도입해 많은 SQL 쿼리를 이전 PostgreSQL 버전보다 빠르게 실행할 수 있게 함
  • PG16 릴리스 노트에 포함된 플래너 개선을 더 자세히 풀어 설명하며, PG15와 PG16의 EXPLAIN 출력 비교와 재현 가능한 테스트 예제를 함께 다룸
  • 여기서 플래너는 다른 관계형 데이터베이스에서 흔히 옵티마이저라고 부르는 구성요소임

정렬과 DISTINCT 최적화

  • SELECT DISTINCT에서 Incremental Sort 사용

    • Incremental Sort는 PostgreSQL 13에서 처음 추가됐으며, 결과가 선행 컬럼 기준으로 이미 정렬되어 있을 때 나머지 컬럼만 정렬해 비용을 줄임
    • PostgreSQL 16 플래너는 SELECT DISTINCT 쿼리에서도 Incremental Sort를 고려함
    • 예를 들어 a 컬럼에 btree 인덱스가 있고 a, b 순서가 필요하면, 인덱스로 a 기준 정렬 결과를 얻은 뒤 a 값이 바뀔 때마다 b만 정렬할 수 있음
    • PostgreSQL의 quicksort에서는 큰 그룹 하나를 정렬하는 것보다 작은 그룹 여러 개를 정렬하는 편이 더 효율적일 수 있음
    • 예제 쿼리에서 PG15는 HashAggregate와 순차 스캔을 사용했고, PG16은 distinct_test_a_idx 인덱스와 Incremental Sort를 선택함
    • PG16 출력의 Presorted Key: aa 기준으로 이미 정렬된 입력을 활용했다는 뜻임
    • PG15의 해시 방식은 약 30MB를 디스크로 스필했지만, PG16의 Incremental Sort 최대 메모리는 26KB였음
    • 실행 시간은 PG15 414.226ms에서 PG16 263.167ms로 줄어듦
  • ORDER BY 또는 DISTINCT가 있는 집계 최적화

    • PostgreSQL 15 이하에서는 ORDER BYDISTINCT 절이 있는 집계 함수가 항상 Aggregate 노드 내부에서 정렬을 수행함
    • PostgreSQL 16 플래너는 Aggregate 노드에 올바른 순서의 행을 공급하는 실행 계획을 만들 수 있고, 실행기는 이미 정렬된 입력이면 내부 정렬을 생략함
    • COUNT(DISTINCT b) 예제에서 PG15와 PG16 모두 GroupAggregateIndex Only Scan을 사용하지만, PG15 출력에는 temp read=4540 written=4560이 나타남
    • 이 임시 파일 I/O는 PG15의 암시적 정렬이 디스크로 스필한 결과임
    • PG16 출력에는 해당 임시 I/O가 없으며, 실행 시간은 PG15 302.693ms에서 PG16 115.534ms로 2배 이상 빨라짐

반복 조회와 조인 계획 개선

  • UNION ALL 내부에 Memoize 적용

    • Memoize 플랜 노드는 PostgreSQL 14에서 처음 도입됐으며, 매개변수화된 Nested Loop와 내부 입력 사이에서 캐시 계층처럼 동작함
    • PostgreSQL 16 플래너는 매개변수화된 Nested Loop의 내부에 UNION ALL 쿼리가 있을 때도 Memoize 사용을 고려함
    • 예제에서 PG15는 Append를 100만 번 실행했지만, PG16은 Append 위에 Memoize를 배치함
    • PG16의 MemoizeHits: 999990, Misses: 10, Memory Usage: 2kB를 기록함
    • Append 실행 횟수는 PG15의 100만 회에서 PG16의 10회로 줄어듦
    • 실행 시간은 PG15 1926.151ms에서 PG16 282.120ms로 약 6배 빨라짐
  • Right Anti Join 지원

    • INNER JOINHash Join에서는 보통 더 작은 테이블에 해시 테이블을 만드는 편이 유리함
    • 작은 해시 테이블은 생성 작업이 적고 CPU 캐시에 더 친화적이며, 메인 메모리에서 데이터를 기다리는 CPU stall 가능성도 낮음
    • PostgreSQL 16 이전의 Anti JoinNOT EXISTS에 언급된 테이블을 항상 조인의 내부에 두었기 때문에, 더 큰 테이블에 해시 테이블을 만들어야 할 수 있었음
    • PostgreSQL 16은 Right Anti Join을 지원해 두 테이블 중 더 작은 쪽을 해시할 수 있게 함
    • 예제에서 PG15는 100만 행의 large 테이블을 해시했고 메모리 사용량은 6446KB였지만, PG16은 100행의 small 테이블을 해시해 12KB만 사용함
    • 실행 시간은 PG15 139.023ms에서 PG16 77.076ms로 거의 절반이 됨
  • FULL/RIGHT 조인의 병렬 해시 조인

    • PostgreSQL 11은 여러 병렬 워커가 단일 해시 테이블 생성에 참여하는 Parallel Hash Join을 도입함
    • PostgreSQL 16의 Parallel Hash JoinFULLRIGHT 조인 유형을 지원함
    • FULL OUTER JOINRight Join 계획도 병렬 실행할 수 있음
    • FULL JOIN 예제에서 PG15는 단일 Hash Full Join을 사용했고, PG16은 Parallel Hash Full JoinGather를 사용함
    • PG16 출력에는 Workers Planned: 1, Workers Launched: 1이 나타남
    • 실행 시간은 PG15 220.677ms에서 PG16 129.769ms로 크게 줄어듦

윈도 함수 최적화

  • 불필요한 RANGE 처리 생략

    • row_number(), rank(), dense_rank(), percent_rank(), cume_dist(), ntile() 같은 윈도 함수에서 윈도 절에 ROWS 옵션이 없으면 PostgreSQL은 기본 RANGE 옵션을 사용함
    • RANGE 옵션은 같은 정렬 값을 가진 peer row를 찾기 위해 앞쪽 행을 확인해야 하며, ORDER BY 기준으로 같은 값이 많으면 비용이 커질 수 있음
    • 위 함수들은 ROWSRANGE 지정 여부에 따라 동작이 달라지지 않지만, PostgreSQL 16 이전 실행기는 이를 구분하지 못해 모든 경우에 peer row 검사를 수행해야 했음
    • PostgreSQL 16 플래너는 어떤 윈도 함수가 ROWS/RANGE 옵션에 영향을 받는지 알고, 실행기가 불필요한 처리를 건너뛰도록 정보를 전달함
    • row_number() <= 10 예제에서 PG15는 인덱스에서 50,410행을 읽은 뒤 중단했지만, PG16은 11행만 읽음
    • PG16은 row_number가 11에 도달하면 <= 10 조건을 만족하는 행이 더 없다는 점을 활용함
    • 실행 시간은 PG15 29.775ms에서 PG16 0.058ms로 500배 이상 빨라짐
  • 단조 증가 윈도 함수의 조기 중단 확대

    • PostgreSQL 15는 WHERE 절 조건이 특정 윈도 함수에 대해 한 번 false가 되면 다시 true가 될 수 없는 경우 WindowAgg 실행을 일찍 멈출 수 있게 했음
    • PostgreSQL 16은 이 최적화 대상을 ntile(), cume_dist(), percent_rank()까지 확장함
    • PostgreSQL 15에서는 row_number(), rank(), dense_rank(), count(), count(*)에만 적용됐음
    • percent_rank() <= 0.01 예제에서 PG15는 조건을 서브쿼리의 Filter로 처리했고, WindowAgg는 50,000행을 모두 처리함
    • PG16은 같은 조건을 Run Condition으로 사용해 WindowAgg 실행을 조기에 중단함
    • 실행 시간은 PG15 84.358ms에서 PG16 19.454ms로 4배 이상 빨라짐

파티션 테이블과 자명한 DISTINCT 처리

  • 파티션 테이블의 LEFT JOIN 제거

    • PostgreSQL은 오래전부터 쿼리에 필요하지 않고 행 중복 가능성도 없는 LEFT JOIN을 제거할 수 있었음
    • PostgreSQL 16 이전에는 파티션 테이블에 대한 LEFT JOIN 제거가 지원되지 않았음
    • 내부 행이 외부 행을 중복시킬 가능성이 없는지 판단하는 데 필요한 증명이 파티션 테이블에는 없었기 때문임
    • PostgreSQL 16 플래너는 파티션 테이블에도 LEFT JOIN 제거 최적화를 적용함
    • 이 최적화는 뷰에서 특히 유용할 수 있음
      • 뷰에는 많은 컬럼이 있어도 실제 쿼리에서 모든 컬럼을 항상 조회하지는 않기 때문임
    • 예제에서 PG15 계획은 part_tab에 대한 조인을 포함하지만, PG16 계획은 normal_table 순차 스캔만 수행함
  • 결과가 하나로 확정되는 DISTINCT를 Limit으로 처리

    • PostgreSQL 플래너는 모든 행이 같은 값을 가진다는 사실을 감지할 수 있으면 결과 중복 제거용 플랜 노드를 생략할 수 있음
    • PostgreSQL 16은 DISTINCT 대상 컬럼들이 모두 WHERE 절의 동등 조건으로 고정된 경우, 결과가 같은 값만 포함된다는 점을 활용해 LIMIT 1로 처리함
    • 예제 쿼리 SELECT DISTINCT a,b,c FROM abc WHERE a = 5 AND b = 5 AND c = 5에서 각 DISTINCT 컬럼은 동일한 값으로 제한됨
    • PG15는 전체 결과를 읽고 Unique 연산자로 1행으로 줄임
    • PG16은 Limit과 순차 스캔을 사용해 1행만 반환함
    • 실행 시간은 PG15 30.381ms에서 PG16 0.025ms로 1200배 이상 빨라짐

Merge Join 이후 Incremental Sort 활용 확대

  • PostgreSQL 16 이전 플래너는 Merge Join을 고려할 때, 조인의 정렬 순서가 상위 DISTINCT, GROUP BY, ORDER BY 작업 요구사항과 정확히 일치하는 경우에만 그 순서를 사용함
  • 이 규칙은 Incremental Sort가 상위 작업에서 부분 정렬된 입력을 활용할 수 있다는 점을 충분히 반영하지 못했음
  • PostgreSQL 16은 Merge Join 순서를 고려하는 규칙을 “정확히 일치해야 함”에서 “선행 컬럼 최소 1개가 올바르게 정렬되어야 함”으로 완화함
  • 이 변경으로 플래너는 Merge Join 결과를 상위 작업에 맞추기 위해 Incremental Sort를 더 자주 사용할 수 있음
    • Incremental Sort는 부분 정렬된 입력을 활용해 작은 배치 단위로 정렬하므로 전체 정렬보다 메모리 사용량과 비교 횟수를 줄일 수 있음
  • 예제에서 PG15는 Merge Join 이후 전체 Sort를 사용했고, PG16은 Incremental Sort를 사용함
    • PG16의 Incremental Sort 최대 메모리는 26KB였음
    • 실행 시간은 PG15 1010.738ms에서 PG16 915.589ms로 소폭 줄었고, 정렬에 사용된 메모리는 크게 감소함

적용 방식과 실무 확인

  • PostgreSQL 16의 10가지 플래너 개선은 모두 기본 활성화되어 있음
  • 각 최적화는 가능한 모든 경우에 적용되거나, 플래너가 도움이 된다고 판단할 때 선택적으로 적용됨
  • 기존 PostgreSQL 버전을 사용 중이라면 PostgreSQL 16에서 실제 워크로드를 실행해 어떤 쿼리가 빨라지는지 확인할 수 있음
  • 실제 사용 피드백은 pgsql-general@postgresql.org 메일링 리스트로 공유할 수 있음

댓글과 토론

Hacker News 의견들
  • PostgreSQL 쿼리 플래너가 실행 중간에 쿼리를 다시 계획할 수 있으면 정말 좋겠음
    병적으로 느린 쿼리는 플래너가 데이터 분포에 필요한 정보를 몰라 비용을 잘못 추정해서 생기는 경우가 많고, 실행 시간이 1ms 대신 1초가 되는 식으로 1000배 차이도 쉽게 남
    테이블 통계가 100% 정확할 수는 없으니, 쿼리를 시작한 뒤 진행 속도가 예상보다 느리면 스캔한 페이지 수와 매칭된 튜플 같은 현재 진행 정보를 플래너에 다시 넣어 새 계획을 만들면 좋겠음
    다만 PostgreSQL은 결과를 끝까지 만든 뒤 보내는 게 아니라 스트리밍으로 보내기 때문에, 중간에 계획을 바꾸려면 이미 클라이언트에 보낸 결과를 추적해야 해서 인프라 변경이 큼
    게다가 클라이언트가 쿼리 중간에 방향을 뒤집어 이전 결과를 역순으로 다시 요청할 수도 있어 복잡도가 더 커짐

    • 블로그 작성자이자 PostgreSQL 커미터로서, 이 기능이 있으면 좋겠다고 생각함. 다만 클라이언트로 튜플을 보내는 문제는 위에서 말한 것보다 더 까다로움
      새 계획이 같은 튜플을 반환한다는 보장조차 없기 때문임. 예를 들어 SELECT * FROM table LIMIT 10처럼 ORDER BY가 없으면 어떤 튜플이 나올지 비결정적임
      차라리 X개의 튜플을 큐에 쌓아두고, 큐가 차면 그때부터 보내는 방식이 쉬울 수 있음. 큐가 가득 찬 뒤에는 재계획이 너무 늦었다고 보고 현재 계획에 고정하는 식임
      사용자는 X를 조절해서 더 많은 메모리와 첫 튜플 지연 시간을 감수하는 대신, 계획을 바꿀 수 있는 시간을 늘릴 수 있음
    • 다른 관점으로는 긴 계획 수립이 허용되는 쿼리를 둘 수 있음. 최적의 계획을 고르는 데 1초나 몇 초를 쓰도록 허용하고, 그 과정에서 통계를 더 모으거나 쿼리를 잠깐 실행해볼 수 있음
    • 클라이언트가 쿼리 중간에 방향을 뒤집어 이전 결과를 역순으로 다시 받는 기능은 어디에 유용한지 궁금함
    • 쿼리가 정렬 순서를 완전히 결정하지 않는다면, 쿼리 계획이 결과 순서에 영향을 줄 수 있는지 궁금함. 그렇다면 제안한 방식은 거의 불가능할 수 있음
      새 쿼리가 단순히 앞의 N개 결과를 건너뛸 수 없고, 이미 보낸 각 행을 사전과 대조해야 함
    • 이 논문과 인용 논문들이 관심 있을 수 있음: https://arxiv.org/pdf/1902.08291
  • 쿼리 시각화에는 이 도구를 씀: https://explain.dalibo.com/
    https://www.pgexplain.dev/도 있는데, 예전에는 출력이 덜 좋았지만 지금은 둘 다 비슷해 보임

    • 도구는 훌륭해서 쓰고 있지만, 계획에서 안 좋아 보이는 부분을 보고 내 접근을 어떻게 고쳐야 하는지 알 만큼 깊이 이해하고 있지는 못함
    • 프로필을 보니 핀테크 CTO인데, 그 도구의 “중요하거나 민감한 정보를 보내지 않는 것이 권장됩니다”라는 안내는 어떻게 다루는지 궁금함
      이런 상황에 도움이 되는 실행 계획 정제 도구가 있는지 궁금함
  • 쿼리 플래너 개선은 언제나 환영이고, 데이터베이스에서 아주 중요한 부분임. 물론 보통은 내가 원하는 대로 안 될 때 가장 눈에 띔
    개인적으로 꽤 답답했던 부분은 최신 PostgreSQL의 JIT임. 언제 사용할지 정하는 휴리스틱이 전혀 견고해 보이지 않음
    전형적인 ORM 생성 쿼리에서 봤는데, 쿼리 자체는 단순하지만 조인으로 많은 테이블을 끌어옴. JIT 없이는 몇 밀리초에 끝나는데, JIT가 1~1.5초를 추가로 쓰면서 작은 데이터에도 엄청 느려짐
    이제는 JIT를 그냥 끄면 된다는 걸 알지만, 왜 느린지 아직 파악 못 하는 사용자에게는 PostgreSQL에 대한 인상을 크게 망칠 수 있음. PostgreSQL을 좋아하지만 JIT를 기본값으로 켜두는 건 너무 위험해 보임

    • 블로그 작성자이자 PostgreSQL 커미터로서, JIT 사용 여부를 결정하는 코드는 개선이 필요하다는 데 크게 동의함
      PG16에서는 계획의 추정 총비용만 보고, 컴파일해야 할 표현식 수는 고려하지 않음
      표현식 몇 개를 컴파일하는 건 빠르지만, 수백 개 파티션이 있는 파티션 테이블을 조회하고 계획에 그 파티션들이 모두 들어가면 JIT 컴파일러가 할 일이 많아짐
      동료와 함께 이를 개선하는 코드를 갖고 있지만, 현시점에서 PG17에 들어갈지는 확실하지 않음
    • JIT에서 또 이상하게 느껴지는 건 생성된 코드가 캐시되지 않는다는 점임. 쿼리 실행에서 가장 비싼 부분인 경우가 많은데 왜 캐시하지 않는지 모르겠음
      PostgreSQL 메일링 리스트의 JIT 관련 논의를 찾아봐도 납득할 만한 이유를 못 찾았음
      OLTP 작업 부하에서는 JIT를 끄는 게 맞음
    • JIT는 사실상 실패에 가깝다고 봄. 의도는 좋았지만 LLVM은 여기에 맞는 도구가 아님. 전역으로 꺼뒀음
      ORM을 쓰지 않으니 단순히 이상한 쿼리 패턴 때문만도 아님
      반면 쿼리 병렬화는 실제로 유용할 수 있고, 무엇보다 드물게만 해를 끼침
    • 최근 프로덕션에서 JIT 관련 희한한 버그를 만났음
      apt로 몇 패키지를 업데이트했더니 5분마다 돌리는 큰 쿼리가 갑자기 실패하기 시작했음. 정확히는 PostgreSQL이 쿼리 실행 중간에 로그도 남기지 않고 연결을 조용히 끊어버림
      수동으로 EXPLAIN을 돌려보며 확인해보니 JIT를 쓰게 된 쿼리 변형만 깨지고, 쓰지 않는 쪽은 괜찮았음. JIT를 끄자 다시 정상으로 돌아옴
    • 준비된 문장을 써서 컴파일을 한 번만 하고, 컴파일 결과를 해당 쿼리 실행 때마다 재사용해보지는 않았는지 궁금함
  • 이런 변경들이 실제 쿼리에서 얼마나 자주 효과를 내는지 궁금함. 특히 “가능할 때 DISTINCT 구현에 Unique 대신 Limit 사용” 변경은 아주 어리석은 쿼리에만 적용될 것처럼 느껴짐
    PostgreSQL 개발자들에게 이를 판단할 정보원이 있는지 궁금함

    • 꽤 자주 효과가 있을 것 같음. DISTINCT는 경험이 적은 개발자가 나쁜 쿼리를 고치려고 자주 붙이는 것이고, 보통 성능 개선을 시작할 때 가장 먼저 하는 일은 그걸 필요 없게 쿼리를 다시 쓰는 것임
      DISTINCT 개선으로 나쁜 쿼리에 더 견고해진다면 얻는 게 많음. 모든 문제를 고치지는 못하겠지만 어떤 개선이든 환영임
    • 블로그 작성자이자 해당 기능 작성자로서, 이 건은 pgsql-hackers 메일링 리스트에서 실제로 나왔음
      자주 적용될 가능성은 낮다는 데 동의하지만, 좋은 점은 적용 가능 여부를 감지하는 일이 포인터가 NULL인지 확인하는 것만큼 단순했다는 것임
      감지는 매우 간단하고 대부분은 적용되지 않겠지만, 적용 가능한 경우에는 상당한 성능 향상을 줄 수 있음
    • 문제는 ORM이 아주 어리석은 쿼리를 만드는 습관이 있고, 개발자들은 SQL을 직접 써서 고치는 건 왠지 순수하지 않다며 거부한다는 점임
      아주 흔한 문제는 아니겠지만, 가끔 나타난다고 해도 놀랍지 않음
    • 예전 직장에서는 레거시 이유로 사용자 테이블에 중복 이메일 주소를 허용했지만, 새 중복은 넣고 싶지 않아서 새 사용자 생성 전에 select distinct email from users where email = ? 쿼리를 실행했음
      같은 이메일을 가진 행이 100개를 넘지는 않았던 것 같음. 대부분은 삭제해도 됐을 테스트 사용자였지만, 이야기가 좀 샜음
  • PostgreSQL에 앱 테스트용 엄격 모드가 있으면 좋겠음. 쿼리 자체만 보고, 통계와 무관하게 인덱스가 있으면 점근적으로 쿼리가 개선되는데 그 인덱스가 없으면 오류를 반환하는 모드임
    앱 업그레이드용으로 해당 인덱스를 만드는 CREATE INDICES FOR 명령도 있으면 좋겠고, 대화형·개발용으로 자동 인덱스 생성 모드도 있으면 함
    전반적으로 시스템은 점근적으로 비최적인 실행이 절대 일어나지 않도록 설계되어야 함

  • 힌트를 구현하지 않는지 모르겠음

    • pg_hint_plan 확장이 있음. 힌트의 위험은 작성 당시에는 맞더라도 테이블 크기나 데이터 치우침이 변하면 오히려 나빠질 수 있다는 점임
      예전에 힌트 논의를 봤을 때, 플래너를 너무 강하게 묶지 않고 기본 데이터 변화에 적응할 수 있는 방식이라면 일반적인 반대는 없었던 것으로 기억함
      예를 들어 특정 조건자가 10개 행과 매칭된다고 지정하는 대신, 두 컬럼 사이에 상관관계가 있다고 알려주는 식임
    • 관련 논의: Why PostgreSQL doesn't have query hints
      https://news.ycombinator.com/item?id=2179433 (댓글 60개, 2011년)
      PostgreSQL 위키의 공식 입장은 https://wiki.postgresql.org/wiki/OptimizerHintsDiscussion에 있음
      “다른 데이터베이스에서 흔히 구현되는 정확한 방식의 힌트에는 관심이 없다”는 입장임
      기존 힌트 시스템의 문제로는 애플리케이션 코드 유지보수성 저하, 업그레이드 방해, 나쁜 DBA 습관 조장, 데이터 크기 확장에 맞지 않음이 꼽힘
      그 입장을 탓하고 싶지는 않지만, PostgreSQL이 멍청한 계획을 고르고도 합리적인 선택을 하도록 설득할 수 없을 때는 답답함
  • 중견 기업 대상 Microsoft DBA인 친구가 PostgreSQL로는 진지한 일을 할 수 없다고 말했음. 심지어 PostgreSQL에 쿼리 플래너도 없다는 걸 알고 충격받았다고 하더라
    조롱은 잠시 제쳐두고, MSSQL은 PostgreSQL이 부적합한 규모를 처리할 수 있다는 더 큰 주장에 그럴듯함이 있는지 궁금함. 직감으로는 말도 안 된다고 느끼지만 DBA는 전혀 아님

    • 그런 측면은 있음. 필요한 게 거의 뭐든 충분히 잘 처리하는 데이터베이스라면 MSSQL과 Oracle이 해낼 가능성이 큼
      이들은 돈과 하드웨어, 즉 더 많은 돈을 투입해서 문제가 해결될 때까지 밀어붙이는 방식으로 풀어왔음. 물론 똑똑한 기술도 들어 있지만, 근본적으로 오랜 기간 훨씬 많은 엔지니어링이 투입됐음
      PostgreSQL이 합리적으로 할 수 있는 것보다 더 크게 수평 확장할 수 있음
      다만 PostgreSQL도 따라잡고 있고, MySQL/MariaDB는 이쪽 이야기가 늘 괜찮았다고 볼 수도 있음. 수평 확장 선택지는 계속 좋아지고 있음
      이제는 적은 수의 머신으로 멀티 테라바이트 PostgreSQL 클러스터를 운영하며 큰 트래픽을 처리하고, “빅데이터”는 더 특화된 데이터베이스에 넣는 것도 쉬워졌음. 모든 걸 MSSQL/Oracle에 밀어 넣던 예전 방식은 좀 구식일 수 있음
    • MSSQL 개발을 많이 해봤고, PostgreSQL에는 조금 놀랄 만한 빠진 기능들이 있음
      친구가 말한 건 PostgreSQL이 쿼리 계획을 캐시하거나 고정하는 방법이 없다는 사실일 수 있음. PostgreSQL은 수동으로 준비된 문장을 쓰지 않는 한 문장마다 다시 계획하고, 그것도 연결별로만 동작함
      MSSQL은 오래전부터 계획을 캐시하고 재사용해왔기 때문에 플래너가 계획 수립에 더 많은 시간을 쓸 수 있음. 또한 힌트가 있고 계획을 고정할 수도 있음
      PostgreSQL에는 정말 힌트가 필요함. 최적화기가 훌륭하더라도 가끔은 내가 더 잘 알고, 내 말을 듣게 만들고 싶음
      또 PostgreSQL에는 진정한 클러스터형 인덱스가 없고 모든 테이블이 힙임. MSSQL에서는 대부분 자주 쓰고, 보통 기본 키를 클러스터형 인덱스로 설정해서 테이블 자체가 인덱스가 되며 키 조회에 간접 참조가 없음
      흥미롭게도 SQLite는 반대로 테이블이 항상 클러스터형 인덱스를 가지며, 만들든 말든 그렇고, MSSQL은 힙과 인덱스 조직 테이블 중 선택하게 해줌
    • PostgreSQL에는 쿼리 플래너가 있음. 이 글 전체가 그 개선에 관한 내용임. 그러니 의사소통이 잘못됐거나, 친구가 PostgreSQL을 전혀 모르는 것 같음
      아주 큰 PostgreSQL 데이터베이스도 잘 동작하는 사례가 있으니 PostgreSQL도 확실히 확장 가능함
      다만 SQL Server에는 PostgreSQL에 없는 기능도 있고, 그것들이 중요하다면 특정 사용 사례에는 더 잘 맞을 수 있음. 결국 서로 다른 강점과 약점을 가진 다른 데이터베이스임
    • OLTP와 데이터 웨어하우징 모두에서 둘 다 써봤고, 둘 다 괜찮음
      처음에는 SQL Server가 필요한 벤더 제공 애플리케이션만 아니었다면 회사에 PostgreSQL 이전을 추천했을 거라고 쓰려 했음
      그런데 Microsoft가 포함해주는 reporting services, integration services, 작업, AD 통합, service broker 같은 것들을 대체하는 일이 얼마나 많을지 깨달았음. notify/listen에는 메시지 타입이 없음
      analysis services는 더 이상 쓰지 않지만, 예전에 쓸 때는 그것도 대체하기 어려웠을 것임
      이런 것들이 사람을 붙잡아둠. 이 모든 걸 대체하는 데 얼마나 걸릴지 감도 안 오고, 이미 가진 것을 대체하느라 1년을 쓰는 건 투자 대비 수익이 좋지 않음
    • AWS의 Aurora는 꽤 잘 처리하는 것 같고, PostgreSQL과 MySQL의 드롭인 대체품을 목표로 함
  • 왜 이 내용이 postgresql.org가 아니라 citusdata에서 공개됐는지 궁금함. 유료 기능 전용인지, 오픈소스 추가인지 모르겠음

    • 글쓴이가 Citus Data에서 일하고, 해당 최적화 중 일부도 직접 작성했기 때문임
  • IS NOT DISTINCT FROM 쿼리를 빠르게 하려고 인덱스를 쓸 수 있게 되는 건 언제쯤일까 ;)