기록하는 습관

[Research] Redis 조사 본문

개발/Research

[Research] Redis 조사

로그뉴 2022. 7. 12. 16:48

0. redis 자료 구조

  • Hashes
    • Every hash can store up to 232 - 1 field-value pairs (more than 4 billion). → key 1개 당, 약 42억개의 field-value 저장 가능.
  • Sets
    • The max number of members in a set is 232 - 1 (4294967295, more than 4 billion of members per set). → key 1개 당, 약 42억개의 member 저장 가능.
  • 참고

 

1. bulk Insert

MySQL에서는 bulk insert를 지원하지만 Redis에서는 따로 지원하지 않음.


방법 개념 장점 단점 비고
Redis pipelining
  • 매 번 응답을 받지 않아도 redis 서버에 여러 명령어를 한 번에 요청할 수 있고, 마지막으로 한 번에 응답을 읽을 수 있게 하는 기능
  • Redis에서 지원하는 pipeline API인 executePipelined 메소드를 이용해 redis에 연결을 한 후, 모든 원소들을 push한 뒤 연결을 닫음.
  • 레디스 파이프라이닝 방식을 통해 RTT 비용이 수십만번 발생하지 않고, 단 한번만 RTT 비용이 발생
  • 실제로 Redis 서버에서 초당 수행할 수 있는 작업의 수를 향상시킬 수도 있음.
    • 일반적으로 하나의 read()로 여러 명령을 읽고, 하나의 write()로 여러 응답을 한번에 전달하기 때문에 context switch 수가 더 적게 일어나기 때문
  • 클라이언트가 파이프라이닝을 사용하여 명령을 보내는 동안 서버는 메모리를 사용하여 응답을 queue에 넣음.
  • 파이프라이닝으로 많은 명령을 보내야할 경우에는 메모리에 부하가 일어날 수 있음.
    • 적당한 수의 batch 방식으로 처리하는 것이 좋음.
  • 순서대로 응답이 꼭 와야함.
    • 만약 먼저 보낸 요청의 응답이 지연될 경우 뒤의 응답은 대기하게 되는 HOB 문제가 있을 수 있음.
Luke protocol
  • row format으로 Redis protocol을 담은 text file을 import 하는 방식
  • ex) 10억개의 key들이 key-value 형태로 insert 되어야 한다면 아래와 같은 Redis protocol 형식을 따르는 파일을 만들면 됨.
    •  
  • pipe mode라고 불리는 새로운 모드의 redis-cli utility를 제공.
    •  
  • 아래와 같은 output 생산
    •  
  • pipelining을 사용하는 것보다 성능이 좋음.
  • 자료가 정말 많이 없다.
  • Ruby 코드는 있지만, Java client 코드는 찾을 수 없음.
Lua Script 활용
  • Redis는 Lua scripts로 사용자 지정 스크립트를 작성하고 실행할 수 있는 스크립팅 기능이 있음.
  • 사용자는 빠르게 실행되는 스크립트의 형태로 Redis 자체에 기능을 추가 할 수 있음
  • 초기화가 매우 빠르기 때문에 스크립트가 cacheDB인 Redis의 속도를 저하시키지 않으면서 데이터에 대한 작업을 수행할 수 있다.
  • Redis에서 실행되는 루아스크립트는 원자적으로 처리된다 (사용자가 작성한 스크립트가 실행되는 동안 다른 레디스 명령어는 실행되지 않는다)

 

참고

 

2. bulk Read

  • keys
    • Redis에 모든 키 값들을 읽어올 수 있는 명령어인 Keys가 있다. 이 명령어는 시간 복잡도가 O(n)이다. 
    • keys 명령어는 모든 key 를 다 찾을 때 까지 blocking 하기 때문에 다른 client 들의 명령어를 수행 하지 못하고 timeout 발생 할 확률이 높음.
  • scan
    • count 값을 정하여 그 count 값만큼 결과를 가져오고 offset 값을 반환. (기본 count값은 10)
      • 레디스에 읽어야하는 키값이 총 10000개라고 치고 count가 10개이면 1000번에 나눠서 이 키를 읽는 것.
      • 커서가 0이 될 때 까지 while문을 돌며 키를 나눠서 읽음.

 

결론

  • zscan 명령어 사용. (sorted set 안에서 key를 가져오는 명령)
  • count는 n으로 진행 (측정 후 n 결정예정)

 

참고

 

3. bulk Delete

