모던지 / 컴퓨터과학 / 데이터의 시대 1강. 데이터를 잃지 않고 보관하는 기술

데이터의 시대 1강. 데이터를 잃지 않고 보관하는 기술

이체 도중에 서버가 꺼져도 돈이 증발하지 않는 이유는, 누군가 "전부 아니면 전무"를 규칙으로 못 박아 두었기 때문입니다.

풀고 시작

문제 1. 이체 도중 서버가 꺼졌는데 출금만 기록되고 입금은 빠진 상태가 남지 않았습니다. 이를 보장하는 성질은 무엇일까요?
원자성은 여러 작업을 쪼갤 수 없는 한 덩어리로 묶어, 중간에 실패하면 이미 한 일까지 되돌립니다. 인덱스는 조회 속도를 위한 장치이고 정규화는 중복을 없애는 설계 원칙이라 장애 복구와는 다른 층의 문제이며, SQL은 절차형이 아니라 선언형 언어입니다.

파일 하나로 회사를 굴려 본 사람들

1960년대 기업 전산실의 풍경을 상상해 보시죠. 영업부는 영업부 파일에, 배송부는 배송부 파일에 고객 주소를 각각 적어 둡니다. 고객이 이사를 갑니다. 영업부는 고쳤고 배송부는 잊었습니다. 그 순간 회사에는 서로 다른 두 개의 진실이 생깁니다. 이것이 데이터 불일치이고, 원인은 같은 사실이 여러 곳에 중복 저장되어 있다는 데 있습니다.

문제는 더 있었습니다. 두 프로그램이 같은 파일에 동시에 쓰면 나중에 쓴 쪽이 앞선 수정을 통째로 덮어썼습니다. 파일 안에 항목 하나만 추가해도 그 파일을 읽던 모든 프로그램을 다시 고쳐야 했습니다. 저장 방식과 응용 프로그램이 한 몸으로 붙어 있었던 것이죠. 데이터베이스 관리 시스템(DBMS)은 바로 이 접착을 떼어 내려고 등장했습니다. 데이터를 어떤 파일에 어떤 순서로 담을지는 시스템이 책임지고, 프로그램은 무엇이 필요한지만 말하게 하자는 발상입니다.

코드가 표 한 장으로 뒤집은 것

1970년 IBM 산호세 연구소의 에드거 코드(Edgar F. Codd)가 발표한 논문이 판을 갈았습니다. 그의 관계 모형(relational model)은 데이터를 표로 봅니다. 표 하나가 하나의 관계이고, 행 하나가 하나의 사실입니다. 놀라운 점은 저장 구조가 아니라 논리 구조만 남겼다는 데 있습니다. 사용자는 데이터가 디스크 어디에 어떤 포인터로 연결돼 있는지 알 필요가 없어졌습니다.

여기서 정규화(normalization)가 따라옵니다. 흔히 저장 공간을 아끼는 기법으로 오해받지만 진짜 목적은 따로 있습니다. 한 사실을 오직 한 곳에만 적어 두어 갱신 이상을 막는 것입니다. 주문 표 안에 고객 주소를 같이 적어 두면 주소를 바꿀 때 수백 행을 전부 고쳐야 하고, 하나라도 빠뜨리면 회사에 다시 두 개의 진실이 생깁니다. 고객 표와 주문 표를 나누고 고객 번호로 잇는 순간 그 위험이 사라집니다.

무엇을 원하는지만 적는 언어

관계 모형 위에서 태어난 질의 언어가 SQL입니다. IBM의 시스템 R 프로젝트에서 만들어졌고 처음 이름은 SEQUEL이었습니다. SQL의 결정적 특징은 선언형이라는 점입니다. "지난달 매출 상위 열 개 지점을 다오"라고 원하는 결과를 적을 뿐, 어느 표를 먼저 훑고 어떤 순서로 합칠지는 쓰지 않습니다.

그럼 그 방법은 누가 정할까요. 질의 최적화기(query optimizer)가 정합니다. 최적화기는 표의 크기, 값의 분포 같은 통계를 보고 실행 계획을 여러 개 만든 뒤 비용이 가장 싼 것을 고릅니다. 그래서 똑같은 SQL 문장이 데이터가 불어나면 전혀 다른 경로로 실행되기도 합니다. 사람이 손으로 짠 코드가 아니라 시스템이 매번 다시 계산한 전략인 셈이죠.

ACID: 실패를 전제로 설계하기

트랜잭션(transaction)은 쪼갤 수 없는 작업 묶음입니다. 이 묶음이 지켜야 할 성질을 네 글자로 부르는 것이 ACID입니다. 원자성은 전부 반영하거나 전부 취소한다는 약속이고, 일관성은 트랜잭션 전후로 정해 둔 규칙(잔액은 음수가 될 수 없다 같은)이 깨지지 않는다는 뜻입니다. 격리성은 여러 트랜잭션이 동시에 돌아도 결과가 하나씩 차례로 실행한 것과 같아야 한다는 요구이고, 지속성은 커밋한 뒤에는 전원이 나가도 살아남는다는 보장입니다.

