RDBMS와 NoSQL에 대한 고찰

2025. 8. 19. 17:29CS/Database System

RDBMS, NoSQL

1. RDBMS

RDBMS는 데이터를 관계를 통해 관리하는 시스템이다. 여기서 관계는 테이블로 표현되며 2차원 구조이다.

주요 특징

  • Schema-on-Write : 데이터를 저장하기 전 테이블의 구조를 명확히 정의해야 한다.
  • 데이터 무결성 보장 : PK, FK 등의 제약 조건을 통해 데이터의 중복을 방지하고 관계의 유효성을 보장한다.
  • SQL (Structured Query Language)
  • 트랜잭션 (Transaction)과 ACID 원칙

RDBMS는 SQL을 사용할 수 있으며 데이터의 일관성과 신뢰성이 매우 높다. 하지만 그만큼 스키마를 미리 정의하기에 데이터 구조를 변경하기 어렵다. 또한 수평적 확장이 구조적으로 어렵고 JSON, XML 등의 비정형 데이터를 저장하고 처리하기에 비효율적이다.

  • RDBMS는 JOIN과 무결성이 핵심이다. 따라서 수평적 확장을 통해 데이터를 여러 노드에 분산시키게되면 여러 서버에 걸쳐 네트워크 통신이 필요하고 굉장히 큰 부하가 일어나게 된다.

RDBMS에서의 FK

최근 대규모 트래픽을 다루는 서비스에서는 FK를 의도적으로 사용하지 않는 것이 트랜드이다.

FK 제약 조건이 걸려있으면 자식 테이블에 쓰기, 수정, 삭제가 일어날 때마다 참조 무결성 검증이 필요하다. 이 과정은 데이터베이스에 추가적인 부하를 주며, 대량의 쓰기 작업이 발생하는 서비스에서는 성능 저하의 주요 원인이 될 수 있다.

또한 데이터 변경 시 FK로 연결된 테이블 간에 잠금이 발생할 수 있는데, 이는 복잡한 트랜잭션에서 데드락의 원인이 되기도 한다. 샤딩 환경에서는 여러 데이터베이스 인스턴스에 걸쳐 FK 제약 조건을 유지하는 것이 매우 어렵다는 문제점도 존재한다.

MSA 서비스를 생각해보면 서비스당 하나의 데이터베이스가 핵심 원칙 중 하나이다. 이에 FK 유지가 거의 불가능하며 DB 레벨의 FK 대신 애플리케이션 레벨에서 느슨하게 결합된 방식으로 데이터의 관계를 관리한다. 주요 방식은 다음과 같다.

  1. API 호출을 통한 검증
  2. 비정규화 : 주문 서비스에서 orders 테이블에 userId + userName 등을 함께 저장해 서비스 간 의존성을 줄인다.
  3. 이벤트 기반 통신 : 사용자 서비스에서 사용자가 탈퇴하면 UserDeleted 이벤트를 발생시킨다. 주문 서비스는 이 이벤트를 구독하고 있다가, 사용자 정보를 비동기적으로 처리한다. 즉, 모든 데이터가 실시간으로 일치하지는 않지만 결국에는 일관성이 맞춰진다.

이러한 문제점을 바탕으로 FK를 사용하지 않는 것이 트랜드가 되었다. 즉, 데이터베이스 레벨의 무결성 보장을 포기하고 그 책임을 애플리케이션 레벨에서 처리하게 되었다.

  • ORM 활용 : JPA 등을 통해 애플리케이션 코드 수준에서 관계를 관리할 수 있다.
  • 비즈니스 로직에서의 검증
  • Soft Delete : 데이터를 물리적으로 삭제하는 대신 is_deleted 와 같은 값을 true로 변경하는 방식을 사용한다. 이를 통해 부모 데이터가 사라졌을 때 자식 데이터가 Orphan Data가 되는 것을 방지할 수 있다.

그럼에도 FK를 사용하지 않으면 데이터 구조 파악도 어렵고 데이터의 무결성을 보장할 수 없다. 따라서 정답은 없으며 상황에 맞게 선택해야 한다.

  • FK 사용이 중요한 경우
    • 금용, 결제는 데이터 정합성이 매우 중요하다.
    • 내부 관리 시스템은 데이터 변경이 빈번하지 않다.
  • FK 미사용을 고려할 수 있는 경우
    • 초당 수천, 수만 건의 쓰기 요청이 발생하는 대규모 서비스
    • MSA 구조의 시스템

2. NoSQL (Not Only SQL)

NoSQL은 관계형 모델을 사용하지 않는 데이터베이스를 총칭한다. 정형화되지 않은 대용량의 데이터를 유연하고 빠르게 처리하기 위해 탄생했다.

