데이터베이스 선택, 무엇부터 봐야 할까?

워크로드의 일관성 요구도(strong consistency vs. eventual consistency), 수평 확장 방식(샤딩 vs. 복제), 읽기-쓰기 비율이 답을 결정한다. 벡터 연산이 표준 데이터타입으로 흡수되면서 "전용 벡터DB vs. 관계형" 경계는 더 이상 절대적이지 않으며, 대신 기존 저장소가 벡터 인덱싱 구조를 얼마나 효율적으로 구현했는가가 판단 기준이 되었다.

강한 일관성이 필수인가, 약한 일관성으로 충분한가?

강한 일관성(ACID)이 필수라면 단일 리더 또는 리더-팔로워 복제 구조를 채택한 관계형 또는 NewSQL 엔진을 택한다. 약한 일관성(eventual consistency)을 허용한다면 다중 쓰기 노드를 운영할 수 있고 분산 쓰기 지연이 줄어든다.

관계형 데이터베이스의 전통적 모델은 ACID 트랜잭션을 중심으로 설계되었다. PostgreSQL, MySQL 같은 엔진은 기본적으로 강한 일관성을 보장하되, 동기식 복제(synchronous replication)를 활성화하면 쓰기 레이턴시가 증가한다. 비동기식 복제로 전환하면 쓰기 처리량은 늘지만 팔로워 노드가 임시로 오래된 상태(stale read)를 제공할 수 있다.

NewSQL 엔진(CockroachDB, TiDB 등)은 분산 환경에서 강한 일관성을 유지하려 설계되었다. 이들은 Raft나 유사한 합의 알고리즘으로 여러 노드 간 상태를 동기화한다. 대신 합의 오버헤드로 인해 지연시간이 단일 노드 RDB보다 높은 편이다(일반적으로 수십 ms~백 ms 범위, 네트워크 분할 상황 제외).

DynamoDB나 Cassandra 같은 NoSQL 엔진은 기본값으로 eventual consistency를 채택한다. 쓰기 가용성과 분할 내구성을 우선하며, 읽기 일관성 수준을 애플리케이션에서 명시적으로 설정할 수 있다(quorum read vs. single replica read).

판단 기준: 금융 거래, 재고 관리 등 오류가 직접 손실로 연결되는 시스템은 강한 일관성을 필수로 본다. 사용자 피드, 캐시된 분석 지표, 로그 저장 같은 시스템은 약한 일관성으로 충분하다.

어느 쓰기 모델이 처리량을 최대화할까?

단일 쓰기 노드 구조는 쓰기 처리량에 상한선이 있지만 일관성을 단순하게 관리한다. 다중 쓰기 노드 구조는 처리량을 확장할 수 있지만 충돌 해결(conflict resolution) 복잡도가 올라간다.

단일 리더-다중 팔로워 모델에서 쓰기는 리더(primary) 노드에만 도달한다. 팔로워(replica)는 읽기만 처리한다. PostgreSQL, MySQL, SQLite는 이 구조를 표준으로 구현한다. 쓰기 처리량의 천장은 단일 머신의 디스크 I/O와 CPU 능력으로 결정된다. 읽기-집약적 워크로드에서는 팔로워를 추가하면 읽기 처리량이 선형으로 증가한다.

다중 마스터 복제(multi-master replication)에서는 모든 노드가 쓰기를 수락한다. CockroachDB, TiDB, DynamoDB 모두 이 구조를 지원한다. 하지만 동일 행을 여러 노드에서 동시에 수정하면 충돌이 발생한다. 관계형 엔진들은 트랜잭션 격리 수준(serializable, snapshot isolation)으로 충돌을 감지하고 재시도를 강제한다. 최종 일관성(eventual consistency) 엔진들은 타임스탬프나 벡터 클록(vector clock)으로 인과관계를 추적하고, 마지막 쓰기 우승(last-write-wins) 같은 휴리스틱으로 자동 합병한다.

