반응형
레디스
- nosql
- not only sql
- 비정형화된 데이터 사용 -> 유연성, 확장성, 실시간 응답
- 레디스는 key-value형태이고
- 그래프, 다큐먼트, 콜롬배이스 형태도 있음
- in memeory 데이터베이스 -> 모든 데이터가 메모리 위에 있어 디스크까지 가서 받아오지 않아도 돼서 빠름
- 단순성 - 다양한 자료구조 제공(10가지 이상), 커맨드 처리, 이벤트 루프로 동작(싱글스레드; 한 번에 하나의 작업만 함)
- 클라의 처리가 빠르고 단순
- 확장성/고가용성
- 클러스터 -> 샤딩을 위한 기술
- 여러개 서버에 나눠서 저장
MSA에서 레디스?
- 데이터 저장소(권장하지 않음) 쓸 수는 있다(10가지의 자료구조 제공; 개발의 편리성) -> 데이터의 영구저장을 할 수는 있다.(파일)
- 캐시: 최소한의 리소스로 막대한 처리; 자체 HA 기능 제공
- 메시지 브로커: pub/sub, event queue, stream
redis 자료구조
- string(모든 종류의 문자열 저장 가능; 이진 데이터 포함)
- set/get으로
- set hello world -> key: hello, value: world
- get hello -> world
- 이미지, html 도 가능
- 최대 512MB
- string -> string
- 키도 최대 512MB
- command
- incr(1씩 증가) / incrby(지정한 수만큼 증가; 락 안 잡고도 증가시킬 수; mysql은 락을 잡음)
- getset: 조회하고 결과 나오고 치환
- 옵션: xx/nx
- xx: 키가 있을 때만 저장(없으면 안 해; 없으면 nil 반환)
- nx: 키가 없을 때만 저장(없으면 해; 있으면 nil반환)
- set/get으로
- list 순서를 가지는 문자열의 목록
- 리스트 안에 인덱스 있음 reverse index도 있음; 추가되면 인덱스는 알아서 재정리됨
- command
- rpush rpop lpush lpop(return 있음)
- pop은 조회하고 삭제
- ltrim 원하는 인덱스가 아니면 다 지움(void)
- linsert(어디 앞에 넣는다) / lset(지정한 인덱스에 치환) 중간에 넣는 작업
- 중간에 넣을 때는 O(n)이라 안 빠름
- rpush rpop lpush lpop(return 있음)
- command
- 리스트 안에 인덱스 있음 reverse index도 있음; 추가되면 인덱스는 알아서 재정리됨
- hash
- hset(데이터 넣고 1반환)
- field-value 쌍을 가진 아이템의 집합; 데이터 페어를 무제한으로 늘릴 수 있음
- rdbms의 테이블 데이터와 가장 유사 -> 변환 가능
- hrandfield 랜덤 / 양수를 주면 겹치지 않게; 음수는 겹쳐도 되게
- set
- sadd
- 중복 안됨
- 정렬되지 않은 문자열
- srandmember 랜덤
- 객체 간 관계 계산(교/차/합/원소개수)
- sunion 합
- sinter 교
- sdiffer 차
- sortedSet
- 스코어 값으로 정렬되는 고유한 문자열의 집합(정렬되어 있음; 매번 정렬할 필요 없음)
- 모든 아이템은 value-score 쌍
- 같은 스코어라면 사전순 정렬
- value는 중복되지 않음
- 인덱스로 접근 가능(O(logn))
- -인덱스 사용 가능 나부터 -1
- command
- zadd 키 스코어 밸류
- 옵션
- xx/nx/lt/gt / withscores(밸류랑 스코어랑 같이 조회)/ rev(역순)/byscore
- zrange 인덱스 기반으로 데이터 조회
- zrandmember 랜덤 뽑기
- bylex(사전순)
- zrange mySortedSet (b (f bylex
- (: 포함하지 않고
- (b 초과
- [: 포함하고
- zrange mySortedSet (b (f bylex
- bitmap
- 확장된 string형태의 자료구조
- 2^32개의 비트를 가질 수
- 저장공간을 줄일 수
- setbit, getbit, bitcount(1 개수 카운트)
- hyperloglog
- 집합의 카디널리티를 추정하는 자료구조
- 대용량(로그)의 개수 카운팅; 중복제거
- 12KB 고정용량(엄청 큰 데이터도), 1% 미만의 오차
- pfadd, pfcount, pfmerge
- geospatial
- 위도, 경도 쌍; 지구가 완벽한 '구'라는 전제; 오차가 약간 있음
- 내부적으로는 sortedSet
- 특정 위치를 기준으로 검색할 수
- 반경 몇 미터 안 검색 가능
- 사각형으로도 조회가능
- stream
- 아파치 카프카에서 영향
- append only 한 key-value형식의 자료구조
- 메시지 브로커에 사용
key를 관리하는 법
- 자동으로 생성되고 삭제됨
- 키가 존재하지 않을 때 아이템을 넣으면 삽입 전 빈 자료구조 생성
- 모든 아이템을 삭제하면 키도 삭제됨(스트림 예외)
- 키가 존재하지 않을 때
- 키를 삭제하는 명령을 하거나;
- 키에 저장된 아이템을 삭제하거나;
- 자료구조 크기를 조회하는 read only 커맨드를 수행하면
- -> 키가 없지만 있는 척; 있고 아이템이 없는 것처럼 동작
- command
- 조회
- keys 명령어는 위험; 다른 요청 blocking 됨;
- scan 커맨드로 대체가능(여러 번 끊어서 데이터 가져옴)
- 다음에 조회할 커서를 알려줌 그걸로 연쇄적으로 조회 가능
- sort, exists, rename/renamenx, copy, type
- 삭제 flushall, del, unlink
- 만료시간 expire, expireat, expiretime, ttl
리더보드
- sorted set: 중복을 허용하지 않고 정렬이 되는 자료구조
- 데이터입력: ZADD daily-score:220817 200 player:286
- 랭킹 합산; ZUNIONSTORE weekly-score:2208-3 3 daily-score:220815 daily-score:220816 daily-score:220817 (integer) 4
- 최대 다섯 개 까지만 저장 하려면
- 검색 기록이 다섯개 이하인 경우 → 삭제 안 함
- 검색 기록이 다섯 개보다 많을 경우 → 인덱스 0 삭제
- 항상 인덱스 -6을 삭제한다면? 오버헤드 감소!
- ZREMRANGEBYRANK 사용
캐시로의 레디스
- 레디스는 빨라, 응답속도를 줄일 수, 자체적인 ha 제공
캐싱 전략
- 읽기 전략: look aside(lazy loading)
- 우선 캐시에 데이터가 있는지 확인; 없으면(cache miss) 디비에서 읽고 캐시에 저장
- 초반에 느릴 수 있음
- 미리(오픈전) 데이터를 넣어주는 작업을 하기도 하는데 이를 cache warming
- 캐시가 없더라도 장애로 이어지지 않음
- 반대로 디비에 몰리면 장애가 날 수도 있음
- 원본과 동일하도록 유지
- 디비 업데이트 칠 때 캐시에도 업데이트해야(write through)
- 우선 캐시에 데이터가 있는지 확인; 없으면(cache miss) 디비에서 읽고 캐시에 저장
- 쓰기 전략(데이터 일관성)
- write through(중요하면 동기방식; 별로 안 중요하면 비동기)
- 항상: redis -> db 업데이트
- 매번 2개 저장
- 쓰기 시간이 듦
- 다시 사용하지 않아도 될 데이터가 이중으로 저장될 수도
- expire time 설정 추천
- cache invalidation
- 디비에 저장할 때 캐시를 삭제
- 찾을 때 디비에서 가져오고 후에 캐시 업데이트
- 불일치는 발생하지 않음
- write behind(나중에 디비에 써; 핫해서 계속 디비 넣기 부하 걸리니; 일정 주기로 동기화)
- 쓰기가 빈번하다면
- 디비에 대량의 write면 디스크 IO -> 성능저하로 이어지기 때문에
- 캐시에 먼저 업데이트하고
- 비동기적으로 디비에 업데이트
- 실시간으로 엄청 정확하지 않아도 될 경우
- 단, 캐시 날아가면,, 끝
- write through(중요하면 동기방식; 별로 안 중요하면 비동기)
레디스를 캐시로 사용할 때 주의할 점
- 자주 사용하는 데이터 일부만 임시로 저장해야(적은 양)
- 적절한 메모리를 유지할 수 있도록 적절히 삭제하고 들어오도록 유지해야 함
- TTL(time to live) 사용은 필수(캐시 유지시간)
- 메모리 꽉 차면 레디스 자체의 max memory policy에 의해 가장 오래된 키부터 삭제
- 쓰기를 막는 설정이 기본이긴 한데 이게 더 장애 유발 가능성 그래서 오래된 키부터 삭제하는 정책
- default는 저장 불가 -> 장애로
- 자주 사용하지 않는 거 삭제하는 방식으로 설정 변경 필요
- 메모리 꽉 차면 레디스 자체의 max memory policy에 의해 가장 오래된 키부터 삭제
- TTL을 너무 작게 하면 cache stampede 현상
- cache miss가 날 경우 조회 시간차로 (db) duplicate read, (redis) duplicate write 발생할 수도
- 캐시로 사용할 경우
- 영구저장소가 아닌 임시라서 캐시의 백업정책 필수는 아님; 비추
- 캐시가 없는 경우 서비스장애로 이어진다면 ha구성 필요
메시지 브로커로서의 레디스
메세징 큐
- 프로듀서 -> -> 컨수머
- 데이터 푸시
- 데이터 영속성: 읽으면 큐에서 데이터 삭제
- 컨수머 추가되면 그 후에 추가된 메시지만 확인가능
- 1:1
이벤트 스트림
- 퍼블리셔 -> -> 섭스크리버(구독자)
- 당겨감
- 데이터 영속성: 저장소별 설정에 따라 특정기간 동안 저장
- 구독자 추가되면 이전부터도 확인가능
- 다:다; 1:다
레디스에서는
- pub/sub
- 최소한의 메시지 기능
- 클러스터에서도 사용가능
- 한번 채널에 전파되면 사라져; 일회성
- 메시지가 잘 갔는지 모름(보관 안 함)
- end-end에서는 별로
- notification 딱 보내고 끝이면 괜찮음
- command
- subscribe / publish
- list를 이용한 메시지 큐
- list.exists
- rpushx 이미 캐싱되어 있는(존재하는) 경우에만 입력(불필요한 리소스 줄일 수)
- event loop
- 폴링: 이벤트 큐에서 확인 -> 핸들러
- 즉각적인 확인 X, 주기별로 체크
- 리스트에서 blocking을 추가하면 큐확인을 기다릴 필요 없음
- brpop / blpop -> 있음 반환; 없으면 새로 올 때까지 기다렸다가 반환
- circular queue: rpoplpush
- stream 자료구조
- 연속적으로 계속 들어오는 데이터 처리
- 써도 사라지지 않고 남아있음
- 카프카처럼 사용가능
- 로그를 저장하기 적절
- append only; 중간에 저장하지 않음
- range 검색가능, tail 가능, 그룹핑 가능
- xadd
- command에 "*" : 시간 millisec을 timestamp로 갖겠다,
데이터 영구적으로 저장하는 법
- 재시작하면 데이터 날아감
- 서버에 파일로 저장함
- AOF: 커맨드 자체 저장; 과정을
- 파일이 커지는 걸 막기 위해 aof는 bgrewriteaof로 파일을 다시 써서 압축
- RDB: 데이터의 최종 스냅샷 저장(메모리 자체); 결과만
- bgsave 커맨드로 새로운 스냅샷 저장
- 이때 백그라운드 프로세스를 생성하기 때문에 메모리 초과로 인한 장애 발생 가능성 매우 큼
- 비추
레디스 HA(high availability)
고가용성, 안정성 = 사용시간/전체시간
- 복제: 마스터 -> 복제본 실시간 복사
- 자동 failover: 마스터 노드에 발생한 장애를 감지해서 연결을 복제본 노드로 리디렉션 하도록; 수동으로 안 해도 됨
replication
- master-replica
- 마스터는 하나; 다중 마스터 불가(지원 안 함)
- 마스터 노드는 rdb파일을 생성하여 리플리카 노드에 전달
- 만드는 중에 메모리 증가
- 리플리카에서 replicaof 실행
- 쓰기는 마스터에서만
- 장애 시 자동 failover 안돼서 잘 안 씀(커넥션 정보 수동으로 설정 변경해 줘야)
sentinel
- ifnot: 복제 구성 시 장애 발생하면 직접 레디스 엔드포인트 변경 필요
- replica of no one(복제본으로 활동하지 않도록) 설정하고 설정 변경 후 재시작
- 데이터를 저장하지 않고 모니터링만 하는 프로세스(최소 3대 필요; 과반수가 필요; 홀수 구성)
- 같이 설치됨; 추가로 다운로드할 필요 없음
- 정상적으로 동작하기 위해선 최소 세 대의 센티널 프로세스가 필요
- 마스터 파악; 마스터로 직접 연결 -> 변경되면 클라한테 알려줌
- 마스터가 정상적으로 동작하는지 판단할 때 쿼럼(과반수) 이상일 때 페일오버
- 새로운 마스터가 되면 그동안의 데이터를 모두 날린 후 새로운 데이터를 복제받음
- 다운된 마스터가 살아나면 새로운 마스터에 복제본으로 연결됨
cluster
- full mesh 구조; 서로서로 감시(최소 3대, replica까지 치면 6대 필요)
- 스캐일 업; 수직 확장: 스펙업
- 스캐일 아웃; 수평 확장: 개수 증가
- 일반적으로 업을 먼저 하고 아웃을 해
- 키가 차있으면 먼저 메모리를 올려 스캐일 업
- 메모리 문제가 아니라 처리량 문제면 스캐일 아웃 -> 처리량이 많아짐; 병렬도 가능; 마스터도 많아질 수
- 데이터 샤딩
- 개발(애플리케이션)에서 신경 쓰지 않아도 됨
- 클러스터 아무 곳으로 던지면 알아서 리디렉션
- 클라이언트 -> 클러스터 맵에 키랑 어디에 있는지 매핑 정보 들고 있음
- 1 키 - 1 마스터
- full mesh구조
- 모든 마스터, 리플리카 노드는 가십 프로토콜을 사용해 서로서로 통신
- 최소 세 개의 마스터 노드 필요
- 샤딩 방법
- 모든 키는 해시 슬롯에 연결됨(최대 16384개; mod 16384)
- 해시 슬롯을 마스터 노드가 나눠가짐(나눠서 저장됨)
- 키가 어떤 마스터에 저장되어 있는지 해시 함수로 알 수 있음(저장; 조회 모두 사용)
- 마스터 노드 추가/삭제
- 카프카는 데이터 이동 시 해당 데이터 사용 불가한데(전체 블라킹)
- 레디스는 키를 새 마스터 옮기더라도 하나씩 옮기기 때문에 다운타임이 안 길어
- 간편한 편..
- 개발(애플리케이션)에서 신경 쓰지 않아도 됨
레디스 도입 시
- 어떤 용도?(persistence 기능 사용 유무)
- 캐시
- 저장 기능을 사용하지 않도록(원본이 디비에 있으면)
- 캐시가 죽어도 애플리케이션에 영향가지 않도록(look-aside , write-through)
- 디비
- sentinel 사용
- 애플리케이션 죽으면 영향 갈 수 있음
- aof 사용하는 것이 안전
- 주기적으로 rewrite 되도록 파라미터 조절 필요
- 캐시
레디스 구성을 할 때 미래를 생각하여 데이터를.. 짜야한다.
싱글 했다가 중간에 cluster로 바꿨는데 미리 염두에 두고 작업해야.. 나중에 cluster 옮겼는데 일부 명령어 안되고 잘 안될 수도 있다.
메모리 관리
- used_memory: 논리적으로 Redis가 사용하는 메모리
- used_memory_rss: OS가 Redis에 할당하기 위해 사용한 물리적 메모리 양
- 삭제되는 키가 많으면 fragmentation 증가
- 특정 시점에 피크를 찍고 다시 삭제되는 경우
- TTL로 인한 eviction이 많이 발생하는 경우
- 파편화 된 데이터 정리 해주는 명령어
- CONFIG SET activedefrag yes
del 명령어보다 unlink
728x90
반응형
'개발 > cache' 카테고리의 다른 글
[redis] sorted set (1) | 2024.12.16 |
---|---|
분산락 - redis 사용 (0) | 2024.11.08 |