MySQL vs PostgreSQL 성능 비교: 대용량 운영 환경을 위한 실무 판단 기준

MySQL과 PostgreSQL의 성능은 워크로드 특성과 잠금 전략에 따라 달라지므로, 단순한 벤치마크 수치보다 읽기/쓰기 비율, 동시성 수준, 인덱스 설계, vacuum/퍼지 정책을 먼저 분석하고 선택해야 합니다.

  • MySQL InnoDB는 클러스터형 인덱스로 프라이머리 키 범위 검색에 강하지만 세컨더리 인덱스 조회 시 더블 룩업이 발생하며, 갭 락으로 인해 REPEATABLE READ에서 쓰기 경합이 커질 수 있습니다.
  • PostgreSQL은 MVCC와 READ COMMITTED 기본으로 동시성이 높은 쓰기 환경에서 확장성이 좋지만, vacuum 설정이 부실하면 테이블 부풀림으로 성능이 급격히 저하됩니다.
  • 읽기 성능은 인덱스 전략과 캐시 효율에 좌우되며, MySQL은 버퍼 풀, PostgreSQL은 shared_buffers와 OS 캐시를 함께 사용하므로 워크로드에 따라 유불리가 갈립니다.
  • 확장성 측면에서 MySQL은 다양한 복제 옵션을 제공하지만 쓰기 확장에는 샤딩이 필요하고, PostgreSQL은 논리적 복제와 선언적 파티셔닝으로 대용량 관리가 용이합니다.

성능 비교의 기준: 워크로드 특성과 스토리지 엔진 이해

MySQL과 PostgreSQL의 성능을 단순한 TPS 수치로 비교하는 것은 위험합니다. 운영 환경에서는 읽기/쓰기 비율, 동시 연결 수, 트랜잭션 길이, 데이터 볼륨, 인덱스 패턴, 복제 구조 등이 성능에 훨씬 큰 영향을 미칩니다. MySQL은 InnoDB 스토리지 엔진을 기본으로 사용하며, 멀티 버전 동시성 제어(MVCC)를 통해 읽기와 쓰기의 잠금 경합을 줄입니다. 그러나 세컨더리 인덱스에 프라이머리 키가 포함되어 있어 인덱스 크기가 커지고, 갭 락(Gap Lock)과 넥스트 키 락(Next-Key Lock)이 REPEATABLE READ 격리 수준에서 팬텀 리드를 방지하기 위해 사용되므로 쓰기 충돌이 발생할 수 있습니다. 반면 PostgreSQL은 테이블 힙(Heap)과 인덱스를 분리한 구조로 프라이머리 키가 아닌 CTID를 사용해 튜플을 참조하며, MVCC는 과거 버전을 테이블 블록에 유지하고 vacuum으로 정리합니다. 이로 인해 읽기 전용 트랜잭션이 쓰기를 차단하지 않지만, vacuum이 지연되면 테이블과 인덱스 부풀림(bloat)이 발생하여 성능이 저하될 수 있습니다. 따라서 워크로드 특성을 먼저 분석하고 각 DBMS의 잠금 및 MVCC 동작을 이해한 뒤 성능을 평가해야 합니다.

mysql postgresql 성능 비교의 핵심 내용을 시각적으로 설명하는 이미지
MySQL(InnoDB)과 PostgreSQL의 인덱스 및 MVCC 구조 차이 도식

읽기 성능: 인덱스 구조와 캐시 효율성

읽기 중심 워크로드에서 두 DBMS의 성능 차이는 인덱스 설계와 버퍼 캐시 효율에 크게 좌우됩니다. MySQL InnoDB는 클러스터형 인덱스를 사용하므로 프라이머리 키 범위 검색이 매우 빠르지만, 세컨더리 인덱스로 조회할 때는 인덱스에서 프라이머리 키를 찾은 뒤 다시 프라이머리 키 B-트리를 탐색해야 하는 더블 룩업이 발생합니다. 커버링 인덱스를 사용하면 이 오버헤드를 피할 수 있지만, 넓은 인덱스는 메모리 사용량을 증가시킵니다. PostgreSQL은 인덱스가 데이터 위치를 직접 가리키므로 세컨더리 인덱스만으로 행을 찾는 경우 더 적은 I/O로 처리할 수 있습니다. 하지만 프라이머리 키가 클러스터링되지 않으므로 범위 검색 시 힙에서 임의 접근이 많아질 수 있고, 이를 완화하려면 클러스터링 작업(CLUSTER 명령)이나 인덱스 온리 스캔을 활용해야 합니다. 캐시 측면에서 MySQL은 전용 버퍼 풀을 사용하고, PostgreSQL은 shared_buffers와 OS 페이지 캐시를 함께 사용합니다. 대용량 테이블을 자주 스캔하는 경우 PostgreSQL의 OS 캐시 활용이 유리할 수 있지만, 쓰기 부하가 높은 환경에서는 이중 캐시로 인한 오버헤드가 발생할 수 있습니다. 따라서 읽기 성능은 인덱스 전략과 메모리 설정의 조합으로 평가해야 하며, 단순히 한쪽이 우월하다고 결론 내릴 수 없습니다.

