글 목록

Backend

PostgreSQL vs. MySQL: 비슷해 보이지만 선택 기준은 꽤 다르다

PostgreSQL과 MySQL은 모두 훌륭한 RDBMS입니다. SQL 기능, JSON, 동시성, 확장성, 운영 관점의 차이를 살펴보고 프로젝트에 맞는 선택 기준을 정리합니다.

웹 서비스를 개발하다 보면 한 번쯤 이런 질문을 만나게 됩니다.

“PostgreSQL이 좋아요, MySQL이 좋아요?”

둘 다 오랫동안 검증된 오픈소스 RDBMS(Relational Database Management System)이고, 일반적인 웹 애플리케이션에서 요구하는 대부분의 기능을 충분히 제공합니다. 그래서 단순히 “어느 DB가 더 좋은가?”라는 질문으로 접근하면 답하기가 어렵습니다.

조금 더 실용적인 질문은 이것입니다.

“우리 서비스의 데이터와 쿼리 특성에는 어느 데이터베이스가 더 잘 맞는가?”

이번 글에서는 PostgreSQL과 MySQL의 차이를 기능 목록으로만 비교하기보다는, 실제 서비스를 설계하고 운영할 때 어떤 차이가 체감되는지를 중심으로 정리해보겠습니다.

먼저 결론부터

PostgreSQL과 MySQL 중 하나가 항상 우월하다고 말하기는 어렵습니다.

다만 프로젝트의 성격에 따라 선택 기준을 어느 정도 단순화할 수 있습니다.

상황 고려할 DB
일반적인 CRUD 중심 웹 서비스 PostgreSQL / MySQL
복잡한 SQL과 분석 쿼리가 많음 PostgreSQL
데이터 무결성과 복잡한 데이터 모델이 중요함 PostgreSQL
JSON과 관계형 데이터를 함께 적극적으로 사용 PostgreSQL
기존 MySQL 기반 인프라와 운영 경험이 풍부함 MySQL
단순하고 예측 가능한 웹 트랜잭션이 중심 MySQL
DB 기능을 적극적으로 확장해서 사용 PostgreSQL

새로운 프로젝트를 시작하면서 특별한 제약이 없다면 PostgreSQL을 기본 후보로 두고 검토하는 것이 꽤 합리적인 출발점입니다.

반대로 조직에 이미 안정적인 MySQL 운영 환경과 충분한 경험이 있다면, 단순히 PostgreSQL의 기능이 더 많다는 이유만으로 데이터베이스를 변경할 필요는 없습니다.

결국 중요한 것은 feature count가 아니라 workload와 operational experience입니다.


PostgreSQL과 MySQL은 무엇이 다른가

PostgreSQL과 MySQL은 모두 SQL을 사용하는 관계형 데이터베이스입니다.

예를 들어 다음과 같은 기본적인 쿼리는 양쪽에서 거의 동일하게 작성할 수 있습니다.

SELECT id, name, email
FROM users
WHERE status = 'ACTIVE'
ORDER BY created_at DESC
LIMIT 20;

INSERT, UPDATE, DELETE, JOIN, INDEX, TRANSACTION 같은 웹 애플리케이션의 기본적인 요구사항 역시 두 데이터베이스 모두 잘 지원합니다.

그래서 작은 프로젝트에서는 둘의 차이가 크게 느껴지지 않을 수도 있습니다.

차이가 본격적으로 나타나는 지점은 애플리케이션이 성장하면서부터입니다.

데이터 모델이 복잡해지고,

  • JOIN이 많아지고
  • transaction이 복잡해지고
  • JSON 데이터를 함께 저장하고
  • 다양한 index 전략이 필요해지고
  • concurrency가 높아지고
  • 운영 중 schema를 변경해야 하는

상황이 생기면 두 데이터베이스가 가진 철학과 기능의 차이가 조금씩 드러납니다.


1. SQL 기능과 복잡한 Query

PostgreSQL의 대표적인 강점 중 하나는 풍부한 SQL 기능입니다.

CTE(Common Table Expression), Window Function, 다양한 JOIN 및 aggregation을 활용하는 복잡한 쿼리를 작성할 때 PostgreSQL은 상당히 강력합니다.

예를 들어 사용자별 최근 주문과 누적 주문 금액을 계산한다고 생각해보겠습니다.

WITH ranked_orders AS (
    SELECT
        user_id,
        id AS order_id,
        amount,
        created_at,
        ROW_NUMBER() OVER (
            PARTITION BY user_id
            ORDER BY created_at DESC
        ) AS rn,
        SUM(amount) OVER (
            PARTITION BY user_id
        ) AS total_amount
    FROM orders
)
SELECT
    user_id,
    order_id,
    total_amount