명령어 특징 성능 (삭제 시간)
DEL
  • Redis 1.0.0 부터 사용 가능
  • sync로 삭제 진행
  • 논리적 처리 소요시간은 O(N). (N은 삭제할 key의 개수)
44,217 microsecond
UNLINK
  • Redis 4.0에서 추가
  • 비동기(async)로 별도 쓰레드에서 백그라운드로 실행
  • collection 데이터 타입에 member 수가 많은 경우 DEL 보다 훨씬 빠르게 처리됨.
  • member수가 64개 이하인 경우는 DEL과 마찬가지로 sync로 삭제.
  • 논리적 처리 소요시간은 O(1). 하지만 별도 쓰레드에서 값을 삭제(메모리 해제)하는데 소요시간은 O(N)이다. (N은 멤버수)
25 microsecond

성능

  • key 1개에 약 10만개의 member 넣고 slowlog로 처리 시간 측정.
  • 값이 차지하는 메모리는 약 12MB

 

결론

  • unlink 사용
  • 삭제 시간이 UNLINK가 약 1700배 빠르다.
  • 구조 상, key(groupSeq) 내에 member 수가 많기 때문에 unlink를 사용할 예정.
  • → RENAME을 사용하면 자동으로 UNLINK 사용

 

 참고

  • flushdb: 연결된 현재 데이터베이스의 키들을 삭제
  • flushall: 모든 데이터베이스의 키들을 삭제
    • 시간복잡도가 O(n)이기 때문에, 성능 지연 현상을 초래할 수 있음. 운영환경에서 부득이하게 사용할 경우 async 옵션을 적용하여 반영.
      • backgroud로 삭제하기 때문에 응답속도가 매우 빠르고, 지연을 방지할 수 있음.
      • String key 100만개를 flush 할 경우, Sync는 약 1초 / Async는 1ms 미만내에 명령이 수행됨.
  • Docs: unlink

 

4. 무중단 데이터 교체

방법 상세 설명 삭제 정책 장점 단점
교체
  • 2개의 key (ex. keyA, keyB)를 두고, 데이터 적재가 다 되면 교체하는 방식
  • 데이터 업데이트 시점에, 기존에 사용중인 key는 그대로 두고 교체할 데이터를 담을 key를 생성한다.
    • keyA가 redis에 있으면 keyB로 생성
    • keyB가 redis에 있으면 keyA로 생성
  • 데이터 적재가 성공하면 기존 key는 삭제
  • redis에 적재되는 data의 size를 최소화 할 수 있음.
  • 적재 실패 되더라도 데이터가 아예 없는 현상은 없으며, 다만 업데이트 전인 기존 데이터로 반환 가능
  • 2개의 key로 번갈아가며 요청해야 함.
version check
  • 데이터 업데이트가 되어 redis에 key 생성을 할 때마다 날짜(ex. 2022.02.09) 등으로 version 표시를 한다.
  • 주기적으로 삭제 (ex. 일주일에 한 번 ..)
 
  • 적재 실패 시 데이터가 아예 없는 현상이 발생.
rename
  • 업데이트 데이터 key를 key_new로 생성한 뒤 적재한다.
  • 적재가 완료되면 RENAME 명령어를 사용해 key_new → key로 교체한다.
    • 변경하는 key가 이미 존재하면 기존 key는 삭제된다.
  • Rename 하는 즉시 예전 data는 삭제되고 최신 data로 유지
  • Redis에는 같은 key로 항상 최신 data 유지 가능
  • 적재 실패 되더라도 rename 전까지는 기존 데이터로 반환 가능
 

 

5. Memory 구성

  • 별도로 Redis의 설정파일인 redis.conf의 max-memory 설정을 하지 않을 경우 0으로 구성되고, OS에서 사용 가능한 Max Memory까지 확장하여 사용하게 됨.
    • Redis 공식 문서에 따르면 32bit 시스템에서는 메모리 제한 사용량 기본값이 3GB로 설정되어 있지만, 64bit 시스템에서는 0. → 64bit 경우 가상메모리까지 사용하게 됨.
    • 0인 경우, eviction policy 적용하지 않음.
  • 이는 특정 인스턴스의 장애가 동일 노드의 다른 Redis 인스턴스로의 전파를 일으킬 수 있음.
    • 특히 물리 메모리를 모두 사용하는 상황이 발생할 경우 스왑 메모리 영역을 사용하게 되는데 이때 Redis의 성능은 상황에 따라 수백배까지 느려질 수 있음.
    • 따라서 반드시 max-memory를 설정하여 메모리 사용계획과 저장되는 데이터 LifeCycle을 관리하는 정책을 세워야 한다.

 

