[RDBMS와 NoSQL의 추구미] 돌고 도는 트레이드 오프

2026. 3. 24. 17:13·인프라/AWS
반응형

교내 프로젝트에서 백엔드를 맡으면, 거의 무의식적으로 손이 먼저 움직입니다.

REST API 설계하고, ERD 그리고, MySQL 연결하고. NoSQL을 쓴 발표를 본 기억이 거의 없습니다.. 아마 하나 정도였나? 그리고 저도 마찬가지였습니다.

왜 그랬을까 생각해보면, 솔직히 귀찮아서였습니다. 엔티티로 딱딱 떨어지는 데이터를 테이블에 넣으면 편하고, 기술의 트레이드 오프를 고민할 이유가 없었습니다. 학교 프로젝트에서 트래픽이 터질 일은 없으니까.

그러다 RDBMS를 파다 보면 어느 순간 '동시성 문제'와 마주하게 됩니다. 그리고 솔직히, 학부 수준에서 이걸 제대로 겪기는 어렵다고 생각합니다. Race condition을 억지로 만들어놓고 "나 정합성 확보했어요"라고 발표한 적도 있다. 나도 그랬다. 근데 지금 수준에서 그게 왜 필요하지? 라는 의문은 끊임없이 듭니다.

 

최근에 NoSQL을 다시 들여다보면서 이런 생각이 들었습니다. RDBMS가 느리다면, 그건 정확성을 위해 속도를 포기한 것이다. 그럼 반대로, 정말 빠르게 처리해야 하는 상황에서는 정합성을 어떻게 지키는 걸까? NoSQL은 그 문제를 어떻게 풀고 있을까?

이 글은 그 궁금증에서 시작합니다!

 

철학의 차이를 이해하자

 

RDBS의 핵심 철학은 '틀리느니 차라리 멈춰라'입니다 데이터의 정확성과 일관성이 최우선이고, 그것을 위해서라면 속도나 가용성을 일부 희생하는 선택을 기꺼이 합니다. ACID가 그것의 결정체.

 

AWS가 DynamoDB(앞으로 NoSQL 대신에 Dynamo를 기준으로 서술)를 만든 배경을 짚어봅시다. 2004년 amazon 내부에서 있었던 한 일화로 DynamoDB가 탄생합니다.

전자상거래 플랫폼은 기본 키만으로 쿼리하는 등 단순한 사용 패턴을 사용했음에도 불구하고 관계형 데이터베이스의 한계를 시험하고 있었습니다. 이러한 사용 패턴은 관계형 데이터베이스의 복잡성을 요구하지 않습니다. (중략)

이 데이터베이스 문제에 대한 COE(Correction of Error) 과정에서 당시 20세였던 스와미나탄(스와미) 시바수브라마니안
 (현재 AWS 데이터베이스, 분석 및 머신러닝 조직의 부사장)이라는 젊고 어쩌면 순진했던 인턴이 "왜 이런 작업에 관계형 데이터베이스를 사용하는 거죠? 이런 워크로드에는 SQL 수준의 복잡성과 트랜잭션 보장이 필요하지 않은데요."라고 질문했습니다.

이러한 계기로 아마존은 데이터 저장소 아키텍처를 재고하고 최초의 다이나모 데이터베이스를 구축하게 되었습니다. 

 

[출처] 

https://aws.amazon.com/ko/blogs/aws/happy-birthday-dynamodb/

 

Happy 10th Birthday, DynamoDB! ??? | Amazon Web Services

On January 18th 2012, Jeff and Werner announced the general availability of Amazon DynamoDB, a fully managed flexible NoSQL database service for single-digit millisecond performance at any scale. During the last 10 years, hundreds of thousands of customers

aws.amazon.com

 

 

그래서, DynamoDB의 다른 철학을 한 줄로 요약하면 '느려지느니, 차라리 빠르게 처리하고 나중에 맞춰라'가 됩니다.

DynamoDB는 온디맨드 모드 기준, 초당 수백만개의 요청을 처리할 수 있음과 동시에 트래픽에 따라 자동으로 확장됩니다. RDS의 경우에는 초당 요청 수로 단순 비교를 할 수는 없지만... 인스턴스의 스펙에 종속되어있습니다. 그래서 두 DB의 형태는 '추구미'가 서로 다르다고 할 수 있겠지요.

 

RDBMS의 동시성 추구미: 비관적 잠금 (Pessimistic Locking)

RDBMS는 동시성 문제를 '충돌이 일어날 것이라고 가정하고 미리 막는' 방식으로 처리하는데, 이것이 비관적 잠금입니다.

BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;  -- A 출금
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;  -- B 입금
COMMIT;

 

위 두 쿼리는 반드시 같이 성공하거나 같이 실패해야 합니다.

이렇게 개발자가 직접 명시해줄수도 있고, 쿼리 하나만 날리는 경우에는 자동으로 BEGIN -> 실행 -> COMMIT을 걸어줍니다. (auto commit)

 

RDBMS는 이처럼 비관적 잠금을 기본으로 제공하고, 낙관적 잠금은 개발자가 version 컬럼을 직접 구현하는 방식으로 사용할 수 있습니다.

DynamoDB의 동시성 추구미: 낙관적 잠금 (Optimistic Locking)

DynamoDB는 반대로 '충돌이 거의 없을 것이라고 가정하고, 충돌이 났을 때만 처리하는'방식을 사용합니다.

락이 없으니 처리량은 높지만, 충돌이 잦은 환경에서는 재시도가 폭발적으로 늘어납니다.

낙관적 잠금 전략을 직접 구현해도 되지만, AWS SDK for Java에서는 `@DynamoDBVersionAttribute`를 제공합니다.

DynamoDB가 버전 번호를 할당하고, 이후 업데이트 할때마다 버전 번호가 자동으로 올라갑니다.

 

 

동시성 문제를 아예 피하는 방법은 atmoic counter 이다. 

read-modify-write 사이클을 없애줍니다. 이 사이클은 DB에서 현재 값을 읽어오고, 애플리케이션에서 값을 수정하고, 수정한 값을 다시 DB에 쓰는 과정을 말합니다. 이 사이클 사이의 간격 때문에 동시성 문제가 발생합니다. 이러한 문제는 서버가 여러 대일때 더 두드러집니다.

DynamoDB에서 이를 해결하는 핵심은 '읽기 자체를 안 한다'는 것입니다. read-modify-write 사이클 자체가 없어서, 애플리케이션은 현재 값이 뭔지 모릅니다.. 수정 명령 (ex. 조회수에 1 더해줘) 만 DB에 던져둡니다.

모든 쓰기 요청은 수신된 순서대로 적용됩니다. '현재 값이 얼마인지'를 애플리케이션에서 알 필요가 없고, DB가 순서대로 처리해주기 때문에 충돌이 생길 여지 자체가 없어집니다.

하지만 멱등성이 없습니다. 따라서 네트워크 오류로 애플리케이션이 재시도를 할 경우에는 카운터가 두 번 증가할 수 있습니다.

 

그래서 결론: 조회수처럼 약간의 오차가 괜찮은 값에는 좋지만, 결제 금액처럼 정확해야 하는 값에는 사용해서는 안 된다!

 

[출처] https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.AtomicCounters

 

Working with items and attributes in DynamoDB - Amazon DynamoDB

With GetItem, you must specify the entire primary key, not just part of it. For example, if a table has a composite primary key (partition key and sort key), you must supply a value for the partition key and a value for the sort key.

docs.aws.amazon.com

 

 

 

DynamoDB도 2018년부터 트랜잭션을 지원합니다.

`TransactWriteItems`를 사용하면 여러 item을 하나의 트랜잭션으로 묶을 수 있습니다.

하지만 내부적으로 2-Phase Commit으로 동작합니다. 그래서 일반 쓰기 대비 비용이 2배입니다.

throughput 소모도 2배입니다. DynamoDB에서 트랜잭션은 RDB처럼 쓰라고 만든 게 아니기 때문입니다.

'꼭 필요한 일부 케이스'에만 선택적으로 쓰는 것이 의도된 사용법입니다.

또한 비관적 락도 지원은 하지만, aws docs에 따르면 RDMS처럼 row를 잡아두고 대기시키는 방식이 아니라(`FOR UPDATE`), 충돌 시 트랜잭션 전체를 날리는 방식입니다. (`TransactWriteItems`) 물론 워딩은 '비관적 락'이라고 해놓았긴 합니다..ㅎㅎ

 

[출처] https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/BestPractices_PessimisticLocking.html

 

DynamoDB 트랜잭션을 사용한 비관적 잠금 - Amazon DynamoDB

버전 확인을 조건 표현식으로 추가하여 트랜잭션을 낙관적 잠금과 결합할 수 있습니다. 이는 추가 보호 계층을 제공하지만 트랜잭션이 충돌을 감지하는 데 필요하지 않습니다.

docs.aws.amazon.com

 

 

Eventually Consistency 읽기에서도 트레이드오프가 있습니다. 