FROM ranked_orders
WHERE rn = 1;

물론 현대의 MySQL도 CTE와 Window Function 등 상당히 많은 SQL 기능을 지원합니다.

따라서 “이 기능은 PostgreSQL에서만 된다”는 식의 단순 비교는 점점 의미가 없어지고 있습니다.

대신 실제 차이는 복잡한 query와 data model을 얼마나 적극적으로 DB에서 표현할 것인가에서 나타납니다.

Application layer에서 대부분의 비즈니스 로직을 처리하고 DB에는 비교적 단순한 CRUD query만 전달한다면 MySQL도 충분히 좋은 선택입니다.

반대로 database 자체의 query capability를 적극적으로 활용하고 싶다면 PostgreSQL이 매력적인 경우가 많습니다.


2. JSON을 사용한다면 PostgreSQL?

현대의 애플리케이션에서는 모든 데이터를 완벽하게 정규화된 테이블로 표현하기 어려운 경우가 있습니다.

예를 들어 상품별로 서로 다른 metadata를 저장한다고 해보겠습니다.

{
  "color": "black",
  "size": "M",
  "features": ["waterproof", "lightweight"]
}

이런 데이터를 위해 PostgreSQL은 json과 jsonb 타입을 제공합니다.

특히 jsonb를 사용하면 JSON document 내부의 값을 query하거나 index를 구성할 수 있습니다.

CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    metadata JSONB NOT NULL DEFAULT '{}'
);

그리고 다음처럼 JSON 내부의 값을 조회할 수 있습니다.

SELECT *
FROM products
WHERE metadata->>'color' = 'black';

GIN index를 활용하는 방식도 고려할 수 있습니다.

CREATE INDEX idx_products_metadata
ON products
USING GIN (metadata);

MySQL 역시 native JSON type과 관련 JSON 함수 및 indexing 전략을 지원합니다.

따라서 JSON 지원 자체가 PostgreSQL만의 기능은 아닙니다.

다만 relational model과 document-like data를 한 데이터베이스에서 적극적으로 섞어서 사용하는 설계에서는 PostgreSQL의 JSONB와 indexing ecosystem이 특히 매력적입니다.

여기서 한 가지 주의할 점도 있습니다.

JSON을 지원한다고 해서 모든 데이터를 JSON 컬럼 하나에 넣는 것이 좋은 설계라는 뜻은 아닙니다.

users
└── data JSONB
    ├── name
    ├── email
    ├── birthday
    ├── address
    └── ...

이렇게 모든 것을 JSON으로 처리하면 constraint, relation, query optimization 측면에서 오히려 관계형 데이터베이스의 장점을 잃을 수 있습니다.

Schema가 명확한 데이터는 column으로, 유동적인 데이터는 JSON으로.

이 정도를 기본 원칙으로 잡는 편이 안전합니다.


3. 데이터 타입과 확장성

PostgreSQL을 사용하면서 개인적으로 가장 흥미로운 부분 중 하나는 데이터베이스를 단순한 “table storage” 이상으로 활용할 수 있다는 점입니다.

PostgreSQL은 다양한 built-in data type뿐 아니라 extension ecosystem을 가지고 있습니다.

대표적으로 PostGIS를 추가하면 geographic data를 전문적으로 다룰 수 있습니다.

CREATE EXTENSION postgis;

이를 기반으로 위치 기반 검색이나 공간 연산 같은 기능을 관계형 데이터와 함께 처리할 수 있습니다.

PostgreSQL 생태계에서는 이 외에도 특정 workload를 위한 다양한 extension을 활용할 수 있습니다.

이 특성은 초기 프로젝트에서는 큰 의미가 없을 수도 있습니다.

하지만 서비스가 성장하면서 검색, GIS, vector 등 일반적인 relational workload 밖의 요구사항이 생기면 이야기가 달라집니다.

별도의 시스템을 추가하기 전에 PostgreSQL ecosystem 안에서 해결할 수 있는 선택지가 생기기 때문입니다.

다만 여기에서도 “가능하다”와 “운영하기 좋다”는 구분해야 합니다.

모든 기능을 PostgreSQL 하나에 넣는 것이 항상 좋은 architecture는 아닙니다.


4. Transaction과 Concurrency

웹 서비스에서는 여러 사용자가 동시에 데이터를 읽고 수정합니다.

따라서 database를 비교할 때 단순 query performance만큼 중요한 것이 concurrency입니다.

PostgreSQL은 MVCC(Multi-Version Concurrency Control)를 기반으로 동시성을 처리합니다.

개념적으로 생각하면 하나의 row가 변경되는 동안 다른 transaction이 무조건 기다리는 것이 아니라, 각 transaction이 적절한 version의 데이터를 바라볼 수 있도록 관리하는 방식입니다.