NewSQL 엔진의 분산 쓰기는 합의 알고리즘 오버헤드가 있다. Raft 합의에서 리더 선출, 로그 복제, 커밋 동기화에 여러 왕복 통신이 필요하다. 실측 벤치마크에서 단일 마스터 PostgreSQL 대비 CockroachDB는 복제 인수 3(replication factor 3)일 때 쓰기 레이턴시가 2배~5배 높지만, 가용성과 수평 확장성을 얻는다.

판단 기준: 초당 10,000 쓰기 이상이 필요하고 단일 노드로는 부족하다면 다중 쓰기 모델을 고려한다. 쓰기가 sparse(흩어진 행에 분산)하다면 충돌 확률이 낮아 다중 마스터 이득이 크다. 반대로 동일 행에 집중된 쓰기라면 단일 리더에서 순차 처리하는 게 오버헤드가 적을 수 있다.

읽기-쓰기 비율에 따라 인덱스 전략이 달라지는가?

쓰기가 많으면 인덱스 유지 비용이 높아지므로 인덱스를 신중하게 선택한다. 읽기가 많으면 여러 인덱스를 운영해 검색 경로를 단축할 수 있다.

B-Tree 인덱스(전통 RDB의 표준)는 삽입·삭제·업데이트 시마다 트리를 재정렬한다. 쓰기 비율이 높으면 인덱스 재구성에 드는 CPU와 디스크 I/O가 전체 쓰기 지연시간의 상당 부분을 차지한다. PostgreSQL의 BRIN(Block Range Index)이나 Cassandra의 SSTable 기반 인덱스는 순차 쓰기에 최적화되어 있고, 무작위 업데이트가 많으면 비효율적이다.

LSM Tree(Log-Structured Merge Tree) 인덱스는 쓰기 집약적 워크로드에서 B-Tree보다 처리량이 높다. RocksDB(LevelDB 기반)를 내장한 엔진들(많은 NoSQL DB)은 메모리 버퍼에 쓰기를 모았다가 배치로 디스크에 플러시한다. 대신 읽기 시 여러 레벨의 파일을 조회해야 하므로 읽기 지연시간은 B-Tree보다 비교적 길다.

읽기-집약적 워크로드(읽기:쓰기 = 100:1 이상)에서는 이차 인덱스(secondary index)를 적극 활용할 수 있다. 각 필터링 조건, 집계 경로마다 인덱스를 생성해 전체 테이블 스캔을 피한다. 컬럼형 저장소(ClickHouse, Parquet)는 OLAP 쿼리의 읽기 경로를 극도로 최적화한다.

판단 기준: 읽기:쓰기 비율이 10:1 이상이면 B-Tree 기반 RDB를 권한다. 1:1~10:1은 하이브리드 최적화(파티셔닝, 선택적 인덱싱)를 검토한다. 1:10 이하의 쓰기 집약적 워크로드라면 LSM Tree 구조나 NoSQL을 고려한다.

벡터 연산은 이제 별도 DB가 필수일까?

2026년 기준, pgvector(PostgreSQL 확장)와 MySQL Vector, MongoDB의 벡터 검색, 그리고 전용 벡터DB(Pinecone, Milvus, Weaviate) 사이의 선택은 벡터 인덱싱 성능과 쿼리 레이턴시 기준으로 결정된다. 벡터 데이터타입 자체는 이제 주요 RDB에 표준으로 흡수되었다.

pgvector와 같은 RDB 확장은 HNSW(Hierarchical Navigable Small World) 또는 IVFFlat 인덱스를 제공한다. 검색 시간 복잡도는 O(log n)에서 O(k + log n) 범위(k는 후보 집합 크기)이며, 메모리 효율성이 좋다. 대신 1억 건 이상의 고차원 벡터(1536차원 이상)를 단일 테이블에서 관리할 때 인덱스 구축 시간이 수십 분~수 시간이 걸린다. 벡터 검색과 스칼라 필터링(메타데이터 조건)을 동일 쿼리에서 결합할 수 있다는 게 RDB의 이점이다.

