DynamoDB 파티션 키, 나중에 바꿀 수 없는 결정
DynamoDB는 파티션 키 값으로 데이터를 여러 파티션에 나눠 저장하고, 파티션마다 처리 한도가 따로 있다.
요청이 한 값에 몰리면 테이블 전체 용량이 넉넉해도 거절되는데, 이 키는 테이블을 만든 뒤에 바꿀 수 없다.
이 글의 목차데이터는 파티션에 나뉘어 담긴다6
DynamoDB 콘솔에서 테이블을 만들면 첫 화면에 칸이 몇 개 없다.
테이블 이름, 파티션 키, 그리고 선택 항목인 정렬 키 정도다.
처음에는 id나 date 같은 이름을 적고 넘어가기 쉽다.
그 아래 용량 설정은 나중에 바꿀 수 있다.
그런데 파티션 키는 한번 정하면 그 테이블에서는 바꿀 수 없고, 테이블이 요청을 얼마나 고르게 받아 낼지도 이 값이 정한다.
데이터는 파티션에 나뉘어 담긴다
DynamoDB는 AWS의 NoSQL 데이터베이스다.
관계형 데이터베이스처럼 SQL로 여러 테이블을 엮어 조회하기보다, 키로 항목을 바로 찾아 읽고 쓰는 데 맞춰져 있다.
테이블 하나에 항목이 들어가고, 항목마다 속성이 붙는다.
DynamoDB는 데이터를 파티션에 나눠 저장한다.
파티션은 테이블에 배정된 저장 공간의 한 조각이다.
몇 개로 나눌지는 DynamoDB가 알아서 관리하고, 사용자가 직접 다루지는 않는다.
그렇다면 항목이 어느 파티션에 들어갈지는 어떻게 정해질까.
DynamoDB는 파티션 키 값을 내부 해시 함수에 넣고, 그 결과로 파티션을 고른다.
해시 함수는 입력을 고르게 흩어진 숫자로 바꾸는 계산이다.
정렬 키까지 정하면 같은 파티션 키 값을 가진 항목들이 정렬 키 순서로 모여 저장된다.
‘사용자 A의 주문을 날짜순으로’ 같은 조회가 한 번에 끝나는 이유다.
파티션마다 처리 한도가 있다
파티션 하나는 초당 읽기 단위 3,000개와 쓰기 단위 1,000개까지 처리하도록 설계돼 있다.
쓰기 단위 하나는 1KB 이하 항목을 초당 한 번 쓰는 양이다.
테이블이 받는 요청은 여러 파티션이 나눠 맡는다.
그래서 요청이 고르게 퍼지면 넉넉하지만, 한 파티션에 몰리면 그 파티션의 한도에 먼저 걸린다.
한도를 넘은 요청을 DynamoDB가 돌려보내는 것을 스로틀링(throttling)이라고 한다.
테이블 전체 용량이 충분해도 파티션 단위 스로틀링은 일어날 수 있다.
요청이 몰린 파티션은 ‘뜨거운 파티션(hot partition)‘이라고 부른다.
이때 오류에 붙는 사유는 KeyRangeThroughputExceeded다.
이 이름이 보이면 테이블 용량보다 키 설계를 먼저 의심한다.
어떤 키가 요청을 고르게 나누는가
AWS는 흔히 쓰는 파티션 키를 놓고 요청이 고르게 나뉘는지 비교해 두었다.
| 파티션 키로 쓴 값 | 고르게 나뉘는 경우 | 한곳에 몰리는 경우 |
|---|---|---|
| 사용자 ID | 사용자가 많고 요청이 퍼짐 | 한 사용자가 요청 대부분을 만듦 |
| 상태 코드 | 거의 없음 | 값이 몇 개뿐이라 인기 값에 집중 |
| 생성 날짜 | 거의 없음 | 새 쓰기가 오늘 날짜 하나에 집중 |
| 장치 ID | 장치마다 보내는 양이 비슷함 | 한 장치가 유난히 자주 보냄 |
장치 ID 줄이 특히 헷갈린다.
값의 종류가 많으면 괜찮을 것 같지만, 실제 요청이 한 값에 쏠리면 결과는 같다.
값이 몇 가지인지와 함께 요청이 어느 값에 몰리는지도 봐야 한다.
오늘 들어온 주문 100건을 저장한다. 네 파티션에 쓰기가 나뉘는 모양은 파티션 키에 따라 달라진다.
파티션 1 100건 · 한도 초과
파티션 2 0건
파티션 3 0건
파티션 4 0건
주문 날짜를 키로 쓰면 오늘 쓰기 100건이 한 파티션으로 몰리고, 사용자 ID처럼 흩어지는 값을 쓰면 파티션마다 비슷하게 나뉜다.
100건이 모두 오늘 날짜라는 같은 키 값을 가져 한 파티션으로 몰린다. 나머지 세 파티션은 비어 있다.
키 값이 파티션에 나뉘는 방식을 단순화한 예시다. 건수와 한도선은 설명을 위한 값이고, 실제 파티션 수와 처리 한도는 표의 크기와 처리량에 따라 다르다.
날짜를 키로 쓰면 생기는 일
로그나 주문처럼 시간순으로 쌓이는 데이터는 날짜를 파티션 키로 쓰고 싶어진다.
날짜별로 조회하기 편해 보이기 때문이다.
그런데 쓰기는 늘 오늘 날짜로 들어온다.
생성 날짜를 키로 쓰면, 그날 새로 쓰는 항목이 모두 한 키 값과 그 값이 놓인 파티션으로 간다.
지난 날짜의 파티션은 놀고 있는데 오늘 파티션만 한도에 걸린다.
DynamoDB가 이 문제를 저절로 풀기도 한다.
요청이 몰린 파티션을 쪼개 같은 키 값의 항목까지 여러 파티션에 나눌 수 있고, 이렇게 적응하는 데 몇 분이 걸린다.
잠깐 나타났다 사라지는 스로틀링은 이 과정일 수 있다.
날짜를 파티션 키로, 생성 시각을 정렬 키로 쓰는 흔한 구성에서는 이 분할에 기대기 어렵다.
정렬 키가 계속 커지면 어디서 잘라도 새 쓰기는 모두 뒤쪽 파티션에 떨어지기 때문이다.
그러면 그 키 값의 쓰기는 초당 쓰기 단위 1,000개에 묶인다.
테이블을 만든 뒤에는 바꿀 수 없다
관계형 데이터베이스는 테이블을 만든 뒤에도 정의를 고칠 수 있다.
DynamoDB는 테이블을 만들 때 키 구성을 함께 지정해야 한다.
테이블을 만든 뒤 설정을 바꾸는 UpdateTable 작업이 다루는 것은 처리량, 보조 인덱스, 스트림 설정이다.
파티션 키는 그 목록에 없다.
그래서 키를 잘못 골랐을 때의 대책이 크다.
뜨거운 파티션의 장기 대책은 키 설계를 바꾸는 것인데, 그러려면 기존 데이터를 모두 옮기고 테이블을 읽고 쓰는 코드를 전부 고쳐야 한다.
옮기는 동안 두 테이블에 함께 쓰거나 서비스를 잠시 멈춰야 할 수도 있다.
나중에 다른 기준으로 조회할 일이 생기면 글로벌 보조 인덱스(GSI)를 추가할 수 있다.
다만 인덱스도 파티션으로 나뉘어 저장되므로 인덱스의 파티션 키를 고를 때 같은 고민을 한 번 더 한다.
키는 자주 할 조회에서 출발한다
관계형 데이터베이스에서는 데이터를 중복 없이 나눠 정리해 두고 필요한 질의를 나중에 만든다.
DynamoDB는 순서가 반대다.
앱이 어떤 조회를 자주 할지 먼저 적고, 그 조회가 한 번에 끝나도록 키를 정한다.
AWS의 파티션 키 모범 사례 문서도 앱에 필요한 접근 패턴을 먼저 정하고, 모든 파티션 키에 요청이 고르게 퍼지도록 설계하라고 권한다.
시간순 데이터라면 날짜를 정렬 키로 내리고 파티션 키에는 사용자 ID처럼 값이 많고 고르게 퍼지는 속성을 두는 방법이 흔하다.
상태 코드처럼 값이 적은 속성은 ACTIVE#customer123처럼 값이 많은 속성과 붙여 쓰기도 한다.
이미 운영 중인 테이블에서 스로틀링이 보인다면 어느 키가 몰리는지부터 확인한다.
AWS의 스로틀링 대응 문서는 CloudWatch Contributor Insights를 켜서 가장 많이 스로틀링된 키를 찾는 방법을 안내한다.