예를 들어 다음과 같은 transaction이 있다고 해보겠습니다.

BEGIN;

SELECT balance
FROM accounts
WHERE id = 100
FOR UPDATE;

UPDATE accounts
SET balance = balance - 10000
WHERE id = 100;

COMMIT;

MySQL의 일반적인 InnoDB 환경 역시 transaction, row-level locking, MVCC를 제공합니다.

따라서 단순히

PostgreSQL은 MVCC이고 MySQL은 아니다.

라고 이해하면 틀립니다.

두 DB 모두 높은 수준의 transactional workload를 처리할 수 있습니다.

실제 차이를 이해하려면 isolation level, locking behavior, deadlock, vacuum/garbage collection, long-running transaction 같은 운영 특성을 함께 살펴봐야 합니다.

이 부분은 database를 선택할 때 benchmark만 보는 것이 위험한 이유이기도 합니다.


5. Performance: 그래서 누가 더 빠른가?

PostgreSQL과 MySQL을 비교할 때 가장 자주 나오는 질문입니다.

“그래서 어떤 게 더 빠른가요?”

아쉽게도 답은 보통 “depends”​입니다.

Database performance는 DB 제품 이름 하나로 결정되지 않습니다.

예를 들어 다음 요소들이 훨씬 큰 영향을 줄 수 있습니다.

  • Query pattern
  • Index design
  • Data distribution
  • Connection count
  • Read/write ratio
  • Transaction size
  • Hardware
  • Memory configuration
  • Cache hit ratio
  • Replication architecture
  • Database version

단순한 primary-key lookup에서는 둘 다 충분히 빠를 수 있습니다.

SELECT *
FROM users
WHERE id = 12345;

반면 여러 table을 JOIN하고 aggregation과 filtering을 수행하는 query에서는 optimizer와 index 설계에 따라 결과가 크게 달라질 수 있습니다.

그래서 “PostgreSQL vs. MySQL benchmark” 결과 하나를 보고 데이터베이스를 선택하는 것은 추천하기 어렵습니다.

가능하다면 실제 서비스의 query와 유사한 workload로 테스트하는 것이 가장 좋습니다.

Synthetic Benchmark
        ↓
실제 Query Pattern과 다를 수 있음
        ↓
잘못된 DB 선택 가능

Production-like Benchmark
        ↓
실제 Schema + Index + Query
        ↓
더 의미 있는 결과

Database benchmark에서는 무엇을 측정했는가가 숫자 자체보다 중요합니다.


6. Replication과 High Availability

Production database에서 replication과 failover는 빼놓을 수 없는 주제입니다.

MySQL과 PostgreSQL 모두 replication과 high availability architecture를 구성할 수 있습니다.

일반적으로는 다음과 같은 구조를 생각할 수 있습니다.

                ┌──────────────┐
                │ Application  │
                └───────┬──────┘
                        │
                 Write / Read
                        │
                ┌───────▼──────┐
                │   Primary    │
                └───────┬──────┘
                        │
                  Replication
                 ┌──────┴──────┐
                 │             │
          ┌──────▼─────┐ ┌────▼───────┐
          │  Replica 1 │ │  Replica 2 │
          └────────────┘ └────────────┘

하지만 실제 production에서는 DB 자체의 기능만으로 판단하기 어렵습니다.

Cloud provider의 managed database를 사용한다면 선택 기준이 달라지기 때문입니다.

예를 들어 AWS RDS, Amazon Aurora, Google Cloud SQL, Azure Database 같은 managed service에서는 backup, replication, monitoring, failover와 관련된 상당 부분을 플랫폼이 대신 처리합니다.

따라서 database engine만 비교하기보다

Database + Cloud Platform + Team Experience

전체를 하나의 운영 시스템으로 보고 판단하는 편이 현실적입니다.


7. 운영 경험은 생각보다 중요한 선택 기준이다

기술적으로 PostgreSQL이 더 적합해 보이더라도 팀 전체가 MySQL을 오랫동안 운영해왔다면 MySQL을 선택하는 것이 더 나은 결정일 수 있습니다.

Database 운영에는 SQL 작성 능력 외에도 많은 경험이 필요합니다.

예를 들어,

  • Slow query 분석
  • Index tuning
  • Backup / Restore
  • Replication lag 대응
  • Connection 관리
  • Schema migration
  • Lock 분석
  • Deadlock 대응
  • 장애 복구

같은 일들이 있습니다.

Production에서 문제가 발생했을 때 “이론적으로 더 좋은 DB”보다 “팀이 문제를 빠르게 진단하고 복구할 수 있는 DB”가 더 좋은 선택일 수 있습니다.