먼저 CAP 이론을 알아야 NoSQL의 탄생을 이해할 수 있다.

CAP 이론

CAP 이론은 분산 데이터베이스 시스템이 Consistency, Availability, Partition Tolerance 중 최대 두 가지의 속성만을 보장할 수 있다는 것을 설명하는 이론이다. 분산 시스템을 설계할 때 어떤 특성을 우선시하고 어떤 것을 희생해야 하는지에 대한 중요한 기능을 제공한다.

먼저 각 속성을 보자.

  1. 일관성 : 모든 노드는 항상 같은 데이터를 보여줘야 한다.
    • 분산된 모든 노드는 동시에 동일한 데이터를 가지고 있어야 한다. 하나의 노드에서 데이터 쓰기가 완료되면 다른 모든 노드에서는 동기적으로 서로의 데이터가 같게끔 작업해야 한다.
  2. 가용성 : 모든 요청에 대해 항상 응답해야 한다.
    • 시스템 일부 노드에 장애가 발생하더라도, 클라이언트의 모든 요청에 대해 시스템은 항상 응답해야 한다.
    • 가용성은 지키고 일관성은 지키지 못하는 경우 서버가 응답하는 데이터가 항상 최신의 데이터라는 보장은 없을 수 있다
  3. Partition Tolerance (분할 용인성) : 네트워크가 끊어져도 시스템은 동작해야 한다.
    • 여러 서버 간 네트워크가 끊기거나 메시지가 유실되더라도 시스템 전체가 멈추지 않아야 한다.
    • 현대의 분산 시스템에서 네트워크 장애는 언제든 발생할 수 있다. 따라서 모든 분산 시스템은 반드시 분할 용인성을 기본으로 가져가야 한다.

CAP의 핵심은 P를 반드시 선택해야 하는 분산 환경에서, 우리는 C와 A 중 하나를 선택해야만 한다는 것이다.

데이터베이스가 네트워크 문제로 A, B 파티션으로 나뉘었다고 생각해보자. 즉 P가 발생한 상황이다. 이때 파티션 A에 새로운 데이터 쓰기 요청이 들어왔다.

  • CP 시스템 (일관성을 선택한 경우)
    • 파티션 A는 B와 통신할 수 없으므로 새로운 데이터를 B에 즉시 전달하여 데이터를 일치시킬 수 없다. 하지만 일관성을 선택했기에, 즉 동기적으로 동작하기에 A는 B와 연결이 복구될 때까지 새로운 쓰기 요청을 거부하거나 응답하지 않아야 한다.
    • 즉, C를 지키기 위해 A를 희생할 수 밖에 없는 것이다.
    • MongoDB, 전통적인 RDBMS가 여기에 속한다.
  • AP 시스템 (가용성을 선택한 경우)
    • 파티션 A는 일단 쓰기 요청을 받아 처리하고 응답한다. B 또한 자신의 버전으로 요청에 계속 응답한다.
    • 따라서 A, B는 서로 다른 데이터를 가지며 일관성을 지킬 수 없다.
    • 나중에 네트워크가 복구되면 서로 다른 데이터를 동기화하는 과정을 거쳐 최종적으로 일관성을 맞추게 된다. 이를 Eventual Consistency라 한다.

이제 우리는 NoSQL을 이해할 준비가 되었다. CAP 이론에 따르면, P를 항상 가져가야 하는 입장에서 우리는 일관성과 가용성 중 하나를 포기해야만 한다. 하지만 RDBMS는 일관성과 가용성을 최우선으로 설계되었다. 단일 서버 환경을 상정하고 만들어진 모델이기 때문이다.

하지만 분산 환경에서 P는 포기할 수 없고, RDBMS의 무수한 무결성들 사이에서 C 또한 포기할 수 없다. 즉, CP 시스템을 선택할 수 밖에 없다.

하지만 NoSQL은 대규모 분산 환경을 전제로 탄생했기에 P를 기본으로 깔고 간다. 또한 서비스가 멈추지 않는 것을 더 우선시하기에 AP 시스템으로 설계된 경우가 많다.

이것이 NoSQL과 RDBMS의 핵심적인 차이이다.

NoSQL의 특징

  • Schema-on-Read : RDBMS와 달리 고정된 스키마가 없이 데이터를 읽어올 때 스키마가 해석된다.
  • 수평적 확장에 용이
  • 다양한 데이터 모델
    • Key-Value : Redis, …
    • Document Store : JSON, XML 등의 문서 저장. MongoDB, …
    • Column-Family Store : 행이 아닌 열을 기준으로 데이터를 저장
    • Graph Store : 데이터와 그 관계를 노드, 엣지, 속성으로 표현
  • BASE 속성: ACID 대신 BASE(Basically Available, Soft state, Eventually consistent) 속성을 따르는 경우가 많다. NoSQL은 AP 모델이기 많기 때문이다.

