개발/대용량트래픽

싱글 스레드의 이해와 Redis의 고급 관리 전략

난중후니 2024. 1. 25. 20:50
728x90
반응형

1. Redis 운영과 관리

Redis는 싱글 스레드

Redis를 사용하면서 Redis가 싱글 스레드라는 걸 잊어버려 의도치 않게 성능이 안나오거나 장애가 발생하는 경우가 있습니다.
Redis는 싱글 스레드 기반이므로 오래 걸리는 명령은 적합하지 않습니다.

ex)

  • keys 명령어

    • keys는 서버에 저장된 모든 Key 목록을 보는 명령어 입니다.
    • 모든 Key를 검색하기 때문에 Key가 몇백만개, 몇천만개 등.. Key가 많을 수록 많은 시간을 소모하게 됩니다.
  • flushall / flushdb 명령을 주의하자

    • Redis는 db라는 가상의 공간을 분리할 수 있는 개념을 제공해주고 select 명령어로 이런 db로 이동할 수 있습니다.
    • flushdb는 이러한 db 하나에 대한 모든 데이터를 지우는 것이며 flushall은 모든 db의 모든 데이터를 지우는 명령어 입니다.
    • Redis에서 모든 명령어를 지운다면 O(n) 이 걸리고 싱글 스레드이기 때문에 데이터가 많을 수록 많은 시간을 소모하게 됩니다.

2. Redis Persistent

RDB

RDB는 RDBMS라는 오해를 받지만 RDB는 단순히 메모리의 스냅샷을 파일 형태로 저장할 때 쓰는 파일의 확장자명입니다.
RDB를 사용해 현재 메모리에 대한 스냅샷을 저장하는 방식은 2가지가 있습니다.

save 방식

모든 작업을 멈추고 현재 메모리 상태에 대한 RDB 파일을 생성합니다.
실제로 save 옵션으로 50GB의 메모리 상태를 저장한다면 7~8분 정도 소요된다고 합니다.

실행되는 명령어: save <Seconds> <Changes>
예를 들어 save 900 10라고 한다면 900초 안에 10번의 변경이 있을 때 RDB를 사용해 디스크에 스냅샷을 저장한다는 뜻입니다.

RDB를 사용하려면 Redis 설정파일인 redis.conf 파일에 다음과 같은 내용을 추가해야 합니다.

# dump.rdb는 예시로 파일명을 적은것으로 원하시는 파일명을 이용하시면 됩니다.
dbfilename dump.rdb
bgsave

백그라운드 save라는 의미로 fork() 명령을 통해서 부모 프로세스로부터 자식 프로세스를 생성하고 현재 가지고 있는 메모리의 상태가 복제된 상태에서 데이터를 저장하도록 합니다.

AOF

AOF는 "Append Only File"의 약어로 데이터를 저장하기 전에 AOF 파일에 현재 수행할 명령어를 저장해놓고 장애가 발생하면 AOF를 기반으로 복구됩니다.

즉, 다음과 같은 순서로 데이터가 저장됩니다.

  • 클라이언트가 Redis에 업데이트 관련 명령을 요청합니다.
  • Redis는 해당 명령을 AOF에 저장합니다.
  • 파일쓰기가 완료되면 실제로 해당 명령을 수행해서 Redis 메모리에 내용을 변경합니다.

AOF를 사용하기 위해 redis.conf에 다음과 같은 설정을 해야합니다.

# appendonly는 기본적으로 no로 설정되어 있습니다.
appendonly yes
appendfilename appendonly.aof
appendfsync everysec

주의해야 할 설정은 appendfsync 값 입니다.
AOF는 파일에 저장할 때 파일을 버퍼 캐시에 저장하고 적절한 시점에 이 데이터를 디스크로 저장하는데 appendfsync는 디스크와 동기화를 얼마나 자주 할 것인지에 대해 설정하는 값으로 다음과 같이 3가지가 있습니다.

  • always: AOF 값을 추가할 때마다 fsync를 호출해서 디스크에 실제 쓰기를 합니다.
  • everysec: 매초마다 fsync를 호출해서 디스크에 실제 쓰기를 합니다.
  • no: OS가 실제 sync를 할 때까지 따로 설정하지 않습니다.

