데이터베이스
데이터베이스의 기본 개념, 데이터 저장 패턴, 그리고 일반적인 백엔드 데이터베이스 선택지 간의 트레이드오프를 살펴보세요.
데이터베이스가 존재하는 이유
애플리케이션은 하나의 프로그램 실행을 넘어 지속되는 데이터를 저장하고 조회할 수 있는 신뢰할 수 있는 방법이 필요합니다.
앱
데이터베이스
메모리에 생성된 데이터 (임시)
- 데이터베이스는 데이터가 재시작과 장애 이후에도 유지되도록 내구성 있는 저장소를 제공합니다.
- 특정 데이터를 빠르게 가져올 수 있도록 효율적인 질의를 가능하게 합니다.
- 일관성을 보장하고 여러 사용자나 서비스의 동시 접근을 지원합니다.
상세정보
실제 시스템에서 애플리케이션은 사용자 계정, 거래, 메시지, 로그와 같은 데이터를 끊임없이 생성하고 이에 의존합니다. 이 데이터를 애플리케이션 메모리에 직접 저장하는 것은 충분하지 않습니다. 메모리는 임시적이며 프로그램이 종료되거나 충돌하면 사라지기 때문입니다.
데이터베이스는 내구성 있는 저장소를 제공함으로써 이 문제를 해결합니다. 데이터는 재시작과 시스템 장애 이후에도 유지되는 방식으로 디스크에 기록됩니다. 이것이 바로 은행 플랫폼, 소셜 네트워크, 전자상거래 사이트 같은 시스템이 장기적인 상태를 유지할 수 있게 하는 이유입니다.
저장뿐 아니라 데이터베이스는 데이터에 접근하는 구조화된 방법도 제공합니다. 개발자는 파일을 직접 하나씩 훑어보는 대신 쿼리를 사용해 필요한 데이터를 정확히 가져올 수 있습니다. 예를 들어 이메일로 사용자를 조회하거나 지난주 주문을 모두 가져오는 식입니다.
데이터베이스는 또한 일관성 규칙을 강제하여 여러 작업이 동시에 발생하더라도 데이터가 유효한 상태로 유지되도록 합니다. 이것이 없다면 시스템은 동시 접근 상황에서 쉽게 데이터를 손상시킬 수 있습니다.
높은 수준에서 데이터베이스는 시스템의 기준 기록(system of record) 역할을 합니다. 애플리케이션은 요청을 처리하지만, 그 요청이 의존하는 데이터를 저장하고 관리하는 책임은 데이터베이스에 있습니다.
SQL 데이터베이스
SQL 데이터베이스는 데이터를 정의된 스키마와 관계를 가진 구조화된 테이블로 조직하여, 신뢰할 수 있고 일관된 데이터 관리를 가능하게 합니다.
구조화된 테이블은 데이터를 행과 열로 구성합니다
- 데이터는 행과 열로 이루어진 테이블에 저장되어 구조화된 형식을 이룹니다.
- 스키마는 데이터가 정확히 어떻게 저장되어야 하는지 정의하여 일관성을 강제합니다.
- 관계형 쿼리를 통해 여러 테이블의 데이터를 결합할 수 있습니다.
상세정보
SQL 데이터베이스는 데이터가 테이블로 조직되는 관계형 모델을 따릅니다. 각 테이블은 사용자나 주문과 같은 하나의 엔터티를 나타내며, 각 행은 그 엔터티 내의 하나의 레코드를 나타냅니다.
핵심 기능 중 하나는 스키마입니다. 데이터를 저장하기 전에 열의 타입과 제약 조건을 포함한 구조를 정의합니다. 이를 통해 모든 데이터가 일관된 형식을 따르게 되어 오류를 줄이고 무결성을 유지할 수 있습니다.
테이블 간의 관계는 SQL 시스템의 핵심입니다. 예를 들어, 사용자 테이블은 user_id를 통해 주문 테이블과 연결될 수 있으며, 이를 통해 시스템은 관련 데이터를 효율적으로 연결할 수 있습니다.
SQL 데이터베이스는 강력한 쿼리 기능도 지원합니다. 조인, 필터, 집계를 사용하면 개발자는 하나의 쿼리로 여러 테이블에 걸친 복잡한 데이터셋을 가져올 수 있습니다.
이러한 구조화된 접근 방식은 강한 일관성과 신뢰성을 제공하며, 이것이 PostgreSQL과 MySQL 같은 SQL 데이터베이스가 정확성과 데이터 무결성이 중요한 시스템에서 널리 사용되는 이유입니다.
NoSQL 데이터베이스
NoSQL 데이터베이스는 고정된 관계형 구조 대신 다양한 데이터 모델을 허용하여 유연성과 확장성을 우선시합니다.
유연한 스키마 — 레코드가 동일한 구조일 필요는 없습니다
다양한 모델 — 테이블에만 국한되지 않음
직접 조회 — 간단한 key → value 접근
수평 확장 — 데이터가 여러 머신에 분산됨
- NoSQL 데이터베이스는 유연한 스키마를 사용하여, 엄격한 구조 없이도 데이터가 변화할 수 있게 합니다.
- 문서, 키-값, 와이드 컬럼, 그래프와 같은 여러 데이터 모델을 지원합니다.
- 높은 확장성과 대규모 분산 데이터셋 처리를 위해 설계되었습니다.
상세정보
SQL 데이터베이스와 달리, NoSQL 시스템은 미리 정의된 스키마를 요구하지 않습니다. 즉, 각 레코드가 서로 다른 구조를 가질 수 있으므로, 데이터가 비정형이거나 자주 변경될 때 유용합니다.
서로 다른 NoSQL 데이터베이스는 각기 다른 사용 사례에 맞게 최적화되어 있습니다. MongoDB 같은 문서 데이터베이스는 JSON과 유사한 객체를 저장하고, Redis 같은 키-값 저장소는 매우 빠른 조회를 제공합니다. Cassandra 같은 와이드 컬럼 데이터베이스는 대규모 분산 데이터셋을 처리하며, 그래프 데이터베이스는 관계를 직접 모델링합니다.
이러한 유연성 덕분에 NoSQL 시스템은 많은 서버에 걸쳐 수평 확장하기가 더 쉬우며, 이는 대규모 트래픽을 처리하는 대규모 애플리케이션에 매우 중요합니다.
대신 많은 NoSQL 시스템은 성능과 확장성을 위해 엄격한 일관성이나 복잡한 관계형 쿼리를 일부 포기합니다. SQL과 NoSQL 중 무엇을 선택할지는 데이터의 구조와 시스템 요구사항에 따라 달라집니다.
인덱스
인덱스는 데이터베이스가 전체 테이블을 스캔하지 않고도 행을 찾을 수 있게 해 주어 데이터 검색 속도를 높입니다.
- 인덱스가 없으면 데이터베이스는 모든 행을 스캔합니다(full table scan).
- 인덱스가 있으면 데이터베이스는 빠른 조회를 수행해 일치하는 데이터를 찾습니다.
- 인덱스는 읽기 성능을 향상시키지만, 쓰기와 저장 공간에 추가 오버헤드를 만듭니다.
상세정보
쿼리가 인덱스 없이 실행되면, 데이터베이스는 일치하는 결과를 찾기 위해 각 행을 하나씩 확인해야 합니다. 이를 full table scan이라고 하며, 데이터셋이 커질수록 매우 느려집니다.
인덱스는 조회 구조(책의 색인과 비슷함)처럼 동작합니다. 모든 데이터를 훑는 대신, 데이터베이스는 데이터의 위치로 바로 이동할 수 있습니다. 이는 특히 큰 테이블에서 쿼리 시간을 크게 줄여 줍니다.
예를 들어:
CREATE INDEX idx_users_email ON users(email);
이렇게 하면 email 컬럼에 인덱스가 생성되어, "find user by email" 같은 쿼리를 훨씬 빠르게 실행할 수 있습니다.
하지만 인덱스는 공짜가 아닙니다. 데이터가 삽입, 업데이트, 삭제될 때마다 인덱스도 함께 갱신되어야 합니다. 이로 인해 쓰기 작업이 약간 느려지고 저장 공간 사용량이 증가합니다.
효과적인 데이터베이스 설계는 어떤 컬럼에 인덱스를 둘지 신중하게 선택하는 것을 포함합니다. 일반적으로 검색 조건, 필터, 조인에 자주 사용되는 컬럼이 대상입니다.
트랜잭션
트랜잭션은 여러 작업을 하나의 단위로 묶어서, 모든 변경이 성공하거나 아무것도 적용되지 않도록 합니다.
- 트랜잭션은 여러 관련 작업을 하나의 원자적 단위로 처리하도록 보장합니다.
- 모든 단계가 성공하면 변경 사항이 커밋되고, 하나라도 실패하면 모든 내용이 롤백됩니다.
- 복잡한 업데이트 중에 데이터가 일관되지 않게 되는 것을 방지합니다.
상세정보
많은 시스템에서 작업은 서로 독립적이지 않습니다. 예를 들어, 두 계좌 사이에서 돈을 이체하려면 두 잔액을 모두 업데이트해야 합니다. 한쪽 업데이트는 성공하고 다른 쪽은 실패하면 시스템이 일관성을 잃게 됩니다.
트랜잭션은 작업을 함께 묶어서 이 문제를 해결합니다:
BEGIN UPDATE account A UPDATE account B COMMIT
실행 중 어떤 단계라도 실패하면 데이터베이스는 롤백을 수행하여 트랜잭션에서 이전에 이루어진 모든 변경을 되돌립니다.
이렇게 하면 부분 업데이트가 절대 발생하지 않습니다. 전체 작업이 성공적으로 완료되거나, 시스템이 이전 상태로 돌아갑니다.
트랜잭션은 금융 시스템, 재고 추적, 예약 시스템처럼 정확성이 중요한 시스템에서 매우 중요합니다. 트랜잭션이 없으면 실패나 동시성 문제로 인해 데이터가 쉽게 손상될 수 있습니다.
ACID 속성
ACID 속성은 데이터베이스 트랜잭션을 신뢰할 수 있고 안전하게 만드는 보장을 정의합니다.
- 원자성(Atomicity)은 트랜잭션의 모든 작업이 완전히 성공하거나 완전히 실패하도록 보장합니다.
- 일관성(Consistency)은 데이터베이스가 항상 유효한 상태를 유지하도록 보장합니다.
- 격리성(Isolation)과 지속성(Durability)은 트랜잭션이 서로 간섭하지 않으며 커밋된 데이터가 영구적으로 유지되도록 보장합니다.
상세정보
ACID는 관계형 데이터베이스가 트랜잭션 중의 정확성을 보장하기 위해 적용하는 네 가지 속성의 집합입니다.
원자성(Atomicity)은 트랜잭션이 분리 불가능하다는 뜻입니다. 어떤 단계에서든 실패가 발생하면 전체 트랜잭션이 롤백되어 부분 업데이트가 방지됩니다.
일관성(Consistency)은 트랜잭션이 완료된 후 데이터베이스가 제약 조건, 관계, 데이터 유효성 같은 모든 규칙을 준수하도록 보장합니다. 유효하지 않은 상태는 허용되지 않습니다.
격리성(Isolation)은 동시에 실행되는 여러 트랜잭션이 서로 간섭하지 않도록 보장합니다. 동시성 시스템에서도 각 트랜잭션은 마치 혼자 실행되는 것처럼 동작합니다.
지속성(Durability)은 트랜잭션이 커밋되면 시스템이 직후에 충돌하더라도 데이터가 영구적으로 저장됨을 보장합니다.
이러한 속성들은 데이터베이스가 데이터 손상 없이 장애, 동시성, 복잡한 작업을 처리할 수 있게 해줍니다. 이는 정확성이 중요한 신뢰할 수 있는 시스템을 구축하는 데 기본이 됩니다.
쿼리 최적화
데이터베이스는 쿼리를 가장 효율적으로 실행하는 방법을 결정하기 위해 쿼리 최적화를 사용합니다.
- 쿼리 플래너는 쿼리를 분석하고 효율적인 실행 전략을 선택합니다.
- 전체 테이블 스캔 대신 인덱스를 사용하는 것처럼, 데이터를 어떻게 접근할지 결정합니다.
- 최적화는 대규모 데이터셋에서 지연 시간과 리소스 사용량을 줄여줍니다.
상세정보
데이터베이스에 쿼리를 보내면, 이를 단계별로 무작정 실행하지 않습니다. 대신 데이터베이스는 먼저 query planner라는 컴포넌트를 사용해 쿼리를 분석합니다.
플래너는 쿼리를 실행할 수 있는 여러 가능한 방법을 평가하고, 그중 가장 효율적인 방법을 선택합니다. 여기에는 인덱스를 사용할지, 테이블을 어떻게 조인할지, 그리고 작업 순서를 어떻게 할지 같은 결정이 포함됩니다.
예를 들어, 어떤 쿼리가 이메일로 사용자를 검색하고 해당 컬럼에 인덱스가 있다면, 데이터베이스는 모든 행을 스캔하는 대신 인덱스를 사용합니다. 이렇게 하면 대규모 데이터셋에서 실행 시간이 몇 초에서 몇 밀리초로 줄어들 수 있습니다.
이 과정의 결과는 execution plan이며, 이는 쿼리가 내부적으로 정확히 어떻게 실행될지를 정의합니다.
데이터가 증가할수록 쿼리 최적화는 매우 중요해집니다. 최적화가 부족한 쿼리는 느린 응답, 높은 CPU 사용량, 시스템 병목을 유발할 수 있는 반면, 잘 최적화된 쿼리는 시스템을 빠르고 확장 가능하게 유지합니다.
복제
복제는 여러 데이터베이스 인스턴스에 데이터를 복사하여 신뢰성과 확장성을 향상시킵니다.
- 데이터는 기본 데이터베이스에서 하나 이상의 복제본으로 복사됩니다.
- 쓰기 작업은 일반적으로 기본 데이터베이스로 가고, 읽기 작업은 복제본들에 분산될 수 있습니다.
- 복제는 가용성, 장애 허용성, 읽기 성능을 향상시킵니다.
상세정보
복제는 동일한 데이터의 여러 복사본을 서로 다른 데이터베이스 서버에 유지하는 데 사용됩니다. 가장 일반적인 구성은 primary-replica 모델입니다.
이 모델에서 기본 데이터베이스는 모든 쓰기 작업을 처리합니다. 이후 변경 사항은 복제본 데이터베이스로 전파됩니다. 이러한 복제본은 데이터의 복사본을 유지하며 읽기 요청을 처리할 수 있습니다.
이 구성은 여러 가지 이점을 제공합니다. 기본 데이터베이스에 장애가 발생하면 복제본이 역할을 대신할 수 있어 시스템 가용성이 향상됩니다. 또한 여러 복제본에 쿼리를 분산하여 단일 데이터베이스에 과부하가 걸리지 않도록 읽기 트래픽을 확장할 수 있습니다.
하지만 복제는 복잡성을 도입합니다. 데이터가 기본 데이터베이스에 기록된 시점과 복제본에 반영되는 시점 사이에 약간의 지연이 발생할 수 있습니다(replication lag). 즉, 복제본이 항상 가장 최신 데이터를 가지고 있지는 않을 수 있습니다.
이러한 절충점이 있지만, 복제는 확장 가능하고 장애 허용성이 높은 백엔드 시스템을 구축하는 데 핵심적인 기술입니다.
샤딩
샤딩은 단일 시스템에 의존하는 대신 데이터를 여러 서버에 분산하여 데이터베이스를 확장합니다.
- 데이터는 여러 샤드로 분할되며, 각 샤드는 전체 데이터셋의 일부를 저장합니다.
- 샤딩은 더 많은 데이터베이스 서버를 추가하여 수평 확장을 가능하게 합니다.
- 여러 머신에 부하를 분산하여 대규모 데이터셋과 높은 트래픽을 처리합니다.
상세정보
데이터가 증가하면 단일 데이터베이스 서버는 결국 병목이 됩니다. 샤딩은 데이터를 더 작은 조각으로 나누고, 각 샤드를 다른 서버에 저장함으로써 이 문제를 해결합니다.
예를 들면:
Shard 1 → users 1–1M
Shard 2 → users 1M–2M
Shard 3 → users 2M–3M
요청은 접근하는 데이터에 따라 올바른 샤드로 라우팅됩니다. 이를 통해 시스템은 수평으로 확장되고 훨씬 더 큰 워크로드를 처리할 수 있습니다.
대신 복잡성이 추가되며, 특히 여러 샤드의 데이터가 필요한 쿼리에서 그렇습니다.
질문 섹션
1 / 5
이 레슨은 프리미엄 콘텐츠입니다
프리미엄으로 업그레이드하여 흐림 효과를 없애고 전체 내용을 읽어 보세요.