지속성은 마법이 아닙니다. 데이터를 고치기 전에 "이렇게 고치겠다"는 기록을 먼저 로그에 안전하게 적어 두는 선행 기록 로그 방식이 대표적 구현입니다. 재시동하면 로그를 읽어 끝내지 못한 일을 되돌리거나 마저 끝내죠. 격리성은 대가가 비싸서 현실에서는 수준을 낮춰 쓰는 경우가 많고, 그 대신 어떤 이상 현상을 감수할지 개발자가 알고 선택해야 합니다.

인덱스의 속도, 그리고 청구서

조회가 느릴 때 가장 먼저 꺼내는 카드가 인덱스입니다. 책 뒤 색인에 비유하지만, 실제로는 B트리 계열의 정렬된 자료구조를 따로 만들어 두는 것입니다. 백만 행을 전부 읽는 대신 트리를 몇 단계만 내려가면 되니 탐색량이 로그 규모로 줄어듭니다.

대가도 분명합니다. 인덱스는 저장 공간을 더 먹고, 행을 넣거나 고칠 때마다 인덱스도 함께 갱신해야 하므로 쓰기가 느려집니다. 인덱스를 많이 걸수록 읽기는 빨라지고 쓰기는 무거워지는 교환이죠.

NoSQL 계열은 아예 다른 지점에서 타협했습니다. 데이터가 서버 한 대에 안 들어가면 여러 대로 쪼개야 하는데, 쪼갠 상태에서 표를 자유롭게 합치고 강한 일관성을 지키기가 아주 비쌉니다. 그래서 복잡한 조인과 즉각적 일관성을 일부 포기하고 수평 확장을 택했습니다. 여기서 자주 인용되는 CAP 정리(CAP theorem)는 "셋 중 둘만 고른다"보다 이렇게 읽는 편이 정확합니다. 서버 사이 통신이 끊긴 상황에서 옛 값을 돌려주더라도 응답할지, 아니면 응답을 거부하고 일관성을 지킬지 중 하나를 골라야 한다는 뜻입니다. 최근에는 분산 환경에서도 트랜잭션을 제공하는 시스템이 늘어 경계가 흐려지는 중입니다. 다음 강에서는 이렇게 쌓인 데이터가 한 대의 처리 능력을 넘어설 때 무슨 일이 벌어지는지 보겠습니다.

인출 문제

문제 1. 주문 표 안에 고객 주소를 함께 적어 두는 설계가 위험한 이유는 무엇일까요?
정규화의 목적은 공간 절약이 아니라 한 사실을 한 곳에만 두어 갱신 이상을 막는 것입니다. 중복이 있으면 수정이 누락된 행이 남아 서로 모순된 데이터가 공존하게 되죠. 나머지 보기는 문법이나 자료형의 문제로 원인을 잘못 짚었습니다.
문제 2. SQL을 선언형 언어라고 부르는 근거는 무엇일까요?
사용자는 무엇을 원하는지만 기술하고 방법은 질의 최적화기가 통계를 보고 계획을 세워 결정합니다. 그래서 같은 문장도 데이터 규모가 바뀌면 다른 실행 경로로 처리되죠. 실행 순서를 사람이 지정하는 방식은 반대편인 절차형의 특징입니다.
문제 3. ACID의 격리성이 요구하는 바를 가장 정확히 말한 것은 무엇일까요?
격리성은 동시 실행의 결과가 순차 실행과 구별되지 않아야 한다는 조건입니다. 되돌리기는 원자성, 전원이 꺼져도 남는 것은 지속성, 규칙 유지는 일관성이라 각각 다른 글자에 해당하죠. 격리성은 비용이 커서 실무에서는 수준을 낮춰 쓰기도 합니다.
문제 4. 인덱스를 늘릴 때 함께 커지는 비용은 무엇일까요?
인덱스는 정렬된 자료구조를 따로 유지하는 장치라서 데이터가 바뀔 때마다 함께 손봐야 합니다. 읽기 속도를 쓰기 속도와 저장 공간으로 사는 교환이죠. 인덱스는 결과의 정확도나 트랜잭션 성질과는 무관합니다.

생각해볼 질문 (정답 없음)

  • 서비스가 잠깐 옛날 값을 보여 주는 것과 아예 응답하지 않는 것 중, 여러분이 쓰는 서비스들은 각각 어느 쪽을 골랐을까요?

이전: 네트워크와 인터넷 4강 · 다음: 데이터의 시대 2강

모던지 · 궁금하면 모던지 GitHub · 2026-09-10