AOF와 RDB의 우선순위

만약 AOF와 RDB의 파일이 모두 있다면 AOF의 파일을 기준으로 읽습니다.
그 이유는, RDB는 주기적으로 스냅샷을 찍어 최신의 데이터를 가지지 못하지만 AOF는 메모리에 수정사항을 반영하기 전에 항상 디스크에 추가하기 때문에 둘 다 있다면 AOF를 기준으로 읽습니다.

Redis가 메모리를 두 배로 사용하는 문제

Redis가 운영되는 중에 장애를 일으키는 가장 큰 원인은 RDB를 저장하는 Persistent 기능으로 bgsave 방식을 사용하기 때문입니다.
bgsave는 fork()를 사용하기 때문에 자식 프로세스를 생성하면 COW(Copy On Write)라는 기술을 이용하여 부모 프로세스의 메모리에서 실제로 변경이 필요한 부분을 복사합니다.
그러므로 해당 부분이 두 개가 존재하게 되므로 메모리를 두 배로 사용하는 경우가 발생할 수 있습니다.

즉, Redis에 write가 발생할 때마다 기존에 사용하던 메모리의 2배가 필요하게 됩니다.
이로 인해 메모리 부족 문제가 발생할 수 있습니다.

Redis 장애: Read는 가능하지만 Write는 실패하는 경우

정기적인 Heartbeat는 이상이 없지만 Redis 서버는 동작하지 않을 때가 있습니다.
이러한 문제는 RDB 저장이 실패할 때 기본 설정상 Write 명령이 동작하지 않기 때문입니다.

Redis는 RDB 저장이 실패하면 해당 장비가 이상이 있다고 생각해서 Write 명령을 더는 처리하지 않고 데이터가 변경되지 않도록 관리합니다.

RDB 생성 실패하는 이유

  • RDB를 저장할 수 없을 정도로 디스크에 공간이 없는 경우
  • 실제 디스크가 고장난 경우
  • 메모리 부족으로 자식 프로세스를 생성하지 못한 경우
  • 강제적으로 자식 프로세스를 종료시킨 경우

RDB 저장을 실패하면 Redis 내부의 lastbgsave_status 라는 변수가 REDIS_ERR 로 설정됩니다.
그리고 Write 관련 요청은 모두 무시하게 됩니다.

Redis 복제

Redis 복제 모델

Redis는 Master / Slave 형태의 복제 모델을 지원합니다.
Slave는 오직 한 대의 Master만 가질 수 있습니다.
그리고 Slave가 다른 장비의 Master가 될 수도 있습니다.

slaveof 명령을 통해 현재 Redis를 다른 Redis의 Slave로 변경하기

아래 명령을 통해 현재 redis를 127.0.0.1의 6379 port의 Slave Redis로 지정할 수 있습니다.
단, Redis 서버가 재시작된다면 해당 설정은 적용되지 않습니다(재시작 후에도 설정을 적용하고 싶다면 redis.conf 파일에서 설정)
slaveof 127.0.0.1 6379

redis.conf를 통해 현재 Redis를 다른 Redis의 Slave로 변경하기

Redis 설정 파일인 redis.conf를 보면 slaveof라는 항목이 있습니다.
여기에 누구의 Slave로 될건지 명시해주면 됩니다.

# redis.conf 파일에 다음과 같이 입력합니다.
slaveof 127.0.0.1 6379

Redis 복제 과정

  1. Slave에서 slaveof 명령을 이용하여 Master 를 설정합니다.
  2. Master가 설정되면 replicationCron 에서 현재 상태에 따라 connectWithMaster를 호출합니다.
  3. Master는 복제를 위해 RDB를 생성한 후 Slave에게 전송합니다.
  4. Slave는 RDB를 로드하고 나머지 차이에 대한 명령을 Master에게 전달받아서 복제를 완료합니다.

