[TIL] 241128 Redis

MONA·2024년 11월 28일

나혼공

목록 보기
37/92
post-thumbnail

오늘도 아침을 여는 코드카타..

프로그래머스-모의고사

수많은 불합리하고 말도안되는 알고리즘 문제 중 한가닥 공감을 사는 문제.. 흑흑

import java.util.ArrayList;
import java.util.Arrays;

class Solution {
    public int[] solution(int[] answers) {
        int[][] students = {
                {1, 2, 3, 4, 5},
                {2, 1, 2, 3, 2, 4, 2, 5},
                {3, 3, 1, 1, 2, 2, 4, 4, 5, 5}
        };
        int[] scores = new int[students.length]; // score

        for(int i=0; i< answers.length; i++) {
            for (int st = 0; st < students.length; st++) {
                if (answers[i] == students[st][i % students[st].length]) {
                    scores[st]++;
                }
            }
        }
        int maxScore = Arrays.stream(scores).max().orElse(0);

        ArrayList<Integer> answer = new ArrayList<>();
        for(int i =0; i < scores.length; i++) {
            if(scores[i] == maxScore) {
                answer.add(i + 1);
            }
        }
        return answer.stream().mapToInt(Integer::intValue).toArray();
    }
}
  • int[][] students : 수포자들(정상인들)의 정답 패턴을 작성
  • int[] scores : 정상인들의 눈물겨운 결과를 저장할 배열
  • for(int i=0; i< answers.length; i++): 정답 배열(answers)과 정답 패턴(students)를 비교하기 위해 answer의 정답을 순회
    • for (int st = 0; st < students.length; st++)
      • 각 학생의 정답 패턴을 순회
    • students[st][i % students[st].length]
      • 학생(st)의 정답 패턴에서 i번째 문제에 해당하는 값을 가져와 비교함
  • int maxScore = Arrays.stream(scores).max().orElse(0): 최고 점수 추출
  • for(int i =0; i < scores.length; i++): 최고 점수인 수포자들 명단 추가

프로그래머스-소수 만들기

class Solution {
    public int solution(int[] nums) {
        int cnt = 0;
        for(int i = 0; i < nums.length - 2; i++) {
            for(int j = i + 1; j < nums.length - 1; j ++) {
                for(int k = j + 1; k < nums.length; k++) {
                    int sum = nums[i] + nums[j] + nums[k];
                    if (isPrime(sum)) {
                        cnt++;
                    }
                }
            }
        }
        return cnt;
    
    public boolean isPrime(int num) {
        if (num <= 1) return false;
        for (int i = 2; i <= Math.sqrt(num); i++) {
            if ( num % i == 0) return false;
        }
        return true;
    }
}

조합을 어떻게 해야할 지 고민하는데 생각나는 가장 간단한 방법이 for문 돌리기였다
3중 for문이 되어버려서.. 저기만 시간복잡도가 O(n^3)가 될 것 같다
더 좋은 방법이 없을까. 이따 찾아봐야겠다.


인메모리 데이터베이스(In-Memory Database, IMDB)

데이터를 디스크 대신 메모리(RAM)에 저장하여 초고속 읽기 및 쓰기 성능을 제공하는 데이터베이스
디스크 기반 데이터베이스에 비해 지연 시간이 매우 낮아, 실시간 처리와 고성능을 요구하는 애플리케이션에서 사용됨

특징

  1. 메모리 기반 데이터 저장
    • 데이터를 디스크가 아닌 메모리에 저장하여 초고속 접근이 가능
  2. 초저지연 데이터 처리
    • 읽기/쓰기 작업이 메모리에서 직접 이루어지므로 밀리초 이하 응답 시간 제공
  3. 데이터 영속성
    • 일부 IMDB는 메모리에 저장된 데이터를 주기적으로 디스크에 저장하거나, 쓰기 작업 로그를 유지하여 데이터 영구성을 보장
  4. 다양한 데이터 구조
    • 문자열, 해시, 리스트, 셋 등 다양한 데이터 구조를 지원함(Redis 등)
  5. 고성능
    • CPU와 메모리 중심의 아키텍처로 높은 동시성과 처리량 제공
  6. TTL(Time-To-Live)
    • 데이터의 자동 만료를 지원하여 캐싱 및 임시 데이터 관리에 적합
  7. 클러스터링 및 분산 처리
    • 여러 노드에 데이터를 분산 저장하여 확장성과 고가용성을 제공

장단점

장점

  1. 빠르다!
  2. 높은 동시성: 실시간 애플리케이션에서 수많은 동시 요청을 효율적으로 처리할 수 있음
  3. 효율적인 캐싱: 자주 사용하는 데이터 캐싱으로 응답 속도 극대화
  4. 다양한 용도: 세션 관리, 실시간 분석, 순위표, 메시지 브로커 등 다양한 활동 가능
  5. 유연한 데이터 구조

단점