전용 벡터DB는 벡터 검색에만 최적화되어 있다. 인덱싱 알고리즘, 메모리 관리, 병렬 검색을 벡터 워크로드에 특화시켰다. 검색 레이턴시(p99)가 RDB보다 빠를 수 있고, 초당 수천 건의 유사도 검색(similarity search)을 처리해야 할 때 유리하다. 하지만 메타데이터(텍스트, 숫자, 날짜)는 별도로 관리해야 하고, 벡터와 메타데이터 간 동기화 로직을 애플리케이션에서 처리해야 한다.

RDB 확장이 지원하는 메트릭(L2, cosine, IP)과 전용 벡터DB가 지원하는 메트릭을 확인해야 한다. 대부분은 cosine 유사도를 표준으로 하지만, IP(inner product)나 Hamming 거리 같은 특수 메트릭은 전용 DB에서 더 빠를 수 있다.

판단 기준: 벡터 검색 요청이 초당 100 이하이고, 메타데이터 필터링과 함께 쿼리해야 한다면 pgvector 같은 RDB 확장으로 충분하다. 초당 1,000 이상의 검색 요청, 밀리초 수준 레이턴시 SLA, 벡터만 독립적으로 저장해도 되는 시스템이라면 전용 벡터DB를 검토한다.

운영 부담은 어디서 갈리는가?

관계형 데이터베이스는 스키마 관리, 백업·복구, 복제 모니터링의 복잡도가 예측 가능하다. 분산 엔진은 노드 간 일관성 추적, 장애 감지와 페일오버 자동화, 데이터 리밸런싱의 오버헤드가 추가된다.

단일 마스터 구조(PostgreSQL, MySQL)는 백업 정책이 직관적이다. 마스터에서 주기적으로 전체 백업을 떠두고, WAL(Write-Ahead Log)을 보관하면 시점 복구(point-in-time recovery)가 가능하다. 팔로워는 마스터 데이터의 읽기 전용 복제본이므로, 팔로워 장애는 단순히 다시 시작하면 된다. 마스터 장애 시 수동으로 팔로워를 승격하거나, 자동화 도구(patroni, orchestrator)를 도입해야 한다.

분산 엔진(CockroachDB, TiDB)에서는 모든 노드가 동등한 역할을 한다. 따라서 백업은 모든 노드에서 동시에 스냅샷을 떠야 일관성 있는 백업이 된다. 노드 추가/제거 시 데이터가 자동으로 리밸런싱되는데, 리밸런싱 중 클러스터 성능이 저하될 수 있다. 네트워크 분할(network partition) 감지와 복구 자동화가 필수적이다.

모니터링 차원에서 RDB는 슬로우 쿼리 로그, 테이블 락 상태, 복제 지연(replication lag)을 주시한다. 분산 엔진은 여기에 합의 알고리즘 상태, 리더십 변경, 리전 간 지연까지 모니터링해야 한다.

비관리형(managed) 서비스는 이런 운영 부담을 클라우드 제공자가 맡는다. 대신 비용이 높고, 특정 설정이나 최적화에 제약이 있을 수 있다.

판단 기준: 운영 자원이 제한적이거나 DevOps 팀이 작다면 관리형 서비스 또는 단일 마스터 RDB를 택한다. 높은 가용성(99.99% 이상)과 자동 페일오버가 필수라면 분산 NewSQL 또는 클라우드 관리형을 검토한다.

비용, 성능, 운영성 사이의 실제 선택은?

관계형 데이터베이스(PostgreSQL, MySQL) — 저비용 자체 구성, 검증된 아키텍처. 단일 마스터 병목으로 수평 확장 한계. 읽기 복제 추가로 읽기 스케일 가능. 미션 크리티컬 시스템에서 오래된 검증 기록.

NewSQL(CockroachDB, TiDB) — 자동 수평 확장, 강한 일관성 유지. 운영 복잡도 증가, 단일 RDB 대비 높은 레이턴시. 높은 가용성 요구와 데이터 크기 증가 시 고려.

NoSQL(DynamoDB, Cassandra) — 극한의 처리량과 가용성. 약한 일관성, 이차 인덱싱 제약. 로그, 시계열, 콘텐츠 분산 저장에 적합.