Redis 복제 사용시 주의사항

slaveof no noe

slaveof no one은 현재 Slave를 Master로 승격시킬 때 사용됩니다.

Redis의 복제에서 주의할 점은 Slave와 Master의 연결이 끊어지고 다시 연결되면 Master의 최종 결과로 sync 한다는 점인데 Master에서 장애가 나서 데이터가 모두 지워진다면 Slave 에도 모든 내용이 사라지는 문제가 발생할 수 있습니다.

slaveof no one 명령을 통해 Slave를 Master로 승격시킨다면 Slave는 데이터를 계속해서 유지시키고 업데이트 시켜 나갈 수 있습니다.

복제를 쓰는 경우 무조건 백그라운드로 RDB를 생성한다는 점을 주의

일반적으로 RDB를 사용하면 메모리를 두 배로 사용할 가능성이 있기 때문에 RDB를 사용하지 않는 경우도 있습니다.
반면, 복제를 한다면 사용자의 설정과 무관하게 무조건 Slave로 전달할 RDB를 만들어야 하기 때문에 fork를 해서 RDB를 백그라운드로 생성합니다.

이러한 과정에서 장애가 발생할 경우가 있기 때문에 하나의 프로세스에서 메모리를 많이 사용하지 않도록 나누는 과정이 필요합니다.

Redis 복제를 이용한 실시간 마이그레이션

Redis 서비스를 운영하다 보면 데이터를 더 좋은 장비로 이동해야 하는 경우가 생깁니다.
가능한 다운 타임 없이 바로 데이터를 옮기고 싶어하는 경우가 많은데 Redis의 복제 기능을 이용하면 다운 타임 없이 쉽게 데이터를 이전하는게 가능합니다.

기존 Master 장비를 127.0.0.1:6379 라 하고 새로운 장비를 127.0.0.1:6380 이라고 가정합니다.

다음과 같은 순서로 작업하면 됩니다.

  1. 데이터 이전을 위해 새로운 장비를 Redis 인스턴스로 실행시킵니다.
  2. 새로운 장비를 기존 장비의 Slave 로 등록시킵니다. 이를 통해 Master 와 Slave 데이터를 동기화 시킵니다.
  3. 새로운 장비의 옵션에 slave-read-only 옵션을 끕니다. 이를 통해 새로운 장비의 데이터를 계속해서 업데이트 시킵니다. 이 설정을 끈다고 Slave의 변경이 Master 로 옮겨가진 않습니다.
  4. 기존의 클라이언트들이 새로운 장비를 Master 로 인식하게끔 설정을 바꿉니다.
  5. slaveof no one 명령을 통해서 새로운 장비를 Master로 승격시키고 기존 Master를 종료시킵니다.

3. Redis의 보안 설정

redis.conf에서 보안 설정을 하길 권장합니다.

Redis 비밀번호 설정하기

# requirepass foobared
주석을 해제하고 foobared를 원하는 비밀번호로 바꿔 Redis의 비밀번호를 설정합니다.

Redis 서버에 접속할 수 있는 ip 설정하기

# bind 127.0.0.1 ::1

주석을 해제하고 bind 뒷부분을 자신이 원하는 ip 주소로 바꿔주면 됩니다.

다음처럼 여러개의 ip에 대해서도 접근 가능하도록 할 수 있습니다.

bind 127.0.0.1 ::1
bind 192.168.1.1

Redis가 사용할 수 있는 최대 메모리 설정하기

#maxmemory <bytes>

주석을 해제하고 부분에 1gb, 500kb와 같이 원하는 최대 메모리 크기를 적어줍니다.

메모리가 가득찬 경우 데이터 교체 우선순위를 결정하는 알고리즘

#maxmemory-policy noeviction

noeviction 부분을 자신이 원하는 알고리즘명으로 바꾸면 됩니다.
적용가능한 알고리즘은 다음과 같습니다.

728x90
반응형