쓰기 성능: 동시성 제어와 잠금 범위

쓰기 중심 워크로드에서는 잠금 범위와 트랜잭션 격리 수준이 성능을 좌우합니다. MySQL InnoDB는 행 수준 잠금을 기본으로 하지만, REPEATABLE READ 격리 수준에서는 갭 락을 사용해 팬텀 리드를 방지하므로 인덱스가 없는 조건으로 갱신하거나 범위 갱신 시 상당한 잠금 경합이 발생할 수 있습니다. 또한 프라이머리 키나 유니크 인덱스가 없는 테이블에서는 내부적으로 생성된 클러스터형 인덱스를 사용하므로 모든 세컨더리 인덱스가 이 값을 포함하게 되어 인덱스 크기가 커지고 쓰기 성능이 저하됩니다. PostgreSQL은 기본적으로 READ COMMITTED 격리 수준에서도 MVCC를 사용하며, 갭 락을 사용하지 않고 팬텀 리드는 REPEATABLE READ에서만 방지합니다. 따라서 동시성이 높은 환경에서는 PostgreSQL이 더 나은 확장성을 보일 수 있습니다. 다만 PostgreSQL은 오래된 튜플 버전을 정리하는 vacuum 작업이 쓰기와 경합할 수 있으며, autovacuum 설정이 부적절하면 테이블 부풀림과 인덱스 팽창이 발생해 성능이 급격히 떨어질 수 있습니다. MySQL도 언두 로그가 커지면 퍼지(purge) 작업이 지연되어 비슷한 문제가 나타납니다. 결국 쓰기 성능은 잠금 전략, 인덱스 설계, vacuum/퍼지 정책을 함께 고려해야 하며, 특히 배치 작업과 OLTP 작업이 혼재된 환경에서는 각각의 격리 수준과 배치 윈도우를 신중히 선택해야 합니다.

확장성과 가용성: 복제 및 파티셔닝 전략

성능 비교는 단일 인스턴스를 넘어 확장성과 가용성까지 포함해야 합니다. MySQL은 다양한 복제 토폴로지를 지원하며, 비동기 복제, 반동기 복제, 그룹 복제(Group Replication)를 통해 읽기 확장과 고가용성을 구성할 수 있습니다. 하지만 동기화 방식에 따라 복제 지연이 발생할 수 있고, 쓰기 확장은 샤딩과 같은 수동 구성이 필요합니다. PostgreSQL은 스트리밍 복제를 기본으로 제공하며, 동기식 복제와 논리적 복제를 지원합니다. 논리적 복제를 사용하면 부분 복제나 업그레이드 중 다운타임을 줄일 수 있고, 외부 도구(Pgpool-II, Patroni 등)와 결합해 자동 장애 조치를 구성할 수 있습니다. 파티셔닝에서 MySQL은 파티션 키가 프라이머리 키와 유니크 키에 포함되어야 하는 제약이 있어 설계에 유의해야 하며, PostgreSQL은 선언적 파티셔닝과 파티션 프루닝(pruning)을 통해 대용량 테이블 관리가 용이합니다. 다만 파티셔닝이 항상 성능 향상을 보장하지는 않으므로, 파티션 수와 쿼리 패턴을 검증해야 합니다. 결론적으로 단일 노드 성능 못지않게 복제 지연 허용 범위, 장애 조치 시간, 샤딩 필요성 등을 기준으로 시스템을 선택하고, 벤치마크는 실제 운영 패턴을 반영한 시나리오로 수행해야 합니다.

출처

  1. MySQL 8.0 Reference Manual – InnoDB Locking Oracle · 2026-09-27
  2. PostgreSQL 16 Documentation – Concurrency Control PostgreSQL Global Development Group · 2026-09-27
  3. PostgreSQL 16 Documentation – Index Types PostgreSQL Global Development Group · 2026-09-27
  4. MySQL 8.0 Reference Manual – InnoDB Multi-Versioning Oracle · 2026-09-27

댓글 남기기