전용 스토어 — OLAP(ClickHouse, Snowflake), 시계열(InfluxDB, TimescaleDB), 검색(Elasticsearch) 등 특화된 워크로드에 최적화. 다중 시스템 운영 오버헤드.

실제 선택 흐름: ① 강한 일관성 필수 여부 판단. ② 초당 쓰기 요청 수와 단일 노드 한계 파악. ③ 읽기-쓰기 비율로 인덱싱 전략 결정. ④ 벡터 요구사항 있으면 RDB 확장 vs. 전용 DB 평가. ⑤ 운영 비용과 팀 역량에 따라 자체 구성 vs. 관리형 선택.

흔한 함정: 스키마 진화와 쿼리 패턴의 불일치

많은 팀이 초기 쿼리 패턴 기준으로 스키마를 설계한 후, 실제 운영에서 예상 밖의 필터링 조건이 등장할 때 당황한다. "사용자 ID와 생성 날짜로만 필터링할 것 같은데" 라고 설계했다가, 운영 중 상태(status) 필터링, 지역(region) 조건이 추가된다.

관계형 데이터베이스는 온라인 스키마 변경(online schema alteration)이 가능하지만, 대규모 테이블에서 새 인덱스 생성은 쓰기를 블록하거나 상당한 리소스를 소비한다. PostgreSQL의 CONCURRENTLY 옵션은 동시성을 유지하지만 2배 이상의 처리 시간이 필요하다. 미리 불릿 인덱스를 여러 개 준비하는 게 현실적이다.

NoSQL에서는 스키마 유연성이 높지만, 쿼리 패턴이 비포괄적(ad-hoc)이면 풀 테이블 스캔에 빠지기 쉽다. 인덱싱 가능 항목이 사전 선언되어야 하고, 선언하지 않은 필드로는 효율적 쿼리가 불가능하다.

회피 방법: ① 초기 설계에서 향후 6~12개월의 예상 쿼리 패턴을 수집한다(실제 비즈니스 요구 인터뷰). ② 잠재적 필터링 필드에 미리 인덱스를 만들어 둔다(초기 디스크 비용은 낮다). ③ 분석 목적으로 원본 DB와 별개의 분석 저장소(data warehouse)를 유지해 OLAP 쿼리를 분리한다.

핵심 정리

  • 일관성 모델이 첫 기준: 트랜잭션 무결성이 필수면 강한 일관성(ACID) 기반 RDB 또는 NewSQL. 비용이 우선이고 약한 일관성을 허용하면 NoSQL.

  • 쓰기 확장성은 아키텍처로 결정: 단일 마스터는 읽기만 확장 가능. 다중 쓰기 노드는 합의 알고리즘 오버헤드와 충돌 해결 복잡도 증가.

  • 읽기-쓰기 비율로 인덱싱 선택: 쓰기 중심(1:10)이면 LSM Tree. 읽기 중심(100:1)이면 B-Tree와 이차 인덱스 다중 생성.

  • 벡터 검색은 더 이상 별도 DB 필수 아님: pgvector 등 RDB 확장으로 충분한 경우 많음. 초당 1,000+ 검색과 밀리초 SLA 필요할 때만 전용 벡터 DB 평가.

  • 분산 엔진의 운영 비용이 높음: 자동 페일오버와 고가용성 얻지만, 모니터링과 튜닝 복잡도가 선형 증가. 관리형 서비스 또는 충분한 DevOps 자원 필요.

  • 스키마 진화는 운영 중 병목: 초기 설계에서 향후 쿼리 패턴 예측하고 미리 인덱스 준비. 분석 워크로드는 별도 저장소로 분리.

  • 단순함 > 정교함: 복잡한 분산 엔진보다 단순 RDB + 읽기 복제가 운영, 모니터링, 문제 해결이 쉬운 경우가 많음.

자주 묻는 질문

PostgreSQL과 MySQL 중 어느 것이 더 스케일 가능한가?

