키-값 저장소
Key-value Store
도구
열쇠 하나에 값 하나를 매달아 두고 그 열쇠로만 꺼내 쓰는 데이터베이스로, 구조가 단순한 만큼 읽고 쓰기가 빠릅니다.
등장 배경
데이터를 담는 자리는 오랫동안 하나였습니다. 행과 열이 있는 표에 담고 표끼리 이어 두는 관계형 데이터베이스가 업무 시스템의 기본값이었고, 회원 정보도 주문 기록도 다 거기 들어갔습니다.
그 한 자리에 성격이 아주 다른 일이 섞이면서 문제가 생겼습니다. 페이지덱의 문서 목록 화면은 열릴 때마다 "지금 요청을 보낸 사람이 로그인 상태가 맞는지" 를 확인해야 했습니다. 답은 언제나 짧습니다. 이 열쇠에 해당하는 사람이 누구인지 하나만 알면 됩니다. 그런데 그 한 줄을 꺼내려고 매번 관계형 데이터베이스를 두드렸고, 사용자가 늘자 이 조회가 전체 요청의 대부분을 차지했습니다.
태오가 확인해 보니 문제는 데이터의 양이 아니라 절차였습니다. 관계형 데이터베이스는 질의 문장을 해석하고 어느 표를 어떤 순서로 읽을지 계획을 세운 뒤에야 답을 꺼냅니다. 정교한 질문에는 값진 과정이지만, 열쇠를 이미 알고 있는 질문에는 대부분이 낭비입니다.
키-값 저장소는 그 낭비를 걷어낸 쪽입니다. 질문을 한 가지로 제한합니다. "이 열쇠에 매달린 값을 달라." 그 대신 답을 아주 빨리 줍니다.
해결하는 문제
이미 열쇠를 알고 있는 조회를 값싸게 만듭니다. 관계형 데이터베이스에서 수 밀리초 걸리던 한 건 조회가 여기서는 대개 1밀리초 아래에서 끝납니다. 사람 눈에는 둘 다 순식간이지만, 화면 하나가 이런 조회를 스무 번 하고 그 화면이 초당 수백 번 열리면 차이가 서비스 전체의 여유로 나타납니다.
빠른 이유는 셋입니다.
- 찾는 방법이 하나뿐입니다. 열쇠를 계산해 값이 놓인 자리를 바로 짚습니다. 조건을 해석하거나 인덱스라는 색인을 뒤질 필요가 없습니다.
- 대개 메모리에 둡니다. 디스크까지 내려가지 않으니 물리적으로 더 빠릅니다. 대신 서버가 재시작하면 내용이 사라질 수 있어서, 사라져도 되는 것만 올립니다.
- 값마다 만료 시간을 걸 수 있습니다. 30분 뒤에 스스로 지워지게 해 두면 오래된 것을 치우는 코드를 따로 두지 않아도 됩니다.
그래서 들어가는 것은 대체로 정해져 있습니다.
대안
고를 때 묻는 문장은 하나입니다. 꺼낼 때 열쇠를 이미 알고 있습니까.
- 관계형 데이터베이스. 표와 관계로 담고 SQL이라는 표준 언어로 조건을 걸어 찾습니다. 지난달 가입자 중 결제가 없는 사람처럼 열쇠가 아닌 조건으로 찾아야 하면 이쪽입니다. 여러 작업을 한 덩어리로 묶어 전부 성공하거나 전부 취소되게 만드는 트랜잭션이 필요한 돈 관련 기능도 여기 남깁니다.
- 표 구조에 매이지 않는 NoSQL 계열의 문서형 저장소. 기록마다 들어 있는 항목이 달라도 되고, 값 안쪽의 항목으로도 찾을 수 있습니다. 구조가 자주 바뀌는데 조건 검색은 포기할 수 없을 때 중간 자리를 맡습니다.
- 프로그램 자신의 메모리. 서버 한 대만 돌린다면 가장 빠르고 아무것도 설치할 필요가 없습니다. 대신 서버를 두 대로 늘리는 순간 각자 다른 값을 들고 있게 되어, 방금 바꾼 설정이 새로고침할 때마다 달라지는 증상이 나타납니다.
셋 다 경쟁 관계가 아닙니다. 대부분의 서비스는 원본을 관계형 데이터베이스에 두고 그 앞에 키-값 저장소를 한 겹 얹는 구성으로 갑니다. 의미가 가까운 것을 찾아 주는 벡터 데이터베이스처럼 아예 다른 질문을 맡는 저장소까지 합쳐 네 종류를 함께 쓰는 팀도 흔합니다.
실무 용례
"이 통계는 매번 계산하지 말고 키-값에 5분짜리로 얹어 둡시다."
같은 답을 반복해 계산하고 있으니 결과를 저장해 재사용하자는 제안입니다.
"그건 여기 넣으면 안 돼요. 나중에 회사별로 찾아야 하잖아요."
열쇠가 아닌 조건으로 찾을 일이 생길 데이터라는 지적입니다.
"서버 늘린 뒤로 로그인이 자꾸 풀려요. 세션을 밖으로 빼야 합니다."
각 서버가 상태를 따로 들고 있으니 공용 저장소로 옮기자는 말입니다.
페이지덱이 처음 얹은 것은 세션이었습니다. 서버를 두 대로 늘린 주에 로그인이 무작위로 풀린다는 문의가 들어왔고, 수아가 그 문의를 모아 넘기면서 원인이 드러났습니다. 두 대가 각자 자기 메모리에 로그인 정보를 들고 있었던 겁니다.
그다음에 얹은 것이 관리자 통계 화면이었습니다. 이 화면은 표 여섯 개를 맞춰 보는 무거운 질의를 돌렸고, 지현이 하루에도 몇 번씩 열었습니다. 결과를 5분짜리로 저장해 두자 화면은 2초에서 0.1초로 줄었습니다. 대신 지현은 방금 가입한 팀이 목록에 바로 안 보인다는 점을 감수하기로 했습니다. 얹을 때마다 이 교환이 따라옵니다.
흔한 오해
빠르다는 말을 듣고 원본을 옮기려는 경우가 가장 많습니다. 조회가 수십 배 빨라지는 걸 보면 회원 정보도 여기 두면 되지 않느냐는 생각이 자연스럽게 듭니다. 하지만 이 저장소는 열쇠 없이는 아무것도 찾지 못합니다. 회원 번호로는 꺼낼 수 있어도 가입일이 지난달인 사람을 뽑아 달라는 요청에는 답할 방법이 없고, 그런 요청은 반드시 생깁니다. 옮긴 뒤에 그 사실을 알게 되면 이미 쌓인 데이터를 되돌려야 합니다.
메모리에 있으니 언제 사라져도 괜찮다고 뭉뚱그리는 것도 흔합니다. 캐시로 쓸 때는 맞는 말이지만, 결제 진행 중인 장바구니처럼 다시 만들 수 없는 값을 같은 자리에 두면 서버 재시작 한 번에 그대로 사라집니다. 사라지면 계산해서 다시 만들 수 있는지를 기준으로 나누고, 다시 못 만드는 값은 디스크에 남기는 저장소에 둡니다.
같이 보면 좋은 페이지
- 데이터베이스
저장소가 여덟 갈래로 어떻게 나뉘는지, 종류를 고른 다음에 무엇이 따라오는지 봅니다
- 관계형 데이터베이스
대부분의 서비스가 원본을 두는 자리이자 가장 자주 비교되는 상대입니다
마지막 업데이트 2026-09-16