LabHub
배우기 러닝패스 코스

Database Concepts

NoSQL — The Schema Did Not Vanish; the Responsibility Moved

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

NoSQL 은 관계형의 상위 호환이 아니라 특정 제약을 포기하고 특정 능력을 얻는 거래이며, 무엇을 포기했는지 모르고 고르면 그 대가를 나중에 치른다.

Concept map: 수평 확장 · 어떤 질의로 읽을 것인가. · 데이터가 얼마나 커지는가. · 함께 바뀌어야 하는 것이 무엇인가.

왜 이게 필요했나

관계형 데이터베이스는 강력하지만 몇 가지를 전제한다. 스키마가 미리 정해져 있고, 데이터가 한 대에 들어가거나 적어도 조인이 가능한 범위 안에 있으며, 강한 일관성이 필요하다는 전제다.

2000년대 후반 웹 서비스들이 이 전제를 하나씩 못 지키게 되면서 다른 선택지가 나왔다. 핵심 동기는 수평 확장이었다. 한 대를 더 크게 만드는 대신 여러 대에 나누려면 조인과 트랜잭션 같은 기능이 걸림돌이 되었고, 그것을 포기한 설계들이 등장했다.

어떻게 동작하나

부류별 성격은 이렇게 갈린다.

부류 데이터 모델 강점 대가
키값 키 하나에 값 하나 단순 조회가 매우 빠름 값 내부로 질의 불가
문서 JSON 유사 문서 스키마 유연, 한 문서에 다 담아 조회 문서 간 조인과 정합성은 앱 책임
와이드 컬럼 행 키 + 컬럼 패밀리 대량 쓰기, 시계열 질의 패턴을 미리 정해 설계해야 함
그래프 노드와 간선 관계 탐색 대량 집계는 약함

여기서 가장 널리 퍼진 오해를 짚어야 한다. "스키마가 없다"는 말은 스키마가 사라졌다는 뜻이 아니라 스키마를 강제하는 주체가 데이터베이스에서 애플리케이션으로 옮겨 갔다는 뜻이다. 필드 이름을 오타 낸 문서가 조용히 들어가고, 몇 달 뒤 그 필드를 읽는 코드가 조용히 실패한다. 데이터베이스가 막아 주던 것을 이제 코드 리뷰와 테스트가 막아야 한다.

CAP 도 자주 오용된다. 네트워크 분단이 실제로 일어난 순간에 일관성과 가용성 중 무엇을 고를지의 문제이지, 평상시에 셋 중 둘을 고르는 이야기가 아니다. 분단이 없을 때는 대부분의 시스템이 둘 다 제공한다.

현장에서 만나는 모습

선택의 기준은 유행이 아니라 질의 패턴이다. 몇 가지 실용적인 신호가 있다.

그리고 현대 관계형 데이터베이스가 상당 부분을 흡수했다는 점도 감안해야 한다. PostgreSQL 의 jsonb 는 문서 저장소의 유연성을 상당히 제공하면서 트랜잭션과 조인을 유지한다. 다만 남용하면 안 된다. 조회하거나 정렬하거나 제약이 필요한 필드는 컬럼으로 승격하고 나머지 원본만 jsonb 에 남기는 것이 실용적인 경계다. jsonb 안의 값에는 정확한 통계가 없어 플래너의 행 수 추정이 어긋나고, 그 결과 조인 방식 선택이 틀어지기 때문이다.

고르기 전에 답해야 할 다섯 질문