  1. 메모리 의존성
    • 데이터가 메모리에 저장되므로 용량이 제한적
    • 대규모 데이터 저장에 적합하지 않음
  2. 비용
    • 메모리는 디스크보다 비싸다
  3. 데이터 영속성
    • 전원이 꺼지거나 시스템 충돌 시 메모리 데이터가 손실될 위험이 있음
    • 영구 저장을 위해 별도 설정이나 로그 기록 필요
  4. 데이터 복잡성
    • 관계형 데이터베이스처럼 복잡한 쿼리 처리에 적합하지 않음

인메모리 데이터베이스와 디스크기반 데이터베이스

특징인메모리 데이터베이스(IMDB)디스크 기반 데이터베이스
속도매우 빠름 (RAM 기반)상대적으로 느림 (디스크 I/O)
데이터 저장소메모리 (RAM)디스크
데이터 영속성메모리만 사용 시 영구성 부족기본적으로 영구 저장 가능
확장성메모리 용량에 제한디스크 용량에 따라 대규모 데이터 저장 가능
비용메모리 비용이 디스크보다 비쌈디스크 기반으로 비용 효율적
사용 사례캐싱, 세션 관리, 실시간 분석트랜잭션, 복잡한 데이터 관계 저장
복잡한 쿼리 처리제한적SQL 기반 복잡한 쿼리 지원

드디어rrrrrrrr Redis

Redis (Remote Dictionary Server)

(조건부) 오픈소스 인메모리 데이터 저장소
고속 데이터 처리를 위해 데이터를 메모리(RAM)에 저장하고 다양한 데이터 구조를 지원하는 NoSQL 데이터베이스
캐시, 메시지 브로커, 세션 저장소 등 다양한 용도로 사용 가능

Redis

특징

  1. 인메모리 데이터 저장소
    • 데이터를 디스크 대신 메모리에 저장하여 빠른 읽기/쓰기 성능을 제공
    • 필요에 따라 데이터를 디스크에 백업(AOF, RDB) 가능
  2. 다양한 데이터 구조 지원
    • 문자열(String), 해시(Hash), 리스트(List), 셋(Set), 정렬된 셋(Sorted Set), 비트맵(Bitmap), 하이퍼로그로그(HyperLogLog) 등 지원
  3. 싱글 스레드 기반
    • 싱글 스레드 이벤트 루프를 사용해 요청 처리
    • 멀티 스레드 대비 낮은 지연 시간과 일관된 성능 제공
  4. 높은 성능
    • 읽기/쓰기 작업에서 초당 수백만 요청 처리 가능
    • 밀리초 단위 응답 속도 제공
  5. 유연한 데이터 저장
    • 캐싱 외에도 데이터의 영구 저장(Persistence)을 지원
      • RDB (Redis Database): 특정 간격으로 데이터를 스냅샷으로 저장
      • AOF (Append Only File): 모든 쓰기 작업을 기록하여 복구 가능
  6. 스케일링
    • 클러스터 모드를 통해 데이터를 여러 노드에 분산 저장하여 확장 가능
  7. PUB/SUB 메시지 브로커
    • Publish/Subscribe 패턴으로 실시간 메시지 전달
  8. 트랜젝션
    • 명령어를 묶어 실행하는 단순한 트랜젝션(MULTI/EXEC) 지원

주요 용도

  1. 캐싱
    • 자주 사용하는 데이터를 메모리에 저장하여 빠르게 조회
    • ex) 웹 애플리케이션의 세션 데이터, 사용자 프로필 등
  2. 세션 저장소
    • 웹 애플리케이션에서 사용자 세션 정보를 저장
    • TTL(만료 시간)을 설정하여 자동으로 데이터 삭제 가능
  3. 실시간 애플리케이션
    • 채팅, 알림, 게임 스코어보드, 실시간 분석에 활용
  4. 메시지 브로커
    • PUB/SUB를 사용해 실시간 알림 시스템 구축
  5. 데이터 스트림 처리
    • Redis Streams를 사용해 이벤트 및 로그 데이터를 처리
  6. 순위표 관리
    • Sorted Set 구조를 사용해 순위 정렬 및 점수 계산
  7. 비정형 데이터 저장
    • 해시, 리스트, 셋 등을 사용해 복잡한 데이터 구조 관리

데이터 구조