둘 다 단일 마스터 아키텍처이므로 쓰기 확장성은 동일한 수준이다. PostgreSQL은 더 강력한 인덱싱 옵션(BRIN, Partial Index)과 파티셔닝 기능을 제공한다. MySQL은 NDB Cluster 같은 분산 변종이 있지만, 표준 InnoDB는 PostgreSQL과 유사한 제약이 있다. 읽기 확장이 필요하면 둘 다 복제를 통해 가능하다.

CockroachDB와 TiDB는 어떻게 다른가?

둘 다 NewSQL으로 분산 강한 일관성을 제공한다. CockroachDB는 Raft 합의, TiDB는 etcd 기반 PD(Placement Driver)를 사용한다. CockroachDB는 지역 간 다중 클러스터 복제(geo-distributed replication)가 더 정교하다. TiDB는 MySQL 호환성을 더 높게 지향하며, 클라우드 기반 관리형(TiDB Cloud)이 발전해 있다. 선택은 기존 인프라와 호환성, 팀의 운영 경험에 따라 달라진다.

DynamoDB와 Cassandra를 구분하는 기준은?

DynamoDB는 AWS 관리형이므로 운영 부담이 낮고, 버스트 트래픽에 대한 자동 스케일링이 간편하다. 대신 쿼리 모델이 제한적(파티션 키 + 소트 키)이고, 비용이 높다. Cassandra는 자체 구성해야 하지만 쿼리 유연성이 높고, 장기 비용이 낮다. 선택은 ① 운영 자원 여유 ② 장기 비용 계산 ③ 쿼리 패턴 복잡도로 결정한다.

벡터 검색을 위해 pgvector를 쓸 때 성능 한계는 어디인가?

pgvector의 HNSW 인덱스는 단일 PostgreSQL 인스턴스에서 약 1억~10억 벡터(차원 1536)까지 실용적이다. 그 이상이면 인덱스 구축 시간이 수 시간 이상 걸리고, 검색 레이턴시(p99)가 악화된다. 또한 메모리 풀에 전체 인덱스를 올리지 못하면 디스크 I/O로 인한 지연이 증가한다. 초당 10,000 검색 요청 이상에서도 단일 인스턴스 한계가 보인다.

시계열 데이터는 따로 DB를 써야 하나?

시계열 데이터(센서, 로그, 메트릭)는 RDB에서도 저장 가능하지만, 시간 범위 쿼리 최적화, 데이터 압축, 자동 집계(downsampling)가 필요하면 전용 엔진(InfluxDB, TimescaleDB)이 효율적이다. TimescaleDB는 PostgreSQL 확장이므로, 기존 PostgreSQL 인프라가 있으면 마이그레이션 비용이 낮다. InfluxDB는 독립적이지만 시계열 특화 인덱싱과 압축이 더 정교하다.

스냅샷 격리(snapshot isolation)와 직렬화 격리(serializable isolation)는 언제 구분해야 하나?

스냅샷 격리는 읽은 데이터가 변경되지 않음을 보장하지만, 쓰기 스큐(write skew) 이상이 발생할 수 있다. 직렬화 격리는 모든 이상을 방지하지만 충돌 감지 오버헤드가 크다. 금융 거래, 예약 시스템 같은 시스템에서는 직렬화를 필수로 본다. 대부분의 일반 비즈니스 애플리케이션은 스냅샷 격리로 충분하다. PostgreSQL은 기본 READ COMMITTED이고, 명시적으로 SERIALIZABLE을 설정할 수 있다.

데이터베이스 선택 후 바꾸기는 얼마나 어려운가?

충분히 어렵다. 데이터 마이그레이션 외에도 쿼리 문법(SQL 방언), 인덱싱 전략, 트랜잭션 모델이 다르므로 애플리케이션 코드 수정이 광범위해진다. 따라서 초기 선택이 중요하다. 불확실하면 ① 시뮬레이션(프로토타입)으로 예상 쿼리 패턴 검증, ② 작은 파일럿 프로젝트에서 먼저 검증하기를 권장한다.