이번장에서는 메모리 관리 아키텍처, 메커니즘에 대해 알아보게 된다.
운영하며 이슈가 발생하는 경우 원인 파악 그 외에도 메모리를 효율적으로 사용할 수 있게 된다.
메모리 관리 아키텍처
레디스 서버는 메모리를 다음과 같은 형태로 사용한다.
레디스는 malloc 함수로 메모리를 할당하기 때문에 운영체제의 메모리 페이지 할당 매핑을 제어할 수 없도록 구현되어 있다.
이게 중요한 이유는 레디스에서 데이터 삭제 명령어를 실행해도 확보된 메모리가 운영체제로 반환된다는 보장이 없기 때문이고, 실제 메모리 사용량인 RSS도 변하지 않기 때문이다.
즉, 메모리가 부족한 상태에서 키를 삭제해도 운영체제 관점에서는 메모리 여유 공간이 반영되지 않으므로 주의가 필요하다.
클라이언트에서 보낸 명령어는 레디스 서버의 클라이언트 쿼리 버퍼라는 공간에 일단 저장하고, 소켓을 통해 데이터를 점진적으로 읽는다.
레디스 클라이언트 쿼리 버퍼
- 역할: 레디스 서버에 연결된 클라이언트가 보낸 명령어를 임시로 저장하는 메모리 공간이다.
- 저장 방식: 쿼리 버퍼는 클라이언트가 보낸 명령어를 바이트 단위로 저장하고, 이 명령어들은 레디스 서버의 이벤트 루프에서 처리
- 크기 제한: 쿼리 버퍼는 설정 가능한 크기 제한을 가지고 있다. 기본적으로 1GB(4.0.7 이전 버전)로 설정되어 있지만, redis.conf 파일에서 client-query-buffer-limit 설정을 통해 변경할 수 있다.
- 제한 초과 시: 쿼리 버퍼가 가득 차면 서버는 클라이언트 연결을 닫거나, 클라이언트 측 오류가 발생할 수 있다.
INFO Memory 출력 결과 해석
user_memory
- used_memory 값이 maxmemory를 초과하면 maxmemory-policy 명령어로 설정한 값을 따르도록 동작한다.
- 레디스가 jemalloc 같은 메모리 할당자를 사용하여 할당한 메모리의 크기. 즉, 실제 데이터가 사용하는 크기이다.
- used_memory에는 클라이언트 출력 버퍼 등도 포함되어 있으므로 데이터만 사용하는 것이 아니라는 점에 유의해야 한다.
- 레디스가 jemalloc 같은 메모리 할당자를 사용하여 할당한 메모리의 크기. 즉, 실제 데이터가 사용하는 크기를 계산할 때는 포함되지 않는다.
maxmemory
- 레디스가 사용할 수 있는 최대 메모리 크기. 즉, maxmemory 명령어로 설정된 값 의미한다.
- 레디스를 캐시 서버나 임시 저장소로 사용하는 경우, 일반적으로 maxmemory를 설정하는 것이 좋다.
메모리 단편화 문제 대처 방법
메모리 단편화율
메모리 단편화 비율을 나타내는 값으로, 레디스가 현재 사용 중인 것으로 인식하는 메모리 사용량을 RSS 값으로 나눈 값
mem_fragmentation_ratio = used_memory_rss / used_memory
mem_fragmentation_ratio
- 메모리 단편화율을 나타낸다.
- 현재 메모리 단편화 상황을 어떻게 해석하고 대처해야 할지 알려주는 값이다.
- 단편화율이 1.0이거나 그보다 약간 더 높으면 양호하고, 1.5 이상이되면 성능 문제가 발생할 수 있다. 만약 1.0 보다 낮다면, 스왑이 발생하고 있을 가능성이 있다.
메모리 단편화율은 RSS 값 대비 레디스가 현재 사용 중이라고 인식한 메모리 사용량을 의미한다.
데이터 삭제나 저장이 잦으면 삭제된 데이터 자리에 새로운 데이터를 효율적으로 쓰지 못해서 메모리 사용 효율이 떨어질 수 있다.
효율이 떨어진다는 뜻은 단편화가 심해질 수 있다는 뜻이며, 단편화가 심해지면 스왑 발생 및 성능 저하로 이어질 수 있다.
만약 단편화가 발생했다면, 아래와 같은 대처가 필요하다.
1. 레디스 재시작
2. 동적 단편화
3. 메모리 스왑 제한
4. 메모리 할당자 변경
5. MEMORY PURGE 명령어 실행(4.0.0 이상에서 jemalloc을 메모리 할당자로 사용하는 경우 사용 가능)
키 만료
레디스는 주로 캐시 용도로 사용되어 데이터를 가능하면 최신 상태로 유지하고 오래된 데이터는 갱신되어야 한다.
따라서 최대한 캐시 데이터를 재사용하고, 백엔드 데이터베이스에 접근하는 횟수를 줄여서 효율적으로 운용되어야 한다.
만약 캐시로 참조하는 데이터가 현재 데이터와 크게 다르지 않다면 TTL 설정을 통해 적절히 관리가 가능하다.
만료 방법
TTL이 만료되면 대상 키가 메모리에서 삭제되는 것이 아니라 메모리에 남게되는데, 이 데이터를 삭제하는 방법을 말한다.
아래 3가지 방식으로 처리가 가능하다.
- 수동적 방법
- 능동적 방법
- 수동적 방법과 능동적 방법의 조합
수동적 방법
클라이언트가 키에 접근할 때 해당 키의 TTL이 만료되었는지 확인한고, 만료되었다면 해당 키를 메모리에서 삭제한다.
능동적 방법
레디스가 매 초마다 10번, 임의로 20개의 키를 샘플링하여 만료된 키를 확인하고 삭제한다.
샘플링 키 중 25% 이상 만료된 상태라면, 다시 샘플링과 삭제를 반복한다.
다만 이와 같이 처리될 경우 싱글 스레드 처리 방식인 레디스의 특성상 다른 작업을 일시적으로 차단하여, 지연 시간에 영향을 줄 수 있다.
삭제 정책
위 TTL 방식으로는 메모리 공간을 충분히 확보하지 못할 수 있고, 만약 만료될 키가 없으면 메모리 사용량이 최대치에 도달 할 수 있다.
따라서 레디스에서는 maxmemory 명령어로 설정된 값에 도달했을 때, 메모리 확보를 위해 키를 삭제하는 과정이 이루어진다.
삭제 상세 정책은 maxmemory-policy 명령어로 설정된 정책을 따르게 된다.
메모리를 효율적으로 사용하기 위한 기타 방법
위에 언급된 방법 이외에 효율적 메모리 사용을 위한 동적 리해싱과 동적 단편화 제거 기법도 사용 가능하다.
동적 리해싱
레디스는 하나의 큰 해시 테이블을 사용하여 데이터를 관리한다.
그리고 이제 설명할 동적 리해싱은 이 해시 테이블의 크기를 사용하려는 상황에 맞추어 자동으로 조정한다.
동작 방식
동적 리해싱은 CPU 시간의 100밀리 초 중 1밀리 초만큼만 사용하여, 리해싱 중인 테이블 관련 작업이 있을 때마다 점진적으로 진행된다.
따라서 자주 접근되지 않는 테이블은 리해싱이 완료되지 않을 수 있다.
리해싱 과정에서 크기가 조정된 새로운 테이블을 추가로 준비하여 새로운 아이템이 테이블에 저장되도록 한다.
각 작업이 수행될 때마다 오래된 테이블에서 새 테이블로 데이터가 옮겨지고, 데이터가 모두 이동되면 이전 테이블은 삭제된다.
이런 동작 방식 덕분에 리해싱 중에도 테이블을 계속 사용할 수 있다.
만약 동적 리해싱으로 인한 쿼리 지연(2밀리)이 문제가 될 경우 비활성화도 가능하나, 즉시 메모리를 해제하고 싶다면 기본적으로 활성화해두는게 좋다.
동적 단편화 제거
레디스 서버를 사용하다 보면 어느 정도 메모리 단편화가 발생할 수 있다. 특히 크기의 차이가 큰 데이터를 다룰 때 메모리 단편화가 더 많이 발생한다.
단편화는 모든 메모리 할당자에서 발생하지만, jemalloc은 그중에서도 단편화가 덜 발생하도록 설계되어 있으므로 사용하는 것을 권장한다.
만약 단편화가 심해져서 문제가 된다면, 레디스 서버 재시작이나 캐시 노드 재생성 등의 조치를 취해야된다.
이때 동적 단편화 제거 기법을 사용하면 재시작 같은 다른 조치 없이도 단편화 문제를 해결할 수 있다.
동작 방식
특정 임계값을 초과했을 때 jemalloc의 기능을 사용하여 연속된 메모리 영역에 새로운 데이터의 레플리카를 생성하고, 이후 오래된 데이터를 해제하는 방식으로 진행된다.
모든 키에 대해 점진적으로 반복해서 수행되는 방식을 통해 단편화를 정상 범위까지 감소시킨다.
동적 리해싱과 달리 기본적으로 비활성화되어 있으며, jemalloc 기능을 활용하기 때문에 메모리 할당자로 jemalloc을 사용하는 설정에서만 사용 가능하다.
jemalloc
jemalloc은 메모리 할당자(memory allocator) 라이브러리이며, 레디스에서 기본적으로 사용하는 메모리 관리 도구이다.
특징
성능 최적화에 특화된 메모리 할당자로, 기본적인 malloc보다 훨씬 효율적이다. 특히 다중 스레드 환경에서 메모리 단편화를 줄이고, 할당/해제 속도를 향상시킨다.
Redis가 jemalloc을 선택한 이유는 메모리 사용량이 많은 인메모리 데이터베이스의 특성상 메모리 효율성이 매우 중요하기 때문이다. jemalloc은 메모리 사용 패턴을 분석해서 최적화된 방식으로 메모리를 관리한다.
Redis에서의 활용
Redis 컴파일 시 jemalloc이 기본으로 포함되며, INFO memory 명령어로 현재 메모리 사용량과 할당자 정보를 확인할 수 있다. 레디스의 성능과 메모리 효율성에 직접적인 영향을 미치는 핵심 컴포넌트라고 볼 수 있다.
만약 레디스를 프로덕션 환경에서 사용한다면, jemalloc으로 인해 메모리 사용량이 최적화되고 있는 것이니 특별히 건드릴 필요는 없고, 그냥 레디스가 내부적으로 잘 관리하고 있다고 보면 될 것 같다.
'Study OR Book > 실전 레디스' 카테고리의 다른 글
[실전 레디스] Chaprter 10_클라우드에서 사용하는 레디스 (0) | 2025.07.21 |
---|---|
[실전 레디스] Chaprter 08_레디스 클러스터 (1) | 2025.07.14 |
[실전 레디스] Chaprter 07_레플리케이션 (0) | 2025.07.07 |
[실전 레디스] Chaprter 06_트러블 슈팅 (0) | 2025.07.01 |
[실전 레디스] Chaprter 05_레디스 운용 관리 (0) | 2025.06.23 |