BASE 속성

BASE 속성은 ACID 원칙과 정반대에 서 있는 개념으로 NoSQL이 분산 환경에 적합함을 보이는 속성이다.즉, 가용성과 성능을 최우선으로 확보하기 위한 설계 철학이다.

  1. Basically Available (기본적인 가용성)
  • 시스템은 항상 사용할 수 있어야 한다.
  • 분산 시스템의 일부 노드에 장애가 발성하더라도 전체 시스템은 절대로 멈추어서는 안된다. 일관성을 져버리더라도 즉, 잘못된 응답을 주더라도 가용성을 최우선으로 해야 한다.
  1. Soft State (소프트 상태 / 유연한 상태)
  • 시스템의 상태는 언제든지 변할 수 있다.
  • 분산된 여러 노드들의 데이터 상태는 외부의 새로운 요청이 없더라도 동기화 과정에서 스스로 상태가 변할 수 있다.
  • ACID에서는 한 번 Commit 된 데이터는 다른 트랜잭션에 의해 변경되기 전까지 그 상태가 영구적으로 유지된다.
  1. Eventually Consistent (결과적 일관성)
  • 결국에는 데이터가 일관성을 갖게 된다.
  • 사용자 요청 처리 이후 여러 노드 간 데이터가 일시적으로 일치하지 않을 수 있지만 충분한 시간이 지나면 결국 모든 노드의 데이터가 같은 상태로 동기화되어 일관성을 가지게 된다.

NoSQL의 문제점

단점 또한 명확하다. 아직 표준화된 쿼리 언어가 없고 트랜잭션 기능이 제한적인 경우가 많다. 일관성도 많이 부족하다. 자세히 보자.

  1. 데이터 일관성의 문제 (Eventually Consistent): Eventual Consistency 특성 때문에 일시적 데이터 불일치를 허용한다. 따라서 금융 거래와 같은 경우 절대로 NoSQL을 사용해서는 안된다.
  2. 데이터 중복과 업데이트의 어려움: 스키마가 없고 JOIN 또한 없기에 데이터의 중복을 야기할 수 있다. 따라서 업데이트 비용이 생각보다 더 크게 들 수 있다.
  3. 복잡한 쿼리의 한계: JOIN 연산이 없거나 비효율적이기 때문에, 여러 컬렉션에 걸친 복잡한 데이터 분석을 작성하기 어렵다. 이는 애플리케이션 레벨에서의 복잡한 로직 추가를 야기한다.

결과적 일관성과 UX

'품절된 상품 주문'과 같은 문제를 해결하기 위한 많은 패턴이 존재한다.

  1. 보상 트랜잭션 (Compensating Transaction) / Saga 패턴
    • 일단 사용자의 주문을 낙관적으로 받아들여 주문이 성공한 것으로 처맇나다.
    • 이후 재고 차감 이벤트 처리 시 재고가 부족하다는 것이 확인되면, 보상 이벤트를 발생시킨다.
    • 이 보상 이벤트는 '주문 자동 취소', '고객에게 사과 알림 및 쿠폰 발송', '결제 자동 환불' 등의 반대되는 보상 조치를 트리거한다.
    • 사용자 입장에서는 주문이 취소되는 불쾌한 경험을 하지만, 적절한 보상(쿠폰 등)을 통해 브랜드 이미지를 관리할 수 있다.
  2. UX/UI를 통한 기대치 관리
    • 버튼 문구 변경: "즉시 구매"가 아닌 "주문하기" 또는 "결제 진행" 등의 문구를 사용하여, 클릭 즉시 모든 것이 확정되는 것이 아님을 암시한다.
    • 중간 상태 안내: 주문 요청 후 "주문이 완료되었습니다"라고 바로 보여주는 대신, "주문 처리 중입니다. 재고를 확인하고 있습니다..." 와 같은 중간 상태를 명확히 보여준다.
    • 결과 알림: 최종적인 주문 성공 여부는 별도의 알림(푸시, 이메일)으로 알려주어, 사용자가 화면 앞에서 계속 기다리지 않도록 한다.
  3. 예약(Reservation) 또는 임대(Lease) 패턴
    • '주문' 버튼을 누르면 재고를 즉시 차감하는 대신, 해당 상품에 대해 짧은 시간(예: 10분) 동안 유효한 '예약'을 건다.
    • 이 예약 정보는 Redis와 같이 빠른 인메모리 저장소에 기록한다.
    • 사용자는 10분 안에 결제를 완료해야만 최종적으로 주문이 확정되고, 시간이 지나면 예약은 자동으로 해제되어 다른 사용자가 구매할 수 있게 된다.
    • 여기서 중요한 점은 사용자가 결제창을 지나는 모든 시간을 거친 뒤 최종 결제 버튼을 누른다는 것이다. 이때 품절 통보를 받게되면 사용자 경험 하락, 그렇다고 그 전에 트랜잭션으로 가두게되면 심각한 비효율이 발생한다. 이를 회피하는 것이 임대 패턴이다.

