❐ Description
나는 Docker 환경에서 redis를 설정하기 때문에 책의 가이드와 약간은 차이가 있었다.
책 내용 + 공부 과정에서 알게된 지식을 기록해보자.
❐ Open files 확인
아래의 커맨드로 현재 설정된 maxclients 설정 값을 확인할 수 있다.
127.0.0.1:6379> config get maxclients
1) "maxclients"
2) "10000"
`maxclients`는 레디스 프로세스에서 받아들일 수 있는 최대 클라이언트 수를 의미한다.
모든 I/O 작업(파일, 소켓, 파이프 등)은 파일 디스크립터를 통해 이루어지기 때문에
Redis는 클라이언트의 연결마다 파일(소켓)을 생성한다.
이때 Redis에 설정된 '파일 디스크립터'의 수가 'maxclients 수 + 32' 보다 작다면 아래의 에러가 발생한다.
MISCONF Redis is configured to save RDB snapshots, but it is unable to persist on disk.
Background saving error.
// 또는
ERR max number of clients reached
그렇다면 왜 최소한 32개 이상이 많아야 하는걸까? Redis는 자체적으로 AOF 파일, RDB 스냅샷 파일과
같은 추가 작업을 위해 약 32개의 파일 디스크립터를 별도로 예약하기 때문이다.
따라서 maxclients가 10000이라면, 파일 디스크립터의 수는 최소 10032가 되어야 한다.
❐ THP 비활성화
Redis 공식문서(Configuration)를 보면 다음과 같이 THP 비활성화를 권장하고 있다.
THP는 무엇이고, 왜 비활성을 권장하는지 알아봤다.
1. THP(Transparent Huge Pages)란?
리눅스는 기본적으로 메모리를 4KB 단위의 페이지로 관리한다. THP는 메모리 관리의 오버헤드를
줄이기 위해 큰 페이지(일반적으로 2MB)를 자동으로 생성하고 관리하는 기능이다.
2. Redis에서 THP가 퍼포먼스를 저하시키는 이유
🧩 THP의 페이지 압출 및 스플릿 오버헤드 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
Redis는 작은 메모리 블록을 매우 빠르게 읽고 쓰는 작업을 수행하는데, THP는 메모리를 2MB로 관리한다.
결국 Redis는 작은 데이터를 요청할 때도 불필요하게 2MB의 페이지를 처리해야 한다.
결과적으로 불필요한 메모리 읽기/쓰기 오버헤드가 발생하고 CPU 리소스를 소모하여 레이턴시를 증가시킨다.
🧩 TLB 미스 증가 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
TLB(Translation Lookaside Buffer)는 가상 메모리 주소와 물리 메모리 주소를 매핑하는 캐시다.
Redis는 메모리에 빠르게 접근해야 하는 성능 민감형 애플리케이션인데, THP는 2MB 단위의 큰 페이지를
사용하기 때문에 TLB 항목 수가 제한적일 수 있고, TLB 캐시 미스가 발생하기 쉽다.
TLB 미스가 발생하면 CPU는 페이지 테이블을 다시 탐색해야 하므로 메모리 접근 속도가 저하된다.
🧩 메모리 단편화 문제 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
THP는 큰 페이지를 할당하려고 하기 때문에 메모리가 단편화될 수 있다. 메모리 단편화가 심해지면 Redis가
메모리를 할당하거나 확장할 때 지연이 발생할 수 있다. Redis는 메모리를 효율적으로 사용해야 하는데,
THP가 이 메모리 구조를 비효율적으로 만들어 레이턴시를 증가시킨다.
🧩 CPU 리소스 낭비 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
THP는 메모리 페이지를 자동으로 병합하거나 분할하는 작업을 수행할 때 CPU 사이클을 소모한다.
Redis와 같이 메모리와 CPU에 민감한 애플리케이션은 THP 관리 비용으로 인해 CPU 사이클이 불필요되며,
결과적으로 성능 저하의 원인이 될 수 있다.
3. 비활성하기
root@8c95b2629d08:/# echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
[출력] : never
root@8c95b2629d08:/# cat /sys/kernel/mm/transparent_hugepage/enabled
[출력] : always madvise [never]
위 방법은 일시적으로 값을 변경하는 것이다. `[ ]` 안에 있는 값이 현재 설정된 정보라고 보면된다.
❐ vm.overcommit_memory= 1로 변경
1. fork & COW
우선 설정을 변경하기 전에 `fork()`와 CoW(copy-of-write)에 대해서 알아봤다.
🧩 fork() ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
`fork()`는 리눅스 및 유닉스 계열 OS에서 사용되는 system call로, 부모 프로세스를 복제하여
자식 프로세스를 생성한다. 이때 '자식 프로세스'는 '부모 프로세스'의 메모리 공간을 그대로 복제한다.
그러나 메모리 전체를 복사하는 것은 매우 비효율적이다.
이런 문제를 해결하기 위해 Copy-On-Write (COW) 메커니즘이 등장했다.
🧩 Copy-On-Write (COW) ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
COW는 메모리 페이지를 실제로 복사하지 않고, 읽기 전용 상태로 공유하는 기술이다.
부모 프로세스와 자식 프로세스는 메모리 페이지를 공유하고, 데이터가 수정될 때만 복사본을 생성한다.
따라서 메모리 사용량과 복사 비용을 최소화할 수 있다.
2. vm.overcommit_memory란?
리눅스 커널에서 메모리 오버커밋(overcommit) 정책을 제어하는 설정으로,
애플리케이션이 요청한 메모리를 즉시 할당할지 또는 메모리의 실제 사용 가능 여부를 검사할지를 결정한다.
/proc/sys/vm 경로에서 확인할 수 있다.
다음은 설정 값에 대한 설명이다.
3. Redis에서 overcommit_memory을 1로 설정하는 이유
- 백그라운드에서 sanp-shot을 생성하고나 AOF를 처리할 때 `fork()`가 실패하지 않도록 보장
- 메모리의 실제 크기와 관계없이 메모리 할당이 항상 성공하도록 보장
- Redis의 경고 해소
4. 설정 방법
컨테이너 내에서는 기본으로 1로 셋팅이 되어 있었다. (redis:7.4.1)
영구적으로 설정하기 위해서는 `/etc/sysctl.conf` 파일에 아래의 설정을 추가해준다.
vm.overcommit_memory = 1
그리고 설정을 적용해주기 위해 아래의 커맨드를 실행한다.
sudo sysctl -p
❐ somaxconn과 syn_backlog 설정 변경
`redis.conf`의 tcp-backlog 파라미터는 레디스 인스턴스가 클라이언트와 통신 할 때 사용하는 tcp-backlog 큐의
크기를 지정한다. 이때 `redis.conf`에서 지정한 tcp-backlog 값은 서버의 Somaxconn과 syn_backlog 값보다
클 수 없다. 기본 tcp-backlog 값은 511이므로, 서버 설정이 최소 이 값보다 크도록 설정해야 한다. (공식문서)
기본 tcp-backlog
127.0.0.1:6379> config get tcp-backlog
1) "tcp-backlog"
2) "511"
아래의 커맨드를 통해 값을 변경해 준다.
root@8c95b2629d08:/# sysctl net.ipv4.tcp_max_syn_backlog=1024
[output] net.ipv4.tcp_max_syn_backlog = 1024
root@8c95b2629d08:/# sysctl net.core.somaxconn=1024
[output] net.core.somaxconn = 1024
참고로 `tcp_max_syn_backlog`는 TCP 연결이 될 때 SYN 패킷이 대기하는 큐의 크기를 제어하는 파라미터다.
그리고 somaxconn은 soket max connection의 약어다.
❐ 개인 추가 설정 사항
docker 컨테이너 내에서 커널의 주요 자원에 접근이 제한된다.
그래서 아래와 같이 compose 파일에 권한 설정을 추가했다.
redis:
image: redis:7.4.1
container_name: redis-container
privileged: true # 권한 설정
ports:
- "6379:6379"
volumes:
- play_ground_redis_data:/data
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
command: [ "redis-server", "/usr/local/etc/redis/redis.conf", "--appendonly", "yes" ]
networks:
- gilbert-local-network
'Back-End > Redis' 카테고리의 다른 글
Redis를 캐시로 사용하기 (0) | 2024.12.20 |
---|---|
Redis 자료구조 활용 사례 (0) | 2024.12.18 |
Redis 기본 개념 (0) | 2024.12.18 |
마이크로서비스 아키텍쳐와 레디스 (0) | 2024.12.16 |
ReadMe (0) | 2024.12.16 |