데이터 구조설명사용 사례
String단순 키-값 형태의 데이터캐싱, 세션 관리
Hash필드-값 쌍으로 구성된 해시 테이블사용자 정보 저장 (ex: 프로필 정보)
List순서가 있는 데이터의 컬렉션 (양방향 삽입/삭제 지원)작업 큐, 채팅 메시지
Set유일한 값들의 집합, 중복 허용하지 않음태그 관리, 집합 연산 (교집합, 합집합)
Sorted Set정렬된 값들의 집합 (점수 기반)리더보드, 순위 관리
Bitmap비트 수준의 값을 저장 및 조작비트 플래그, 간단한 상태 관리
HyperLogLog근사적(unique) 개수 계산 알고리즘방문자 수 추정
Streams데이터 스트림 처리 (로그, 이벤트)이벤트 스트림 분석, 실시간 데이터 처리

근사적(unique) 개수 계산 알고리즘: 대규모 데이터 집합에서 중복되지 않은 고유 요소(Unique Elements)의 개수를 빠르고 효율적으로 추정하기 위한 방법

주요 명령어

명령어설명예제
SET key value키에 값을 저장SET user:1 "Alice"
GET key키의 값을 가져옴GET user:1
HSET key field value해시에 필드와 값을 저장HSET user:1 name Alice
HGET key field해시에서 필드의 값을 가져옴HGET user:1 name
LPUSH key value리스트의 왼쪽에 값 추가LPUSH queue task1
LRANGE key start end리스트에서 특정 범위의 값을 가져옴LRANGE queue 0 -1
SADD key value집합에 값 추가SADD tags redis
ZADD key score value정렬된 집합에 점수와 값을 추가ZADD leaderboard 100 user1
EXPIRE key seconds키에 TTL(만료 시간) 설정EXPIRE user:1 300
PUBLISH channel messagePUB/SUB에서 메시지 전송PUBLISH news "Hello"

타입별 주요 명령어 정리

데이터 타입명령어설명예제
StringSET key value키에 값을 저장SET name Alice
GET key키의 값을 가져옴GET name
DEL key키 삭제.DEL name
INCR key키의 값을 1 증가 (숫자로 간주)INCR counter
APPEND key value키에 값을 추가APPEND name " Smith"
HashHSET key field value해시에 필드-값 쌍 저장HSET user:1 name Alice
HGET key field특정 필드의 값을 가져옴HGET user:1 name
HDEL key field특정 필드 삭제HDEL user:1 age
HGETALL key모든 필드-값 쌍을 가져옴HGETALL user:1
HEXISTS key field필드 존재 여부 확인HEXISTS user:1 name
ListLPUSH key value리스트 왼쪽에 값 삽입.LPUSH tasks "Task 1"
RPUSH key value리스트 오른쪽에 값 삽입RPUSH tasks "Task 2"
LPOP key왼쪽 값 제거 및 반환LPOP tasks
RPOP key오른쪽 값 제거 및 반환RPOP tasks
LRANGE key start end리스트의 특정 범위 값을 반환LRANGE tasks 0 -1
SetSADD key value집합에 값 추가SADD tags redis
SREM key value집합에서 값 제거SREM tags redis
SMEMBERS key집합의 모든 값 반환SMEMBERS tags
SINTER key1 key2두 집합의 교집합 반환SINTER tags1 tags2
SUNION key1 key2두 집합의 합집합 반환SUNION tags1 tags2
Sorted SetZADD key score value값과 점수를 추가ZADD leaderboard 100 user1
ZRANGE key start end특정 범위 값 반환 (오름차순)ZRANGE leaderboard 0 -1
ZREVRANGE key start end특정 범위 값 반환 (내림차순)ZREVRANGE leaderboard 0 -1
ZSCORE key value특정 값의 점수 반환ZSCORE leaderboard user1
ZREM key value값 제거ZREM leaderboard user1
BitmapSETBIT key offset value특정 위치의 비트를 설정SETBIT user:1 0 1
GETBIT key offset특정 위치의 비트를 가져옴GETBIT user:1 0
BITCOUNT key비트 1의 개수를 반환BITCOUNT user:1
HyperLogLogPFADD key value값 추가PFADD visitors user1
PFCOUNT key고유 값 개수 추정PFCOUNT visitors
PFMERGE dest source여러 HyperLogLog 병합PFMERGE all_visitors day1 day2
StreamXADD key id field value스트림에 데이터 추가XADD mystream * name Alice
XRANGE key start end범위 내 스트림 데이터 조회XRANGE mystream - +
XREAD COUNT count STREAMS key id특정 ID 이후 데이터 읽기XREAD COUNT 2 STREAMS mystream 0

장단점

장점

  1. 고성능: 메모리 기반 아키텍처로 읽기/쓰기 속도가 매우 빠르다
  2. 다양한 데이터 구조 지원
  3. 단순성과 유연성: 설정이 쉽고 다양한 용도로 확장 가능
  4. 스케일링: 클러스터링과 샤딩을 통해 수평 확장 가능
  5. 풍부한 기능: TTL, Pub/Sub, 트랜젝션, 데이터 지속성 등 다양한 기능 제공