3. MongoDB

RDBMS (관계형 데이터베이스) 방식

RDBMS에서는 정규화 원칙에 따라 데이터를 별도의 테이블로 분리하여 저장한다. 따라서 'kim'이라는 사용자가 작성한 모든 글을 가져오려면, users 테이블과 posts 테이블을 user_id로 JOIN해야 한다.

MongoDB에서는 관련된 데이터를 하나의 문서(Document) 안에 중첩된(nested) 형태로 함께 저장할 수 있다.

{
  "_id": 1,
  "username": "kim",
  "email": "kim@example.com",
  "posts": [
    {
      "post_id": 101,
      "title": "첫 번째 글",
      "content": "안녕하세요."
    },
    {
      "post_id": 102,
      "title": "두 번째 글",
      "content": "반갑습니다."
    }
  ]
}
{
  "_id": 2,
  "username": "lee",
  "email": "lee@example.com",
  "posts": [
    {
      "post_id": 103,
      "title": "이 대리의 글",
      "content": "안녕하세요!"
    }
  ]
}

이 방식은 'kim' 사용자의 정보를 조회할 때, 별도의 JOIN 없이 하나의 문서를 읽는 것만으로 사용자의 모든 게시글 정보까지 한 번에 가져올 수 있어 매우 빠르고 효율적이다.


4. Redis (Remote Dictionary Server)

Redis는 In-Memory 데이터베이스이나 Key-Value 데이터 스토어이다. 모든 데이터를 메모리에 저장하여 압도적으로 빠른 속도를 자랑한다.

Redis의 주요 특징

  • 매우 빠른 속도: 데이터를 메모리에 저장하기 때문에 디스크 기반의 데이터베이스와는 비교할 수 없을 정도로 빠르다. 이 특징 때문에 주로 캐시(Cache) 서버로 가장 많이 활용된다.
  • 다양한 자료구조 지원: 단순한 Key-Value를 넘어 Strings, Lists, Sets, Sorted Sets, Hashes 등 다양한 자료구조를 지원한다. 이는 개발자가 애플리케이션의 요구사항에 맞춰 데이터를 더 효율적으로 관리할 수 있게 해준다.
  • 영속성(Persistence) 지원: 인메모리 데이터베이스는 서버가 다운되면 데이터가 사라지는 단점이 있지만, Redis는 스냅샷(RDB)과 AOF(Append Only File) 방식을 통해 데이터를 디스크에 저장하여 영속성을 확보할 수 있다.
  • 싱글 스레드(Single-Threaded): Redis는 기본적으로 싱글 스레드로 동작한다. 이로 인해 Race Condition을 피할 수 있다.
  • 다양한 활용성: 캐싱 외에도 실시간 랭킹 시스템, 세션 관리, 메시지 큐(Pub/Sub 기능 활용), 분산 락(Distributed Lock) 등 다방면에서 활용된다.

Redis 사용 시의 단점 및 주의사항

  1. 메모리 비용과 한계: 모든 데이터를 RAM에 저장하므로, 저장할 데이터의 양이 많아질수록 상당한 메모리 비용이 발생한다.
  2. 데이터 유실 가능성: 영속성 기능을 제공하지만, 기본적으로는 휘발성 메모리를 사용한다. 스냅샷(RDB) 방식은 특정 시점 사이에 발생한 데이터 변경 사항이 유실될 수 있고, AOF 방식은 모든 쓰기 명령을 기록하므로 파일 크기가 커지고 복구 속도가 느려질 수 있다.
  3. 싱글 스레드의 한계: 싱글 스레드는 동시성 제어에 유리하지만, 반대로 말하면 한 번에 하나의 명령만 처리할 수 있다는 의미이다. 따라서 큰 자원을 잡아먹는 작업으로 인한 병목이 생길 수 있다.
  4. 복잡한 쿼리 불가능: Redis는 특정 Key에 대한 빠른 접근에 특화되어 있어, RDBMS처럼 데이터의 내용(Value)을 조건으로 검색하거나 복잡한 집계 연산을 수행하는 데는 적합하지 않다.