이것은 database뿐 아니라 대부분의 infrastructure technology를 선택할 때 적용되는 기준이라고 생각합니다.


8. PostgreSQL을 선택하기 좋은 경우

다음과 같은 프로젝트라면 PostgreSQL을 우선적으로 검토할 만합니다.

복잡한 데이터 모델

많은 relation과 constraint가 존재하고 데이터 무결성이 중요한 서비스입니다.

예를 들면 금융, ERP, SaaS platform 등이 있을 수 있습니다.

복잡한 Query

Reporting, analytics, aggregation 등 SQL 자체의 표현력을 적극적으로 활용해야 하는 경우입니다.

JSON + Relational Data

정형 데이터와 반정형 데이터를 같은 database에서 유연하게 다뤄야 하는 경우입니다.

PostgreSQL Extension 활용

GIS 같은 PostgreSQL ecosystem의 기능이 프로젝트에 중요한 경우입니다.


9. MySQL을 선택하기 좋은 경우

MySQL 역시 여전히 매우 강력하고 실용적인 선택입니다.

기존 MySQL Infrastructure가 있는 경우

이미 MySQL replication, monitoring, backup, migration pipeline 등이 잘 구축되어 있다면 이것은 상당히 큰 자산입니다.

팀의 MySQL 운영 경험이 풍부한 경우

Database 장애 대응에서 팀의 경험은 기술적인 feature 차이보다 중요할 수 있습니다.

단순한 Web Transaction 중심

비교적 단순한 CRUD workload가 대부분이고 복잡한 DB 기능이 필요하지 않다면 MySQL은 충분히 좋은 선택입니다.

특히 기존 ecosystem과 infrastructure가 MySQL을 중심으로 구축되어 있다면 굳이 PostgreSQL로 이동해서 새로운 operational complexity를 만들 이유가 없을 수 있습니다.


10. 새 프로젝트라면 무엇을 선택할까?

아무런 legacy constraint가 없는 새로운 backend 프로젝트라고 가정해보겠습니다.

저라면 다음 질문부터 확인하겠습니다.

1. 팀이 어떤 DB를 더 잘 운영하는가?
              │
              ▼
2. 복잡한 SQL / JSON / 특수 데이터 타입이 필요한가?
              │
              ▼
3. 예상되는 Read / Write workload는 무엇인가?
              │
              ▼
4. 어떤 Managed DB 서비스를 사용할 것인가?
              │
              ▼
5. 실제 workload로 benchmark했을 때 문제가 없는가?

이 질문에 특별한 제약이 없다면 PostgreSQL을 먼저 검토할 가능성이 높습니다.

PostgreSQL은 전통적인 relational database 역할을 충실하게 수행하면서도 JSON, 다양한 index와 data type, extension 등 서비스가 성장하면서 사용할 수 있는 선택지가 넓기 때문입니다.

하지만 이것을

“새 프로젝트에서는 무조건 PostgreSQL을 사용해야 한다.”

라는 의미로 받아들일 필요는 없습니다.

팀이 MySQL에 익숙하고 workload가 MySQL에 잘 맞는다면 MySQL을 사용하는 것이 더 합리적입니다.


PostgreSQL vs. MySQL을 고르는 진짜 기준

Database를 선택할 때 feature comparison table부터 만드는 경우가 많습니다.

하지만 실제로 중요한 질문은 조금 다릅니다.

우리 서비스가 어떤 데이터를 저장하고, 어떤 방식으로 읽고 쓰며, 누가 이 시스템을 운영할 것인가?

PostgreSQL은 풍부한 SQL 기능, 데이터 타입, JSONB, extension ecosystem 때문에 복잡한 data-intensive application에서 특히 매력적입니다.

MySQL은 오랜 기간 웹 서비스에서 검증되어 왔고, 풍부한 운영 경험과 ecosystem을 가지고 있습니다. 기존 MySQL infrastructure를 보유한 조직에서는 여전히 매우 합리적인 선택입니다.

그래서 최종적으로는 이렇게 정리할 수 있을 것 같습니다.

PostgreSQL과 MySQL 중 “더 좋은 데이터베이스”를 찾기보다, 우리 시스템에서 “더 예측 가능하게 운영할 수 있는 데이터베이스”를 선택하는 것이 중요합니다.

Database는 한 번 선택하면 오랫동안 함께하게 되는 기술입니다.

몇 퍼센트의 benchmark 차이보다 데이터 모델, query pattern, 운영 경험 그리고 앞으로 서비스가 어떤 방향으로 성장할지를 함께 보고 선택하는 것이 결국 더 좋은 결과를 만듭니다.

Tags

태그

PostgreSQLMySQLDatabaseRDBMSSQL