6. 기타

1) 저장 만료 주기

  • max-memory만큼 메모리를 사용하게 되면, 메모리 정책에 따라 과거에 만들어진 키들이 삭제된다.
  • 이와 같은 문제점을 개선하기 위해 Redis 서버는 4.0 버전부터 LRU(Least Recently Used: 가장 최근에 사용되지 않은 것) 알고리즘과 LFU(Least-Frequently-Used: 가장 적게 사용된 것) 알고리즘을 제공하고 있다.
  • Redis가 Export 된 데이터를 삭제하는 정책은 내부적으로 active와 passive 두가지 방법을 사용한다.
    • active: 요청된 key가 호출된 시점에 expired 여부를 검증하여 삭제하는 방법
    • passive: random으로 key 100개만 반복적으로 스캔해서 지우는 방식이다.
  • expired time이 지난 후 클라이언트에 의해서 접근되지 않는 데이터는 active 방식으로 인해서 지워지지 않고 passive 방식으로 지워져야 하는데, 마찬가지로 전체를 scan하는 것이 아니기 때문에 Redis에는 항상 Expired 되었으나 지워지지 않는 Garbage 데이터가 존재할 수 있다는 점을 명심해야 한다.

 

2) Eviction 정책

  • noeviction: 캐시를 지우지 않는 정책이다. 메모리가 maxmemory 이상을 사용하게 되면 error를 발생시킨다.
  • allkey: 각 정책에 따라 모든 키를 대상으로 정리한다.
  • volatile: 각 정책에 따라 EXPIRE SET에 있는 키들을 대상으로 정리한다.
  • LRU
    • allkeys-lru: LRU 알고리즘 기반으로 키를 삭제한다.
    • volatile-lru: LRU 알고리즘 기반으로 키를 삭제한다.
  • Random
    • allkey-random: 랜덤하게 키를 삭제한다.
    • volatile-random: 랜덤하게 키를 삭제한다.
  • LFU
    • allkeys-lfu: LFU 알고리즘 기반으로 키를 삭제한다.
    • volatile-lfu: LFU 알고리즘 기반으로 키를 삭제한다.
  • TTL
    • volatile-ttl: TTL이 짧은 순으로 삭제한다.

 

3) LazyFree 정책

  1. lazyfree-lazy-eviction : yes
    1. del → unlink 사용
  2. lazyfree-lazy-expire : yes
    1. del → unlink 사용
  3. lazyfree-lazy-server-del: yes
    1. set 또는 rename 명령어를 실행하면 내부적으로 DEL 명령어가 실행되면서 블록킹 현상 발생. 이를 막기 위해서는 yes 설정으로 하여 UNLINK로 수행되게 한다.
  4. slave-lazy-flush
    1. yes 설정시, 빠른 동기화 진행.

 

3) Memory Fragment

maxmemory 대비 사용된 메모리 비율이 낮더라도 메모리 조각화로 인해 Redis 인스턴스의 메모리가 부족해질 수 있다. Redis 버전 4.0 이상에서는 activedefrag 구성을 제공한다.

activedefrag를 yes로 설정하면 CPU 사용량이 증가하지만 메모리 조각화를 완화하여 메모리 부족 문제를 해결할 수 있다.

 

참고

 

6. Collection 활용

  • Redis와 같은 캐시 솔루션은 단일 데이터 조회시 hash key를 기반으로 조회하기 때문에 대부분 조회 성능은 빠르게 처리된다.
  • 다만, Redis에는 Memcached와 달리 Collection Type을 제공한다.
  • Collection은 대량 데이터 조회에 보다 유용하게 활용될 수 있지만, 단일 Collection에 데이터 100만건을 넣으면 10초, 1천만건을 넣으면 100초씩 걸리는 식으로 시간이 늘어나기 때문에 Collection 당 저장해야 하는 데이터 사이즈는 1만건 미만으로 관리하는 것이 좋다.

'개발 > Research' 카테고리의 다른 글

[Research] Redis 캐싱 전략  (0) 2022.07.13
[Research] MySQL Batch Insert  (0) 2022.07.12
[Research] Springfox Swagger2 -> Springdoc OpenAPI 3  (0) 2022.07.12
tomcat vs. hikaricp  (0) 2022.07.12
배치, 스케줄러  (0) 2022.07.12
Comments