Redis JSON RAM 사용량
Redis JSON RAM 사용량 (Redis JSON RAM Usage)
Redis JSON을 쓰다 보면 "이게 메모리를 얼마나 먹지?" 궁금할 때가 있어요. 이 페이지는 Redis JSON 값이 메모리를 어떻게 차지하는지, 특히 JSON.DEBUG MEMORY 명령으로 소비 메모리를 진단하는 방법을 설명합니다.
노트: 지속적인 기능 추가·개선·최적화 때문에 JSON 메모리 소비량은 Redis 버전에 따라 달라질 수 있어요. 이 페이지의 예제는 Redis 오픈 소스(Redis Open Source)의 Redis 8을 기준으로 작성됐습니다.
기본 메모리 요구사항 (Basic memory requirements)
Redis의 모든 키는 메모리를 차지하며, 최소한 키 이름을 저장할 RAM과 Redis가 사용하는 키당 오버헤드(per-key overhead)가 필요해요. 그 위에 키 안의 값(value)도 RAM을 요구합니다.
Redis JSON은 JSON 값을 역직렬화(deserialization) 후 이진(binary) 데이터로 저장합니다. 이 표현은 직렬화된 형태보다 크기 측면에서 종종 더 큽니다. 모든 JSON 값은 (64비트 아키텍처에서) 최소 8바이트를 차지하는데, 각 값이 포인터(pointer)를 감싸는 얇은 래퍼로 표현되기 때문이에요. 타입 정보는 포인터의 하위 비트에 저장되는데, 정렬(alignment) 제약 덕분에 그 비트들이 0으로 보장되므로 보조 데이터를 저장하는 데 재사용할 수 있습니다.
일부 JSON 값 타입은 8바이트면 충분해요.
- Null과 boolean은 추가 저장 공간이 필요 없습니다.
- **작은 정수(small integers)**는 자주 사용되므로 정적 메모리(static memory)에 저장되어 역시 처음 8바이트만 사용해요.
- 빈 문자열, 배열, 객체도 부기(bookkeeping)가 필요 없으며, 대신 null 문자열·배열·객체의 정적 인스턴스를 가리킵니다.
JSON.DEBUG MEMORY 명령으로 메모리 소비를 확인하는 예제를 볼게요:
127.0.0.1:6379> JSON.SET boolean . 'true'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY boolean
(integer) 8
127.0.0.1:6379> JSON.SET null . null
OK
127.0.0.1:6379> JSON.DEBUG MEMORY null
(integer) 8
127.0.0.1:6379> JSON.SET emptystring . '""'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY emptystring
(integer) 8
127.0.0.1:6379> JSON.SET emptyarr . '[]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY emptyarr
(integer) 8
127.0.0.1:6379> JSON.SET emptyobj . '{}'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY emptyobj
(integer) 8
이 RAM 요구사항은 모든 스칼라 값에 동일하지만, **문자열(string)**은 길이에 따라 추가 공간이 필요해요. 예를 들어 3글자 문자열은 3바이트를 추가로 사용합니다:
127.0.0.1:6379> JSON.SET foo . '"bar"'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY foo
(integer) 11
배열의 메모리 (Array memory)
다음 네 예제에서 각 배열은 56바이트를 차지합니다. 이는 이렇게 나뉘어요:
- 초기 배열 값 포인터 8바이트
- 메타데이터 16바이트: 할당된 용량(capacity) 8바이트 + 해당 시점 배열 크기 8바이트
- 배열 32바이트. 배열의 초기 용량은 4이므로
4 * 8= 32바이트
127.0.0.1:6379> JSON.SET arr . '[""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56
127.0.0.1:6379> JSON.SET arr . '["", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56
127.0.0.1:6379> JSON.SET arr . '["", "", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56
127.0.0.1:6379> JSON.SET arr . '["", "", "", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56
현재 용량이 새 값을 담기에 부족해지면 배열은 용량을 두 배로 재할당합니다. 5개 요소를 가진 배열은 용량이 8이 되므로 8 + 16 + 8 * 8 = 88바이트를 소비해요.
127.0.0.1:6379> JSON.SET arr . '["", "", "", "", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 88
재할당 연산은 비쌀 수 있기 때문에 Redis는 JSON 배열을 선형이 아니라 기하급수적으로(geometrically) 증가시켜요. 이 방식은 비용을 여러 삽입에 분산시킵니다.
실제 파일 크기 비교 (Size comparison)
이 표는 모듈 저장소의 테스트 파일 몇 개를 JSON으로 저장했을 때의 크기(바이트)를 보여줘요. MessagePack 열은 참고용으로, MessagePack으로 저장했을 때의 값 길이를 반영합니다.
| 파일 (File) | 파일 크기 (File size) | Redis JSON | MessagePack |
|---|---|---|---|
| /tests/files/pass-100.json | 381 | 1069 | 140 |
| /tests/files/pass-jsonsl-1.json | 1387 | 2190 | 757 |
| /tests/files/pass-json-parser-0000.json | 3718 | 5469 | 2393 |
| /tests/files/pass-jsonsl-yahoo2.json | 22466 | 26901 | 16869 |
| /tests/files/pass-jsonsl-yelp.json | 46333 | 57513 | 35529 |
노트: 현재 버전에서는 컨테이너(container)에서 값을 삭제해도 컨테이너의 할당된 메모리가 해제되지 않습니다.
JSON 문자열 재사용 메커니즘 (JSON string reuse mechanism)
Redis는 메모리 사용을 줄이기 위해 전역 문자열 재사용(global string reuse) 메커니즘을 사용해요. 같은 문자열 값이 여러 번 나타나면(같은 JSON 문서 안이든, 같은 노드의 서로 다른 문서 사이든) Redis는 그 문자열의 단일 복사본만 저장하고 그에 대한 참조(reference)를 사용합니다. 이 방식은 많은 문서가 비슷한 구조를 공유할 때 특히 효율적이에요.
다만 JSON.DEBUG MEMORY 명령은 문자열이 실제로 재사용되더라도 각 인스턴스가 독립적으로 저장된 것처럼 메모리 사용을 보고합니다. 예를 들어 문서 {"foo": ["foo", "foo"]}는 내부적으로 문자열 "foo"를 재사용하지만, 보고되는 메모리 사용량은 그 문자열을 세 번(키로 한 번, 배열 요소로 두 번) 센 것으로 계산돼요.