부류별 성격을 외우는 것보다, 지금 만들려는 것에 대해 다음 다섯 가지를 먼저 적어 보는 편이 훨씬 빨리 답에 닿는다.

  1. 어떤 질의로 읽을 것인가. 키 하나로 찾는가, 조건으로 거르는가, 관계를 따라가는가. 문서 저장소와 와이드 컬럼은 저장할 때 정해 둔 접근 경로로만 잘 읽히므로, 이 답이 "아직 모른다" 라면 관계형이 안전하다.
  2. 데이터가 얼마나 커지는가. 한 대에 들어갈 크기라면 분산의 대가를 치를 이유가 없다. 요즘 서버 한 대는 수 테라바이트를 다룬다.
  3. 함께 바뀌어야 하는 것이 무엇인가. 이 목록이 길면 트랜잭션이 필요하다는 뜻이다.
  4. 옛 값을 잠시 보여 줘도 되는가. 조회 수나 추천 목록은 괜찮지만 잔액과 재고는 안 된다.
  5. 누가 운영하는가. 새 저장소는 새 백업 절차, 새 지표, 새 장애 유형을 함께 데려온다.

다섯 질문에 답해 보면 대부분의 경우 결론이 하나로 모입니다. 처음에는 관계형 하나로 시작하고, 특정 질의 패턴이 명확히 아파지면 그 부분만 다른 저장소로 떼어 내는 것입니다. 세션은 키값 저장소로, 전문 검색은 검색 엔진으로 옮기는 식입니다. 반대 순서, 즉 처음부터 여러 저장소를 깔아 두고 시작하는 방식은 아직 존재하지도 않는 문제에 운영 부담부터 지불하는 선택입니다.

분산이 되는 순간 달라지는 것들

한 대에서 돌던 데이터베이스가 여러 대로 나뉘면, 익숙하던 성질 몇 가지가 조용히 사라진다. 이것을 모르고 옮기면 코드는 그대로인데 결과만 이상해진다.

첫째, 쓴 값을 바로 못 읽을 수 있다. 복제본이 여러 개인 저장소에서 쓰기는 한쪽에, 읽기는 다른 쪽에 갈 수 있다. 프로필을 저장하고 곧바로 조회하는 화면이 옛 값을 보여 주는 사고가 여기서 나온다. 대부분의 저장소는 "방금 내가 쓴 것은 내가 읽을 수 있다" 를 옵션으로 제공하므로, 그 옵션을 켜거나 쓰기 직후의 읽기만 주 복제본으로 보내면 된다. 옵션 이름은 제품마다 다르지만 대가는 같다. 그 읽기는 느려진다.

둘째, 여러 문서를 한 번에 바꾸는 일이 어려워진다. 관계형에서는 트랜잭션 하나로 끝나던 작업이, 문서가 서로 다른 노드에 흩어져 있으면 부분적으로만 성공할 수 있다. 그래서 문서 저장소는 함께 바뀌어야 하는 것은 같은 문서에 담는다 는 원칙으로 설계한다. 주문과 주문 항목을 한 문서에 넣는 이유가 성능이 아니라 원자성이라는 점이 중요하다.

셋째, 샤드 키를 나중에 바꿀 수 없다. 데이터를 어느 노드로 보낼지 정하는 키는 사실상 되돌릴 수 없는 결정이다. 잘못 고르면 특정 노드에만 부하가 몰리고(핫 샤드), 그 노드를 늘려도 해결되지 않는다. 대표적인 실패는 시각을 샤드 키로 쓰는 것이다. 최근 데이터가 전부 한 노드로 몰려서, 노드를 열 대로 늘려도 아홉 대가 논다.

정리하면 이렇습니다. 관계형에서 NoSQL 로 옮기는 결정은 저장소를 고르는 일이 아니라, 데이터베이스가 대신 지켜 주던 규칙 중 무엇을 애플리케이션이 직접 지킬지 정하는 일입니다. 그 목록을 적어 보지 않고 옮기면, 옮긴 뒤에 하나씩 사고로 발견하게 됩니다.

이어지는 퀴즈에서 확인할 것

각 부류가 무엇을 포기하고 무엇을 얻는지, 그리고 "스키마 없음"이 실제로 무엇을 뜻하는지 설명할 수 있는지 확인한다.