1) 개요
- 냉혹한 현실 세계에서 데이터 시스템은 여러 가지 문제가 생길 수 있다
(1) 데이터베이스 소프트웨어나 하드웨어는 (쓰기 연산이 실행중일때를 포함해서) 언제라도 실패할 수 있다
(2) 애플리케이션은 (연속된 연산이 실행되는 도중도 포함해서) 언제라도 죽을 수 있다
(3) 네트워크가 끊기면 애플리케이션과 데이터베이스의 연결이 갑자기 끊기거나
데이터베이스 노드 사이의 통신이 안될 수 있다
(4) 여러 클라이언트가 동시에 데이터베이스에 쓰기를 실행해서 다른 클라이언트가 쓴 내용을 덮어쓸 수 있다
(5) 클라이언트가 부분적으로만 갱신돼서 비정상적인 데이터를 읽을 수 있다
(6) 클라이언트 사이의 경쟁 조건은 예측하지 못한 버그를 유발할 수 있다
- 시스템이 신뢰성을 지니려면 이런 결함을 처리해서 전체 시스템의 치명적인 장애로 이어지는 것을
막아야 한다.
그러나 내결함성을 갖춘 시스템을 구현하려면 할 일이 많다.
잘못될 수 있는 모든 것에 대해 신중하게 생각해야 하며,
테스트를 여러 번 해서 해결책이 실제로 동작하는지 확인해야 한다.
- 수십년 동안 트랜잭션은 이런 문제를 단순화하는 메커니즘으로 채택돼 왔다
트랜잭션은 애플리케이션에서 몇 개의 읽기와 쓰기를 하나의 논리적 단위로 묶는 방법이다
개념적으로 한 트랜잭션 내의 모든 읽기와 쓰기는 한 연산으로 실행된다.
트랜잭션은 전체가 성공(커밋)하거나 실패(어보트)한다.
트랜잭션이 실패하면 애플리케이션에서 안전하게 재시도할 수 있다.
트랜잭션을 쓰면 애플리케이션에서 오류 처리를 하기가 훨씬 단순해진다.
(원인이 무엇이든 간에) 어떤 연산은 성공하고 어떤 연산은 실패하는 경우처럼
부분적인 실패를 걱정할 필요가 없기 때문이다.
- 트랜잭션을 몇 년 동안 사용해 봤다면 트랜잭션이 뻔해 보일지 모르지만
이를 당연한 것으로 여기면 안된다.
트랜잭션은 자연 법칙이 아니다. 데이터베이스에서 접속하는 애플리케이션에서
프로그래밍 모델을 단순화하려는 목적으로 만든 것이다.
트랜잭션을 사용함으로써 애플리케이션에서 어느 정도의 잠재적인 오류 시나리오와
동시성문제를 무시할 수 있다.
데이터베이스에서 대신 이런 일을 도맡아 주기 때문이다.
(이를 안전성 보장이라고 한다)
- 모든 애플리케이션에서 트랜잭션이 필요하지는 않으며,
때로는 트랜잭션적인 보장을 완화하거나 아예 쓰지 않는 게 이득이다
(예를 들어 성능을 향상시키거나 가용성을 높일 수 있다)
어떤 안전성 속성을 트랜잭션 없이도 보장될 수 있다
- 트랜잭션이 필요한지 어떻게 알수 있을까?
이 질문에 대답하려면 먼저 트랜잭션이 제공하는 안전성 보장에는 어떤 것이 있으며
이와 관련된 비용은 무엇인지 정확히 이해해야 한다.
언뜻 보기에는 트랜잭션이 간단해 보이지만 실제로는 여러 가지 미묘하면서도
중요한 세부 사항이 작용한다.
- 이번 장에서는 문제가 생길 수 있는 여러 예를 조사하고
이런 문제를 방지하기위해 데이터베이스에서 사용하는 알고리즘을 살펴본다.
특히 동시성 제어 분야를 깊게 다루며, 발생할 수 있는 다양한 종류의 경쟁 조건과
데이터베이스에서 커밋 후 읽기, 스냅숏 격리, 직렬성 같은 격리 수준을 어떻게
구현하는지 설명한다.
- 이번 장의 내용은 단일 노드 데이터베이스와 분산 데이터베이스에 모두 적용된다.
분산 시스템에서만 생기는 특정한 난제는 8장에서 집중적으로 살펴본다.
2) 애매모호한 트랜잭션의 개념
- 현대의 거의 모든 관계형 데이터베이스와 일부 비관계형 데이터베이스는
트랜잭션을 지원한다.
이들은 대부분 1975년에 첫 번째 SQL 데이터베이스인 IBM 시스템 R에서 소개된 스타일을 따른다.
구현 세부사항은 좀 바뀌었지만, 일반적인 아이디어는 40년동안 사실상 변함이 없었다.
MySQL, Postgresql, Oracle, SQL 서버 등에서 지원되는 트랜잭션은
놀라울 정도로 시스템 R의 트랜잭션과 유사하다.
- 2000년대 후반에 비관계형(NoSQL) 데이터베이스가 인기를 얻기 시작했다.
이들은 새로운 데이터 모델을 선택할 수 있게 하고,
기본적으로 복제와 파티셔닝 기능을 제공함으로써 관계형 데이터베이스의 현 상황을
개선하는 것을 목표로 했다.
- 트랜잭션은 이 움직임의 주요 피해자였다.
새로운 세대의 데이터베이스 중 다수는 트랜잭션을 완전히 포기하거나
과거에 인식되던 것보다 훨씬 약한 보장을 의미하는 단어로 트랜잭션의 의미를 재정의했다.
- 이렇게 새로 탄생한 분산 데이터베이스가 홍보되면서 트랜잭션은 확장성의 안티테제이며,
어떤 대규모 시스템이라도 높은 성능과 고가용성을 유지하려면 트랜잭션을 포기해야 한다는
믿음이 널리 퍼졌다.
반면, 데이터베이스 벤더에서는 트랜잭션적인 보장은
"값진 데이터"가 있는 "중대한 애플리케이션"에 필수적인 요구사항이라고 주장하곤 한다.
두 관점 모두 완전한 과장이다.
- 진실은 단순하지 않다. 다른 모든 기술적 설계 선택과 마찬가지로 트랜잭션은
이점과 한계가 있다.
이 트레이드오프를 이해하기 위해 정상적인 운영 상황과 다양한 극단적인 환경에서
트랜잭션이 제공하는 보장의 세부 사항을 살펴보자.
3) ACID의 의미
- 트랜잭션이 제공하는 안전성 보장은 흔히 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation),
지속성(Durability)을 의미하는약어인 ACID로 잘 알려져 있다.
1983년, 테오 하더와 안드레아스 로이터는 데이터베이스에서 내결함성 메커니즘을 나타내는
정확한 용어를 확립하기 위해 ACID를 만들었다
- 그러나 현실에서는 데이터베이스마다 ACID 구현이 제각각이다.
예를 들어 곧 보게되겠지만 격리성의 의미 주변에는 모호함이 많이 있다.
상위 수준의 아이디어는 견실하지만 악마는 세부 사항에 있다
오늘날 시스템이 "ACID를 준수(ACID compliant)"한다고 할 때,
그 시스템에서 실제로 어떤 것을 기대할 수 있는지 분명하지 않다.
ACID는 유감스럽게도 거의 마케팅 용어가 돼버렸다
- ACID 표준을 따르지 않는 시스템은 때로 BASE라고 불린다.
기본적으로 가용성을 제공하고(Basically Available),
유연한 상태를 가지며(Soft state),
최종적 일관성(Eventual consistency)을 지닌다는 뜻이다.
ACID의 정의보다 더 모호하다.
BASE의 그럴듯한 정의는 "ACID가 아니다"뿐인 것 같다.
즉, 아무 의미나 갖다 붙일 수 있는 것처럼 보인다.
- 원자성, 일관성, 격리성, 지속성의 정의를 파헤쳐보자.
그러면 트랜잭션에 대한 우리의 생각을 가다듬을 수 있다.
4) 원자성
- 일반적으로 원자적이란 더 작은 부분으로 쪼갤 수 없는 뭔가를 가리킨다.
이 단어는 컴퓨터의 여러 분야에서 비슷하지만 미묘하게 다른 것을 의미한다.
이를테면 다중 스레드 프로그래밍에서 한 스레드가 원자적 연산을 실행한다면
다른 스레드에서 절반만 완료된 연산을 관찰할 수 없다.
시스템은 연산을 실행하기 전이나 실행한 후의 상태에만 있을 수 있으며
그 중간 상태에는 머물 수 없다.
- 반대로 ACID의 맥락에서 보면 원자성은 동시성과 관련이 없다.
원자성은 여러 프로세스가 동시에 같은 데이터에 접근하려고 할 때
무슨 일이 생기는지 설명하지 않는다.
이 문제는 I, 즉 격리성에서 다루기 때문이다.
- 대신 ACID의 원자성은 클라이언트가 쓰기 작업 몇 개를 실행하려고 하는데
그 중 일부만 처리된 후 결함이 생기면
(예를 들어 프로세스가 죽거나 네트워크 연결이 끊기면 혹은 디스크가 가득 차거나
어떤 정합성 제약 조건을 위반하면)
무슨 일이 생기는지 설명한다.
여러 쓰기 작업이 하나의 원자적인 트랜잭션으로 묶여 있는데 결함 때문에 완료(커밋)될 수 없다면
어보트되고 데이터베이스는 이 트랜잭션에서 지금까지 실행한 쓰기를 무시하거나 취소해야 한다.
- 원자성 없이는 여러 변경을 적용하는 도중 오류가 발생하면 어떤 변경은 효과가 있고
어떤 것은 그렇지 않은지 알기 어렵다.
애플리케이션에서 재시도할 수 있지만 동일한 변경이 두 번 실행돼서 중복되거나
잘못된 데이터가 만들어지기 쉽다.
원자성은 이 문제를 단순하게 만들어준다.
트랜잭션이 어보트됐다면 애플리케이션에서 이 트랜잭션이 어떤 것도 변경하지 않았음을 알 수 있으므로
안전하게 재시도할 수 있다.
- 오류가 생겼을 때 트랜잭션을 어보트하고 해당 트랜잭션에서 기록한 모든 내용을 취소하는 능력은
ACID 원자성의 결정적인 특징이다. 아마도 어보트 능력이 원자성보다 나은 단어겠지만,
원자성이 자주 쓰이므로 이 단어를 계속 사용하겠다.
5) 일관성
- 일관성이란 단어는 굉장히 여러 의미로 쓰인다.
ACID 일관성의 아이디어는 항상 진실이어야 하는, 데이터에 관한 어떤 선언이 있다는 것이다.
예를 들어, 회계 시스템에서 모든 계좌에 걸친 대변과 차변은 항상 맞아떨어져야 한다.
트랜잭션이 이런 불변식이 유효한 데이터베이스에서 시작하고,
트랜잭션에서 실행된 모든 쓰기가 유효성을 보존한다면 불변식이 항상 만족된다고 확신할 수 있다.
- 그러나 일관성의 아이디어는 애플리케이션의 불변식 개념에 의존하고,
일관성을 유지하도록 트랜잭션을 올바르게 정의하는 것은 애플리케이션의 책임이다.
이는 데이터베이스가 보장할 수 있는 게 아니다.
데이터베이스는 불변식을 위반하는 잘못된 데이터를 쓰지 못하도록 막을 수 없다.
(데이터베이스에서 확인할 수 있는 특정한 종류의 불변식이 있기는 하다.
예를 들어, 외래 키 제약 조건이나 유일성 제약 조건을 쓸 수 있다.
그러나 일반적으로 애플리케이션에서 데이터가 유효한지 아닌지를 정의하고
데이터베이스는 데이터를 저장할 뿐이다)
- 원자성, 격리성, 지속성은 데이터베이스의 속성인 반면
ACID에서의 일관성은 애플리케이션의 속성이다.
애플리케이션에서 일관성을 달성하기 위해 데이터베이스의 원자성과 격리성 속성에
기댈수는 있지만 데이터베이스만으로 되는 것은 아니다.
따라서 C는 실제로는 ACID에 속하지 않는다.
6) 격리성
- 대부분 동시에 여러 클라이언트에서 데이터베이스에 접속한다.
클라이언트들이 데이터베이스의 다른 부분을 읽고 쓰면 아무 문제가 없지만
동일한 데이터베이스 레코드에 접근하면 동시성 문제(경쟁 조건)에 맞닥뜨리게 된다.
- 그림 7-1은 이런 종류의 문제의 간단한 예다.
데이터베이스에 저장된 카운터를 동시에 증가시키는 클라이언트가 두 개 있다고 하자.
각 클라이언트는 현재 값을 읽어 1을 더한 후 새 값을 다시 기록해야 한다.
(데이터베이스에 내장된 증가 연산이 없다고 할 때)
그림 7-1에서 증가 연산이 두 번 실행됐으므로 카운터는 42에서 44로 증가해야 하지만
실제로는 경쟁 조건 때문에 43이 됐다.
- ACID에서 격리성은 동시에 실행되는 트랜잭션은 서로 격리된다는 것을 의미한다.
트랜잭션은 다른 트랜잭션을 방해할 수 없다.
고전적인 데이터베이스 교과서에서는 격리성을 직렬성이라는 용어로 공식화한다.
직렬성은 각 트랜잭션이 전체 데이터베이스에서 실행되는 유일한 트랜잭션인 것처럼
동작할 수 있다는 것을 의미한다.
데이터베이스는 실제로는 여러 트랜잭션이 동시에 실행됐더라도
트랜잭션이 커밋됐을 때의 결과가 트랜잭션이 순차적으로 실행됐을 때의 결과와 동일하도록 보장한다.
- 그러나 직렬성 격리는 성능 손해를 동반하므로
현실에서는 거의 사용되지 않는다.
오라클 11g 같은 대중적인 데이터베이스 중에는 아예 구현조차 하지 않는 것도 있다.
오라클에는 "직렬성"이라는 격리 수준이 있지만 실제로는 직렬성보다 보장이 약한 스냅숏 쿼리를 구현한 것이다.
스냅숏 격리와 다른 형태의 격리 수준은 "완화된 격리 수준"에서 분석한다.
7) 지속성
- 데이터베이스 시스템의 목적은 데이터를 잃어버릴 염려가 없는 안전한 저장소를 제공하는 것이다.
지속성은 트랜잭션이 성공적으로 커밋됐다면 하드웨어 결함이 발생하거나
데이터베이스가 죽더라도 트랜잭션에서 기록한 모든 데이터는 손실되지 않는다는 보장이다.
- 단일 노드 데이터베이스에서 지속성은 일반적으로 데이터가 하드디스크나 SSD 같은
비휘발성 저장소에 기록됐다는 뜻이다.
보통 디스크에 저장된 데이터 구조가 오염됐을 때 복구할 수 있게 해주는,
쓰기 전 로그나 비슷한 수단을 동반한다.
- 복제 기능이 있는 데이터베이스에서 지속성은 데이터가 성공적으로 다른 노드 몇 개에 복사
됐다는 것을 의미할 수 있다.
지속성을 보장하려면 데이터베이스는 트랜잭션이 성공적으로 커밋했다고 보고하기 전에
쓰기나 복제가 완료될 때까지 기다려야 한다.
- 6장 "신뢰성"에서 설명한 대로 완벽한 지속성은 존재하지 않는다.
모든 하드디스크와 백업이 동시에 파괴돼 버리면 당연히 데이터베이스가
해줄 수 있는 것은 아무것도 없다.
'데이터 중심 애플리케이션 설계' 카테고리의 다른 글
[데이터 중심 애플리케이션 설계] 분산 시스템의 골칫거리 (0) | 2024.08.18 |
---|---|
[데이터 중심 애플리케이션 설계] 관계형 모델과 문서 모델 (0) | 2024.08.18 |
[데이터 중심 애플리케이션 설계] 유지보수성 (2) | 2024.07.24 |
[데이터 중심 애플리케이션 설계] 부하 대응 접근 방식 (0) | 2024.07.24 |