단점

  1. 메모리 제한
    • 인메모리 기반으로, 저장 가능한 데이터 크기가 RAM 용량에 의존
  2. 데이터 지속성
    • RDB와 AOF로 데이터를 영구 저장할 수 있지만, 디스크 기반 데이터베이스에 비해 신뢰도가 낮음
  3. 싱글 스레드
    • 싱글 스레드 기반이므로, 고성능을 유지하려면 CPU 코어에 따라 여러 Redis 인스턴스를 배포해야 함

Redis vs Memcached vs RDBMS

특징RedisMemcachedRDBMS
데이터 저장 방식Key-Value 및 다양한 데이터 구조 지원단순 Key-Value 저장관계형 데이터 모델 (테이블)
속도매우 빠름매우 빠름상대적으로 느림
데이터 구조String, Hash, List, Set 등 지원단순 문자열 저장SQL 쿼리로 복잡한 데이터 처리 가능
데이터 영구 저장지원 (RDB, AOF)지원하지 않음기본적으로 지원
스케일링클러스터링 및 샤딩 지원샤딩 지원수평 스케일링 제약, 샤딩 필요
TTL 지원기본적으로 지원기본적으로 지원별도 설정 필요 (트리거, 스케줄러)
사용 사례캐싱, 세션 관리, 실시간 애플리케이션단순 캐싱데이터 저장, 복잡한 쿼리 처리
메모리 사용량효율적 (압축 가능)데이터 크기에 따라 증가메모리 외 디스크 사용
복잡도다양한 기능 제공으로 상대적으로 높음단순한 설정과 사용높은 복잡도 (스키마 설계 등)

Memcached
오픈 소스 인메모리 데이터 캐싱 시스템
데이터베이스, API, 애플리케이션에서 데이터를 캐싱하여 요청 속도를 높이고, 성능과 확장성을 개선하는 데 사용

  • Redis: 다양한 데이터 구조와 영구 저장(Persistence) 옵션 지원. 실시간 애플리케이션이나 복잡한 캐싱 요구에 적합
  • Memcached: 단순하고 경량으로 설계되어 빠르고 기본적인 캐싱에 적합
  • RDBMS: 관계형 데이터 저장과 복잡한 쿼리를 위한 시스템. 영구 데이터 저장소로 사용

Redis, Memcached는 주로 캐싱 용도로 사용되지만, Redis는 비교적 폭넓은 데이터 구조와 영구 저장 기능으로 다용도로 활용됨

Redis 실행해보기

  • 컴퓨터에 설치(Mac- brew 이용)
  • Docker 이용

내 컴퓨터는 소중하니까 Docker compose를 이용해서 켜본다

  1. docker compose.yml 작성
services:
  redis-stack:
    image: redis/redis-stack
    container_name: redis-stack-compose
    restart: always
    environment:
      REDIS_ARGS: "--requirepass systempass"
    ports:
      - 6379:6379
      - 8001:8001
  • "--requirepass systempass": 비밀번호를 systempass로 설정

정상적으로 띄워진 걸 확인하면, Intellij의 데이터베이스에서 확인할 수 있다.

  1. 연결

database -> data source -> Redis
User: default
password: systempass

  1. 연결 확인

정상적으로 데이터가 삽입, 조회되는 것을 확인할 수 있다

  1. Redis 인사이트 확인

localhost:8001 에 접속하면 레디스 인사이트 페이지가 출력된다

콘솔도 제공하고..

삽입한 key-value 값도 확인할 수 있다

이것저것 가능 (재밌다!)

Redis stack을 쓰면 내 컴퓨터에 있는 레디스에만 연결할 수 있다.
외부 레디스를 쓰는데 인사이트를 쓰고싶다면 앱스토어에서 다운받을 수 있다
또는 dmg 파일로 설치 진행 가능

이것저것 건드려 보다가 인텔리제이에 연결된 데이터베이스 데이터와 브라우저에서 확인한 데이터가 다름을 확인했다.

둘 다 db0에 연결되어 있는데 왜일까..?

이래저래 테스트해 본 결과, 레디스 컨테이너는 정상적으로 실행되고 있었고, 브라우저에서 확인할 수 있는 데이터베이스가 내가 연결하려 했던 레디스였음을 확인했다.
-> 인텔리제이에 연결된 레디스는 다른 것을 참조하고있다..!

컨테이너를 내리고 레디스가 여전히 6379에서 실행 중인지 확인
sudo lsof -i :6379

sudo kill -9 <PID>로 삭제 후 확인했으나 새로운 번호로 실행중인 레디스(!!)
자동 재시작 설정이 되어 있는 듯해 종료시켜줬다.

brew로 설치한 경우 brew services stop redis로 종료할 수 있다.

드디어 조용해졌다.

다시 컨테이너를 띄우고 확인해봤다.

정상 연동!

profile
고민고민고민

0개의 댓글