기본적으로 DynamoDB는 Eventually Consistent Read입니다.

즉 분산 시스템에서 데이터 업데이터가 즉시 모든 노드에 반영되지 않더라도, 일정 시간이 지나면 결국 모든 복제본이 최종적으로 동일한 데이터를 가지게 되는 일관성 모델입니다. 고가용성을 위해 강한 일관성을 포기하고 일시적인 데이터 불일치를 허용합니다. 그 덕분에 어떤 노드에서든 바로 응답할 수 있으니 빠르고, 노드가 몇 대든 상관없으니 스케일아웃에도 유리하겠지요.

Eventually consistent read는 strongly consistent read의 절반 금액입니다.

 

결국에 돌고도는 트레이드 오프이다.

고가용성을 얻기 위해 복제를 하고➡️ 복제를 하니까 일시적 불일치가 생기고 ➡️ 그 불일치를 허용하는 모델이 eventually consistency...

DynamoDB는 왜 RDS랑 다르게 서버리스? ➡️ 처음부터 그렇게 (분산 환경 전제로) 태어났으니까... ➡️ 왜 분산환경이어야되는데? ➡️ 다시 위로 돌아가... 고가용성을 얻기 위해 복제를 하고... ➡️ ➡️ ➡️ ➡️ ➡️♾️♾️♾️♾️♾️

DynamoDB에게 무슨 질문을 하든 다시 처음으로 돌고 돌아가게 됩니다.

 

그래서 이러한 특성을 어떻게 활용해야 할까?

좋아요, 조회수처럼 약간의 지연이 괜찮은 데이터는 그대로 두는 것이 맞습니다. (물론 인스타 좋아요 수 하나가 절박한 사람은 계속해서 새로고침을 하겠지만...)

반면 재고수량, 잔액처럼 정확성이 중요한 데이터는 Strongly Consistent를 쓰거나, 아예 설계 단계에서 DynamoDB가 적합한지 다시 생각해봐야 합니다. 대신 RDBMS는 단일 인스턴스에서는 트랜잭션으로 동시성을 제어하기 쉽지만, 트래픽이 늘어나 스케일아웃이 필요해지는 순간 Redis 같은 외부 도구로 분산 락을 따로 관리해줘야 하는 복잡도가 생깁니다.

 

그래서 RDBMS가 맞냐, NoSQL이 맞냐는 동시성 문제에서도 한 번 생각해보는 것이 좋습니다.

1. 조금의 불일치는 허용할 수 있는지

2. 그리고 동시성 문제를 앞으로 어떻게 핸들링 해나갈 것인지.

 

그래서 나의 생각 정리.

DynamoDB는 다른 문제를 풀기 위한 트레이드오프를 선택한 도구이다. 

도구의 철학을 이해하면, 트레이드오프가 보이고, 트레이드오프가 보이면 올바른 선택을 할 수 있다.

 

반응형

'인프라 > AWS' 카테고리의 다른 글

2주안에 AWS SAA + DVA 뽀개기  (0) 2026.03.27
AWS Athena 쿼리 성능 최적화하기  (1) 2026.03.16
AWS RDS(MySQL) Migration하기 (📦 새 계정으로 이사가요)  (0) 2025.11.11
Auto Scaling with NLB (Network Load Balancer)  (0) 2025.06.03
'인프라/AWS' 카테고리의 다른 글
  • 2주안에 AWS SAA + DVA 뽀개기
  • AWS Athena 쿼리 성능 최적화하기
  • AWS RDS(MySQL) Migration하기 (📦 새 계정으로 이사가요)
  • Auto Scaling with NLB (Network Load Balancer)
kiritoni
kiritoni
안녕하세요, cool & soft한 엔지니어가 되고싶은 토니입니다!
    반응형
  • kiritoni
    Code Art Online
    kiritoni
  • 전체
    오늘
    어제
    • 분류 전체보기 (38)
      • 이야기 (6)
      • 개발 (10)
        • Java (1)
        • Spring (9)
      • 인프라 (17)
        • AWS (5)
        • Server (12)
      • 공부 (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    network
    구름톤
    server
    웹
    dynamodb
    빅챗
    JPA
    springSecurity
    보안
    후기
    pfsense
    gdgoc
    docker
    Spring boot
    be
    알고리즘
    Spring
    AWS
    구름톤딥다이브
    CS
    백준
    nlb
    AUSG
    springboot
    ubuntu
    java
    backend
    kdt
    Linux
    서버
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
kiritoni
[RDBMS와 NoSQL의 추구미] 돌고 도는 트레이드 오프
상단으로

티스토리툴바