ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 레디스 정리
    SQL 공부 2024. 3. 4. 06:41

    레디스의 중요 포인트 정리

     

    싱글 스레드 

    레디스에서 클라이언트 커맨드를 처리하는 부분은 이벤트 루프를 이용한 싱글 스레드로 동작한다.

     

    싱글 스레드로 동작하는 것은 다른 사용자가 오래 걸리는 커맨드를 수행하면, 다른 사용자는 그 쿼리가 완료될 때 까지

    대기할 수 밖에 없으므로 느린 커맨드 사용에 주의를 해야한다.

     

    다만 모든 작업에서 싱글 스레드로 처리되는 것은 아니고,

    일부 비동기 작업에 대해서 백그라운드 스레드가 작동한다. (EX: UNLINK, FLUSHALL ASYNC, FLUSHDB ASYNC) 

     

    네트워크 I/O 병목 현상을 해결하기 위해서 I/O스레딩이라고 하는 핵심 네트워크 모델에 멀티스레딩을 6.0버전에서부터

    도입하였는데 설정파일을 통해 멀티스레딩을 사용할 수 있다. (이전보다 2배정도의 성능 향상)

     

    이에 대해서는 연결설정, 명령가져오기, 명령 실행, 소켓에 응답쓰기를 실행할 수 있는데, 메모리 작업과 관련된 명령 실행은 단일 스레드에서 실행된다. (동시성 문제 예방)

     

     

     

    캐싱

    Look aside (읽기)

     

    1. 데이터가 캐시에 있는지 확인

    2-1. 캐시에 있는 경우 - 캐시에서 데이터를 읽어온다. (캐시 히트)

    2-2. 캐시에 없는 경우 - 애플리케이션이 DB에서 데이터를 읽어오고 애플리케이션이 캐시에 저장한다. (캐시 미스)

     

    처음 레디스를 시작하거나 새로운 데이터를 DB에만 저장하는 경우 캐시 미스가 발생할 경우 

    지연시간이 많이 발생할 수 있다. 

     

    이를 방지하기 위해 미리 데이터베이스에서 캐시로 데이터를 밁어넣어주는 작업을 할 수 있는데,

    이를 캐시 워밍이라고 한다.

     

     

    Read Through(읽기)

     

    1. 캐시에 있는 경우 - 캐시로 부터 읽어 온다. (캐시 히트)

    2. 캐시에 없는 경우 - 캐시 서버에서 데이터베이스로부터 데이터를 저장하고 반환한다.

     

    위 전략과의 차이점은 애플리에키션이 캐시를 채우는 역할을 하냐 안하냐의 차이가 있다.

     

    Write Through(쓰기)

     

    애플리케이션이 데이터베이스 업데이트시마다 매번 캐시에도 데이터를 함께 업데이트 시킨다.

    매번 2개의 저장소에 저장해야하므로 시간 소요가 많이 들게 된다.

    TTL(만료시간)을 설정해 리소스를 낭비도록 하는게 좋다.

     

    Cache Invalidation(쓰기)

    애플리케이션이 데이터베이스에 값을 업데이트할때마다 캐시에서 데이터를 삭제하는 전략

    데이터를 삭제하는것이 새로운 데이터를 저장하는 것보다 리소스 사용이 적어서 사용하는 전략이다.

     

     

    Write Behind[write back] (쓰기)

    쓰기가 빈번한 서비스인 경우 해당 방식을 고려할 수 있는데

    우선 캐시에 데이터를 저장하고 후에 캐시에서 데이터베이스로 저장하는 방식이다.

    빈번한 업데이트를 데이터베이스에 직접하는 것보다. 캐시에 데이터를 저장하고 일정 주기마다

    데이터베이스 저장처리를 하는 방식으로, 데이터베이스 성능을 향상 시킬 수 있는 방식이다.

    단, 캐시 데이터가 휘발될 수 있으므로 사용에 주의가 필요하다.

     

     

    만료키 삭제방식

    레디스에서 키가 만료되었다고하여, 바로 메모리에서 삭제되지는 않는다.

    두 가지 방식으로 삭제되는데 다음과 같다.

     

    1. passive 방식: 클라이언트가 키에 접근하고자 할때 키가 만료되었다면 메모리에서 수동 삭제한다.

    사용자가 다시 접근한지 않을 키가 존재하고 있어 이 방식 하나로는 불충분하다.

     

    2. active 방식: TTL값이 있는 키 중 20개를 랜덤에서 뽑아낸 뒤 만료된 키를 모두 메모리에서 삭제한다. 

    25% 이상 키가 삭제 되었다면 다시 20개 키를 랜덤하게 뽑고 아니라면 20개의 키 집합에서 다시 확인한다.

    (1초에 10번 수행)

     

     

    캐시 스탬피드 현상

    만료시간 설정에 따라 캐시 스탬피드 현상이 발생할 수 있는데

    캐싱으로 Look aside 패턴을 사용하는 경우 만료된 키에 여러 애플리케이션이 동시 다발적으로 접근하게 된다면

    데이터베이스로부터 중복 읽기와 캐시에 중복 쓰기가 발생하게 된다.

    이로 인해 데이터베이스 및 캐시서버에 부하가 가게된다.

     

    여러가지 해결방법 또한 있다.

    1. 적절한 만료시간 설정

    2. 캐시가 만료되기전 랜덤한 확률로 캐시를 업데이트 해주는 방법 - 선계산, PER 알고리즘

     

     

    Redis 백업

    레디스를 캐시가 아닌 영구 저장소와 같은 용도로 사용할 수 있기 위해 Redis에서는

    RDB와 AOF 두 가지 백업방식을 지원한다.

     

    AOF (Append Only File): 레디스 인스턴스가 처리한 모든 쓰기 작업을 차례대로 기록,

    복원 시에는 파일을 다시 읽어 가며 데이터셋을 재구성한다. 

    물론 명령어가 사용자 커맨드 그대로 입력되는것은 아니며, 블로킹작업이나 계산작업같은것은 없어지고,

    단순한 커맨드로만 저장된다. (SET)

     

     

    RDB(Redis DataBase): 일정 시점에 메모리에 저장된 데이터 전체를 저장한다. (Snapshot)

    지정한 시간대에만 저장이 되기 때문에 다음 지정시간까지의 데이터 손실이 발생할 수 있다.

    자동(조건), 수동, 복제시  모두 지원한다.

     

    안정성을 위해 두 가지 방식 모두 다 사용할 수 있으며, RDB만큼의 안정성을 원한다면 둘다 사용하는 것이 좋다.

    물론, AOF파일이 안정성이 더 우위에 있다고 생각하기 때문에 두 파일이 모두 공존할 경우 AOF파일을 우선으로 한다.

     

    RDB, AOF파일을 사용하는 경우 인스턴스의 Max Memory값은 실제 서버 메모리보다 여유를 갖고 하자.

    RDB저장 혹은 AOF파일 재구성시 레디스는fork()를 이용해 자식 프로세스를 형성하는데

    레디스는 Copy-On-Write 방식을 이용해 메모리상의 데이터를 하나 더 복사하는 방법을 이용해 백업을 진행하면서도

    클라이언트 요청을 받아 메모리의 데이터를 읽어 수정하는 작업을 진행한다.

    최악의 경우 기존 메모리 2배를 사용할 수 있게 되는 것이다. (OOM이 발생할 수 있음)

     

     

    Redis 복제

     

    가용성 = 사용가능시간 / 총 시간

     

    레디스에서 고가용성을 확보하기 위한 두 가지 기능

    1. 복제

    2. 자동 페일오버

     

    레디스에서 마스터 역할을 하는 서버는 여러개의 복제본이 연결될 수 있으며, 복제본 노드에 새로운 복제본을 추가하는 것도 가능하다.

    그러나, 가장 상위노드 (마스터) 에서만 데이터를 업데이트 하는 커맨드 수행이 가능하며, 하위 복제본은 모두 읽기전용으로 작동된다.

     

    복케 메커니즘은 다음과 같다.

    1. REPLICAOF 커맨드로 복제 연결을 시도한다.

    2. 마스터 노드에서 fork()로 자식 로세스를 만들어 RDB 스냅샷을 생성한다.

    3. 위 2번의 과정동안 마스터노드에서 수행된 변경작업은 레디스 프로토콜(RESP) 형태로 복제 버퍼에 저장한다.

    4. RDB파일이 생성완료되면 파일은 복제본 노드로 복사된다.

    5. 복제본에 저장했던 모든 내용 삭제 후 RDB파일을 이용해 데이터를 로딩한다.

    6. 복제 과정동안 버퍼링되었던 복제 버퍼 데이터를 복제본으로 전달해 수행시킨다.

     

    위의 과정은 디스크를 사용하는 방식이며 사용하지 않게되면

    1. REPLICAOF 커맨드로 복제 연결을 시도한다.

    2. 마스터 노드에서 소켓통신을 이용해 복제본 노드에 바로 연결하고, RDB파일이 생성됨과 점진적으로 복제본 소켓에 전송한다.

    3. 위 2번의 과정동안 마스터노드에서 수행된 변경작업은 레디스 프로토콜(RESP) 형태로 복제 버퍼에 저장한다.

    4. 소켓에서 읽어온 RDB파일을 복제본 디스크에 저장한다.

    5. 복제본에 저장했던 모든 내용 삭제 후 RDB파일을 이용해 데이터를 로딩한다.

    6. 복제 과정동안 버퍼링되었던 복제 버퍼 데이터를 복제본으로 전달해 수행시킨다.

     

     

    센티넬

    클라이언트가 마스터 노드에 접속하였을때 문제가 발생하였다면, 복제본 노드가 마스터 노드로 변경되어야 하는데,

    이를 처리해주는 작업을 진행하는 서버이다.(자동 페일오버)

     

    복제 구성을 사용하고 센티넬 기능을 사용하지 않는다면, 사용자가 직접 복제본 노드에 접속해

    REPLICA OF NO ONE 커맨드를 이용해 읽기 전용을 해지한 후 애플리케이션 코드에서 앤드포인트를

    복제본 IP로 변경하고, 배포를 진행하여야한다.

     

    센티넬은 다음과 같은 기능을 제공한다.

    1. 모니터링: 마스터, 복제본 인스턴스 상태 실시간 확인

    2. 자동 페일오버: 마스터의 비정상 상태를 감지해 정상 상태의 복제본 중 하나를 마스터로 승격

    3. 인스턴스 구성정보 안내: 클라이언트에게 현재 구성에서 마스터 정보 제공, 페일 오버 발생시에는 변경된 마스터 정보 제공

     

    센티넬은 SPOF 를 방지하기위해 최소 3대 이상 일때, 정상적으로 동작할 수 있도록 설계되었다.

    하나의 센티넬이 문제가 생기더라도 다른 센티넬이 계속해서 역할을 수행한다.

     

    센티넬은 장애 발생의 오탐을 줄이기 위해 쿼럼 개념을 사용하는데, 쿼럼은 마스터가 비정상 동작을 하는것에 동의해야하는 센티넬 수로

    쿼럼을 만족하는 경우 페일오버를 시작한다. (센티넬 수가 3일때 쿼럼은 2로 설정)

     

    복제본을 마스터로 승격하는 경우는 다음과 같은 우선순위로 승격된다. (복제본이 여러개인 경우)

    1. redis.conf파일에 명시된  replica-priority가 낮은 복제본

    2. 마스터로부터 더 많은 데이터를 수신한 복제본

    3. 2번 조건까지 동일하다면, runId가 사전순으로 작은 복제본 (특별한 이유는 없음)

     

     

    스플릿 브레인 현상(split brain)

     

    네트워크 파티션 이슈로 인해 분산 환경의 데이터 저장소가 끊어지고, 끊긴 두 부분이 각각을 정상 서비스로 인식하는 현상

     

    그룹 A (센티넬A + 마스터노드) 과 그룹 B(센티넬B, C + 복제본 노드) 가 연결이 단절되었을 경우

    그룹 B의 복제본 노드가 마스터로 승격되어 센티넬B,C에서는 해당 복제본 노드로 마스터로 연결하게 해주는데,

    그룹A를 접속한 클라이언트와 그룹B에 접속한 클라이언트가 서로 다른 정보를 입력하고, 조회하는 현상이 발생하게 된다.

     

    만약 네트워크가 복구된다면 센티넬 B,C에 의해 기존 마스터노드는 복제본노드의 복제본으로 다운된다.

    이렇게 됨으로써 마스터 노드에 입력되었던 데이터들이 유실 될 수 있다.

     

     

    클러스터

    레디스 운영중 이빅션(eviction)이 자주 발생한다면 서버 메모리를 증가시키는 스케일 업을 고려할 수 있다.

    그러나 레디스는 싱글 스레드로 동작하기 때문에 처리량을 증가시키고자 CPU성능을 아무리 높힌다 해도

    한계가 있다.

     

    이를 해결하고자 레디스는 클러스터 모드를 지원한다.

    레디스 클러스터 모드는 분산처리, 복제, 자동 페일오버 기능 모두 사용할 수 있다.

     

    데이터 샤딩

    데이터 저장소를 수평 확장하며 여러 서버 간에 데이터를 분할하는 데이터베이스 아키텍처 패턴을 샤딩이라고 한다.

    레디스 클러스터 기능을 사용하면 마스터를 최대 1000개까지 확장가능하다.

     

    클러스터모드에서는 키를 이용해 샤딩되며, 하나의 키는 항상 하나의 마스터 노드에 매핑된다.

    모든 노드는 키가 저장되어야할 노드를 알고 있기 때문에 클라이언트가 다른 노드에 데이터를 쓰거나 읽으려고하면

    해당 키가 할당된 마스터노드로 연결을 리디렉션 한다.

     

    클러스터는 모든 노드가 TCP연결을 사용해 다른 노드와 연결되어 있는 풀 메쉬 토폴로지 형태이다.

     

    클러스는 해시슬롯을 이용해서 모든 데이터를 저장한다. 총 16384개가 있으며, 각 마스터 노드들은 해당 해시슬롯들을 나눠 가진다.

    해시슬롯을 나누는것은 해시 함수를 이용하는데, 데이터 저장시 뿐만아니라 읽을 때도 해당 해시함수를 이용해서

    커맨드를 처리할 마스터 노드를 찾아간다.

     

    해시태그

    위에서 처리되는 해시슬롯에 의해 MGET과 같은 여러 키의 접근할때 문제가 발생할 수 있다.

    해시슬롯1과 해시슬롯16383은 다른 마스터노드를 가지고 있기 때문이다.

     

    이를 방지하기 위해 해시태그를 이용해 같은 해시슬롯을 경유하기 위해 다음과 같이 키에 대괄호를 사용하여 해당 괄호안의 값으로

    해시할 수 있게 처리한다.

    order:{4433}:payment

    order:{4433}:option

    위 두개의 키는 4433이라는 같은 값을 가지고 있기 때문에 같은 해시슬롯에 저장되는 것을 보장한다.

    {order:4433}:payment

    {order:4433}:option 과 같이 {부터 }까지의 값들이 해싱된다.

     

    클러스터 모드에서 각 노드의 감시는 레디스 노드가 서로 감시하는 구조이다.

    모든 노드는 클러스터 버스를 통해서 통신하며 노드 인스턴스 문제가 발생할시에는 자동으로 클러스터 구조를 재구성한다.

    재구성은 총 두 가지이며,
    마스터 노드가 장애를 발생했을 때 복제본을 마스터로 승격시키는 자동 페일오버

    잉여 복제본을 다른 마스터에 연결시키는 복제본 마이그레이션이 있다.

     

    자동 페일오버시에 주의할점이 하나 있다. cluster-require-full-coverage가 기본값이 yes이기 때문에

    만약 하나의 마스터노드에서 한 번 이상의 장애가 발생하여 해당 마스터 노드가 정상상태가 아니게 될 경우

    전체 클러스터를 사용할 수 없게 된다. (데이터 정합성 때문)

    만약, 최대한 이를 방지하기 위해서는 다른 노드에 잉여 복제본을 추가해두는 것이 좋다.

     

     

    복제본을 이용하여 읽기 성능 향상 

    경우에 따라 복제본 노드를 읽기전용으로 사용하고 싶을대가 있는데, 복제본으로 이어지는 커넥션을 READONLY 모드로

    변경하여 클라이언트가 복제본 노드를 직접 읽을 수 있게 한다.

    서버에 접속후 readonly 커맨드를 입력하면 해당 모드로 변경된다.

     

     

    하트비트 패킷

    클러스터 노드들은 서로의 상태를 확인하기 위해  PING, PONG 패킷을 주고 받는데 이 두 패킷을 합쳐 하트비트 패킷이라 한다.

    패킷 헤더에는 노드ID, 현재 에포크/ 구성 에포크(분산환경 일관성 유지 정보), 노드 플래그(마스터,복제본 유무),

    클러스터 포트(노드간 커뮤니케이션을 위한 포트), 클러스터 상태, 마스터 노드ID(복제본인 경우)

    가 있으며 추가적으로 가십 섹션이 추가되는데,

    가십섹션에는 발신하는 노드가 알고 있는 클러스터 내의 다른 노드 정보를 나타낸다. (랜덤한 몇개의 노드만)

     

    서로 에포크 값을 확인해 수신받은 에포크값이 알고있는 에포크값보다 작을 경우 이 값을 업데이트 해준다.

    이 값은 클러스터 상태 변경시와 페일오버가 발생할 때 동의를 구하기 위해 사용된다.

     

    해시슬롯 구성 전파는 하트비트 패킷, 업데이트 메시지를 통해 전파되는데

    에포크 값의 변경이 확인되면 신규 에포크의 구성 정보를 포함한 업데이트 메시지를 노드에 보내 하트비트 패킷을

    보낸 노드의 해시슬롯 구성을 업데이트한다.

     

     

    노드 핸드셰이크

    한 노드가 클러스터에 합류하기 위해서는 CLUSTER MEET 커맨드를 다른 노드에 보낸다.

    해당 커맨드를 수신한 노드는 자신이 알고 있는 다른 노드들에게 전파하고, 이 정보를 수신한 노드가 신규 합류한 노드를 모르는

    상태라면 해당 노드와 CLUSTER MEET를 통해 신규연결을 맺으며, 이러한 방식으로 모든 노드들은 풀 메쉬 연결을 하게 된다.

     

     

     

    클라이언트 관리

     

    파이프라이닝

    레디스 서버와 클라이언트는 네트워크를 통해 연결되어있으며, 요청과 응답사이 왕복시간(RTT)은 성능에 큰 영향을 미친다.

    파이프라이닝은 클라이언트가 연속적인 여러 개의 커맨드를 레디스 서버에 보낼 수 있도록 하는 기능이다.

    이를 이용해 네트워크 소요시간을 줄여 레디스 성능을 크게 향상시킬 수 있다.

     

    다음과 같은 방법으로 줄바꿈을 이용해 동시에 실행할 여러개 커맨드를 한번에 보낼 수 있다.

    (printf "PING\r\n PING\r\n PING\r\n"; sleep 1) | nc localhost 6379

     

    파이프라이닝을 사용할 때에도 주의할 점이 있는데, 한번에 너무 많은 쿼리를 파이프라인을 이용해 레디스에 보내면

    네트워크 대역폭 한계로 인해 속도 저하가 일어 날 수 있거나 클라이언트 쿼리 버퍼 제한에 걸려 오류가 발생할 수 있다.

     

    이에 따라 명령을 일정한 개수로 나누어 배치 형태로 서버에 보내는 것이 좋다.

    배치 사이즈의 경우는 충분한 테스트 이후 결정하도록 하는 것이 좋다.

     

    다만, 파이프라인으로 들어온 명령을 처리할 때 레디스는 원자성을 보장해주지 않는다.

    클라이언트 1,2가 동시에 같은 데이터에 접근 및 처리하려고할 때, 트랜잭션과 같이 처음 접근할때의 데이터 정보를 

    바탕으로 읽어오거나 수정하거나  할 수 없는 것이다.

     

    또한, 파이프라이닝을 통한 여러 커맨드 실행시 일부 커맨드가 오류가 발생하더라도 전체 롤백이 진행되지 않는다.

     

     

    클라이언트 사이드 캐싱

    쿼리가 들어올때마다 레디스 서버에 데이터를 요청하는 방법이 아닌

    클라이언트 측에서 데이터를 캐싱하고 필요할 때 해당 데이터를 반환하는 방법이 있다.

     

    SNS게시글과 같이 수정이 잘 이뤄나지 않는 경우 사용을 검토해볼만 하다.

    그러나, 업데이트된 데이터를 처리하는 방법을 고려해야하는데 이를 트래킹이라고 부르며

    두 가지 모드를 지원한다.

    기본모드 - 레디스 서버가 클라이언트가 엑세스한 키를 기억해서, 동일한 키가 수정될 때 무효 메시지를 전송한다.

    브로드캐스팅 모드 - 레디스 서버가 모든 키에 대한 엑세스를 기억하려고 시도하지 않으며, 특정 프리픽스에 대해 접근한

    클라이언트만 기억한다.  클라이언트는 특정 프리픽스를 가진 키를 기억해야하며, 자신이 소유하지 않은 키라하더라도

    해당 프리픽스와 일치하는 키가 변경될 때마다 변경메시지를 수신하는 단점이 존재한다.

     

     

     

    레디스 운영하기

    성능 저하 요소 알아보기

    maxmemory-policy - 레디스가 메모리 한계에 도달했을 때 어떤 키를 제거할지 결정하는 설정값

    기본값은 noevicition이며, 메모리가 가득찼을때 임의 데이터 삭제하지 않고 데이터를 더 저장할 수없다는 에러 발생.

    데이터 일부가 임의로 삭제되더라도 계속해서 새로운 입력을 받아들일 수 있도록 설정하고 싶다면 allkeys-lru로 설정한다.

     

    stop-writes-on-bgsave-error - RDB 스냅샷이 정상적으로 저장되지 않을 때 레디스로의 모든 쓰기 작업 중단하는 역할

    그러나 디스크 문제가 발생해도 신속한 감지가 가능하고, 쓰기작업을 중단하고 싶지 않다면 이 설정을 비활성화한다.

     

    자동 백업 옵션 - RDB와 AOF을 이용해 백업 파일을 생성하는 작업은 운영중인 레디스 인스턴스에 큰 부하가 간다.

    백업이 포그라운드 인 경우 작업 실핼중 다른 작업들은 대기해야하여 성능에 영향이 가며,

    백그라운드 인 경우 COW 방식으로 작동하기 때문에 메모리 사용량이 최대 2배까지 증가가능하다.

    따라서 백업작업은 의도한 시간에 의도한 레디스 인스턴스에서 실행될 수 있도록 하는 것이 좋다.

     

    오래 걸리는 커맨드들 

    레디스는 싱글 스레드로 동작하기 때문에, 하나의 커맨드로 오래수행되면 나머지 클아이언트들은 그 커맨가 종료될 때까지

    대기하게 된다. 레디스에서는 O(N) 이상의 시간복잡도를 갖는 커맨드 사용할 지양해야한다.

    O(N)이상의 시간복잡도를 갖는 커맨드들은 다음과 같다.

    [키스페이스 커맨드]

    KEYS - 모든 키에 한번 씩 접근하므로 SCAN을 사용하도록 권장한다. 

    FLUSHALL - 모든 키를 삭제한 후 OK를 반환하기 때문에 O(N)을 가진다. FLUSHALL 과 ASYNC 옵션을 같이 사용하여 백그라운드 작업으로 할 수 있도록 처리한다. (신규 생성키는 삭제하지 않는다.)

    FLUSHDB - 위와 동일하다.

     

    [자료구조 공통 커맨드]

    자료 구조 공통 커맨드의 시간 복잡도에서 N은 자료 구조 내부의 아이템 개수를 의미하는데, 하나의 자료 구조에 속한 아이템의 개수에

    비례해 수행시간이 증가하므로, 하나의 키 안에 아이템을 많이 저장하는 것은 바람직하지 않다.

     

    DEL - String키를 삭제한다면 O(1)의 시간복잡도 이지만, list, set과 같은 자료구조 삭제할 경우 O(N)의 시간복잡도를 가진다.

    자료구조안에 데이터가 너무 많다면 UNLINK를 통해 백그라운드로 삭제하는 것이 좋다.

    SORT, SORT_RO 등등..

     

    [기타 자료 구조 관련 커맨드]

    그외 시간복잡도는 해당 사이트를 참고해보자 (레디스 공식 사이트)

    HGET | Redis

     

     

    레디스에도 트랜잭션을 사용할 수 있다.

    이 기능을 사용하면 원자적으로 수행되어야하는 커맨드를 보장할 수 있어 유리하지만, 레디스는 싱글스레드로 동작하기 때문에

    사용에 주의를 하여야한다. MULTI로 트랜잭션 시작을 알리고 ESEC 커맨드를 입력하면 트랜잭션을 원자적으로 실행하고

    성공하면 결과를 반환 오류가 발생하면 모든 커맨드를 롤백한다.

     

     

    루아 스크립트

    레디스는 루아 스크립트 실행 기능을 내장해 데이터 조작, 계산과 같은 작업을 처리할 수 있게 해준다.

    트랜잭션과 유사한 원자성을 가지지만, 일부 명령어가 실패해도 다음 명령어로 진행되며 롤백이 발생하지 않는다.

     

    트랜잭션과 루아스크립트를 잘 이용하면 여러 레디스 커맨드를 원자적으로 실행하여 데이터 일관성을 유지하고

    루아스크립트를 활용하면 복잡한 데이터 로직 구현이 가능하며 네트워크 부하를 줄일 수도 있다.

    트랜잭션과 루아스크립트 모두 사용중에 다른 클라이언트 커맨드가 모두 대기하게 되므로 주의가 필요하다.

     

     

    has-get / has-del 패턴

    레디스에서 데이터 조회 또는 삭제 시 EXIST 커맨드를 사용해서 데이터 존재 확인 후 처리하는 has-get, has - del 패턴은

    지양하는게 좋다. (네트워크 부하 발생) 그냥 바로 get을 통해 가져오면 2배에 가까운 성능 차이를 낼 수 있다.

     

    특정 프리픽스 가진 키 삭제

    레디스에서 특정 프리픽스를 가진 키를 삭제해야할때가 있는데 (잘못된 배포 혹은 정리) 
    이때 애플리케이션 -> 레디스 스캔 -> 애플리케이션 ->  레디스  삭제 -> 애플리케이션 -> 레디스  스캔 -> 애플리케이션 방식보다

    루아 스크립트를 활용하여 삭제하는 방법도 좋다. 그렇게되면 단순히 애플리케이션 -> 레디스 스캔, 삭제 (순회) 로 끝나게된다.

    예시 스크립트는 다음과 같다.

    ===스크립트
    local cursor = "0"
    local count = 0
    repeat
        local result = redis.call("SCAN", cursor, "MATCH", ARGV[1])
        cursor = result[1]
        local keys = result[2]
        for _, key in ipairs(keys) do
            redis.call("UNLINK", key)
    	count = count + 1
        end
    until cursor == "0"
    return count
    ==스크립트 실행 명령어
    
    == 해당 파일들을 읽어오자
    redis-cli -p {port} --eval /directory/delete.lua 0 , "{prefix}"

     

     

     

     

    슬로우 로그

    레디스는 슬로우 로그 즉 실행 속도가 느린 커맨드를 기록하는 로그가 있다.

    SLOWLOG GET 명령어로 가져올 수 있으며, 명령이 실행된 시간정보, 명령이 실행되는데 소요된시간, 느리게 수행됬던 커맨드

    등을 확인할 수 있다.

     

     

    그래프 지표

    레디스를 안정적으로 운영하기 위해 각 메트릭 지표를 적절하게 모니터링 해야하는 것이 중요하다.

    각 요소들은 다음과 같다.

     

    1) CPU
    레디스는 커맨드 실행동안 단일 스레드로 동작하지만, 백업 파일 저장 및 UNLINK와 같은 백그라운드 작업시에는

    다른 CPU 활용이 가능하다. 

    또한, 집합 자료 구조의 카디널리티가 높을수록, 단일 Hash에 저장되는 항목이 너무 많아질 수록 부하 발생이 높아진다.

     

    2) 메모리

    레디스 메모리 사용의 핵심 지표는 used_memory로 현재 할당중인 메모리를 나타낸다.

    DatabaseMemoryUsagePercentage(메모리 사용 백분율) 메트릭을 이용해서 메모리가 차기 전에 모니터링을 해주자.

    이빅션 발생은 CPU 사용량 증가로 이어질 수 있기 때문이다.

     

    논리적 메모리에 비해 물리적 사용 메모리가 커지면 메모리 단편화 문제가 발생할 수 있는데,

    키가 급격이 몰렸다가 삭제되는 경우, 만료시간으로 인해 한꺼번에 이빅션이 발생하는 경우 발생할 수 있다.

     

    ※메모리 단편화

    메모리가 사용할 수 없는 블록으로 분할되어 메모리 효율성기 감소하는 컴퓨터 메모리 관리 문제이다.

     

    내부 단편화 - 할당된 메모리 블록에 사용되지 않은 공간이 포함됨.

    외부 단편화 - 사용 가능한 메모리가 연속되지 않고 분산됨.

    해결방안: 메모리 압축 , 슬랩할당자 사용, 가비지 수집등 

     

     

    3) 네트워크

    네트워크I/O 지표를 확인해 트래픽을 모니터링하여 예상치 초과되지 않는지 확인해야한다. (병목현상 확인)

    레디스는 더 많은 명령을 처리할 수 있지만 네트워크로 인해 낮은 처리량으로 내려가는 수가 있다.

     

    읽기작업이 병목인 경우 -> 복제본을 사용하고 있는지, 복제본 하나로는 네트워크 감당이 안되는 경우 하나 더 추가

    쓰기작업 명목인 경우 -> 레디스 서버 사양 업그레이드 혹은 네트워크 대역폭이 높은 환경으로 마이그레이션, 클러스터 모드 활용하여

                                            쓰기 분산


    4) 커넥션

    레디스에서 클라이언트 연결 수 확인은 필요하다. 갑작스럽게 연결 수 증가하는 경우

    어플리케이션 문제 혹은 연결종류가 안된 경우 이럴수가 있는데 tcp-keepalive 설정을 활용하여 연결 문제를 방지할 수 있다.

    일반적인 DB와 마찬가지로 커넥션 풀링을 사용하여 기존 새로운 연결을 추가하는 작업의 비용을 줄이는 것이 좋다.

     

    5) 복제

    마스터 노드는 하나 이상의 복제본이 있는 경우 데이터 명령 스트림을 전송하게 되는데,

    복제 지연이 발생하는지 여부를 확인하는것이 매우 중요하다. 해당 사항이 발생하는 경우 전체 동기화를 요청 처리하게 되어

    마스터 노드에서 스냅숏을 생성하기 때문에 성능저하가 발생할 수 있다.

     

     

     

    해당 게시글은 

    '개발자를 위한 레디스' 책을 읽고 작성하였습니다.

     

     

     

Designed by Tistory.