티스토리 뷰

[개발] 데이터베이스/Redis

Redis 소개

Devsong26 2024. 9. 2. 05:43

Redis(레디스)는 오픈 소스 비관계형(NoSQL) 데이터베이스로, 주로 인메모리 데이터 구조 서버로 사용됩니다. 즉, 데이터를 메모리(RAM)에 저장하고 관리하며, 매우 빠른 속도를 자랑합니다.

 

Redis의 주요 특징

  1. 인메모리 데이터 저장:
    • Redis는 데이터를 메모리에 저장하므로, 매우 낮은 지연 시간과 높은 처리량을 제공합니다. 이는 실시간 애플리케이션이나 캐시 서버로 사용하기 적합합니다.
  2. 다양한 데이터 구조 지원:
    • Redis는 단순한 키-값 저장소 이상으로, 다양한 데이터 구조를 지원합니다. 예를 들어, 문자열(Strings), 리스트(Lists), 집합(Sets), 정렬된 집합(Sorted Sets), 해시(Hashes), 비트맵(Bitmaps), 하이퍼로그로그(HyperLogLogs), 지오스페이셜 인덱스(Geospatial Indexes) 등이 있습니다.
  3. 영속성 옵션:
    • Redis는 데이터를 디스크에 영속적으로 저장할 수 있는 옵션도 제공합니다. 이는 RDB (Redis Database Backup) 방식과 AOF (Append-Only File) 방식이 있으며, 이를 통해 서버 재시작 후에도 데이터를 복구할 수 있습니다.
    • RDB: 특정 시간 간격으로 메모리의 스냅샷을 디스크에 저장합니다.
    • AOF: 모든 쓰기 작업을 로그로 기록하여 복구 시 이 로그를 재생합니다.
  4. 복제와 고가용성:
    • Redis는 마스터-슬레이브(Master-Slave) 구조로 복제를 지원합니다. 여러 슬레이브가 하나의 마스터로부터 데이터를 복제할 수 있으며, 슬레이브는 읽기 작업을 처리하여 부하를 분산시킬 수 있습니다.
    • Redis Sentinel을 통해 자동 장애 조치(failover)를 설정할 수 있으며, 클러스터 모드를 통해 수평적으로 확장(scale-out)할 수 있습니다.
  5. 고급 기능:
    • Lua 스크립트: Redis는 Lua 스크립트를 지원하여 복잡한 작업을 원자적으로 실행할 수 있습니다.
    • 트랜잭션: MULTI, EXEC, DISCARD, WATCH 명령어를 통해 트랜잭션을 지원합니다.
    • Pub/Sub: Redis는 발행/구독(Publish/Subscribe) 모델을 통해 메시징 시스템으로도 사용될 수 있습니다.

 

Redis의 사용 사례

  1. 캐시(Cache):
    • 주로 데이터를 빠르게 액세스할 수 있도록 캐싱 레이어로 사용됩니다. 예를 들어, 데이터베이스의 쿼리 결과를 캐싱하여 응답 시간을 줄일 수 있습니다.
  2. 세션 저장소:
    • 사용자 세션 데이터를 관리하기 위해 Redis를 사용합니다. 메모리 기반 저장소이기 때문에 빠른 세션 조회 및 업데이트가 가능합니다.
  3. 실시간 분석:
    • 실시간 로그 처리나 실시간 통계 계산에 사용됩니다. Redis의 데이터 구조를 활용해 카운터, 랭킹 시스템 등을 구현할 수 있습니다.
  4. 메시지 큐:
    • Redis의 리스트(List)와 Pub/Sub 기능을 활용하여 메시지 큐로 사용될 수 있습니다. 이는 간단한 작업 큐나 알림 시스템 등에 적합합니다.

 

 

Redis의 단점

  • 메모리 의존성: 데이터가 메모리에 저장되므로, 대용량 데이터를 처리할 때 메모리 부족이 문제가 될 수 있습니다.
  • 복잡한 데이터 처리: Redis는 단순하고 빠른 데이터 처리를 목표로 하므로, 복잡한 관계형 데이터베이스 기능을 제공하지 않습니다.

 


 

E-Commerce에서 레디스 활용

생성 만료
장바구니 : 비 로그인 시 임시로 저장되는 정보 특정 시간 이후 만료
할인정보 : 특정 기간 * 특정 상품의 할인정보 할인기간 종료 후 만료
쿠폰정보 : User 별, 제품별 할인을 위한 정보 로그아웃이나 쿠폰행사 종료 시 만료
배송정보 : 배송현황 제공을 위한 정보 배송완료 후 n일 후 만료
토큰정보, 세션정보 : 로그인 시, 로그인 상태 유지를 위함 로그인 후 소멸, 로그아웃 시 만료
광고 : User별, 상품별, Seller 별 광고계약시점 이후 만료
채팅정보 : 채팅시 ~ 채팅 종료시까지 채팅 종료 시 만료
그 밖의 Cache 기타 소멸기한이 정해져있을 시 만료

 

 


 

레디스와 MySQL 질의 시간 비교

1. 반복 질의

 

레디스와 mysql의 데이터를 3개씩 셋팅하여 n 번 반복했을 때 시간 차이를 알아보는 실험을 했습니다.

@Test
public void speedComparison(){
    final int n = 100;

    final StopWatch redisStopWatch = new StopWatch();
    redisStopWatch.start();
    for(int i=0; i<n; i++){
        redisTemplate.opsForValue().get(WelfareCategory.DISABLED.name());
    }
    redisStopWatch.stop();

    System.out.println("redis 조회 시간(초) >> " + redisStopWatch.getTotalTimeSeconds());

    final StopWatch mysqlStopWatch = new StopWatch();
    mysqlStopWatch.start();
    for(int i=0; i<n; i++) {
        welfareCardRepository.findTop3ByCategoryOrderByIdDesc(WelfareCategory.DISABLED);
    }
    mysqlStopWatch.stop();

    System.out.println("mysql 조회 시간(초) >> " + mysqlStopWatch.getTotalTimeSeconds());
}

 

결과는 다음과 같습니다.

n redis 조회 시간 mysql 조회 시간
1 0.73 0.34
10 0.75 0.33
100 0.77 0.64
1000 1.25 2.75
10000 3.01 10.53

 

반복 횟수가 100 이하일 경우 redis가 더 느려보이나 그 이상으로 횟수가 증가할 경우 redis가 훨씬 빠른 성능을 보여줍니다. mysql에도 동일한 쿼리의 결과에 대한 캐시를 생성하는데도 불구하고 이 정도로 유의미한 차이가 발생할 지는 몰랐습니다. 

 

성능 개선률 (%)=(이전 시간개선  시간 / 이전 시간)×100

 

n = 1_000일 때는 54.55% 성능이 개선되었고,

n= 10_000 일 때는 71.41% 성능이 개선되었습니다.

 

 

2. 저장

@Test
public void speedComparisonV2(){
    int seq = 1;
    final long inquiryId = 1L;

    final Inquiry parentInquiry = new Inquiry();
    parentInquiry.setId(inquiryId);

    final List<InquiryAnswer> inquiryAnswerList = new ArrayList<>(100_000);
    final List<InquiryAnswer> inquiryAnswerListRedis = new ArrayList<>(100_000);

    for(int i=0; i<100_000; i++){
        final InquiryAnswer inquiryAnswer = new InquiryAnswer();
        inquiryAnswer.setAnswer("ANSWER" + seq++);
        inquiryAnswer.setInquiry(parentInquiry);
        inquiryAnswer.setCreatedBy("TESTER");
        inquiryAnswer.setCreatedAt(LocalDateTime.now());
        inquiryAnswer.setModifiedBy("TESTER");
        inquiryAnswer.setModifiedAt(LocalDateTime.now());
        inquiryAnswerList.add(inquiryAnswer);

        final InquiryAnswer inquiryAnswer2 = new InquiryAnswer();
        inquiryAnswer2.setAnswer("ANSWER" + seq++);
        inquiryAnswer2.setInquiry(parentInquiry);
        inquiryAnswer2.setCreatedBy("TESTER");
        inquiryAnswer2.setModifiedBy("TESTER");
        inquiryAnswerListRedis.add(inquiryAnswer2);

    }

    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for(InquiryAnswer redisIA : inquiryAnswerListRedis){
        redisTemplate.opsForList().leftPush("inquiry:1:answer", redisIA);
    }
    stopWatch.stop();

    System.out.println("REDIS 10만건 저장 시간(초) >> " + stopWatch.getTotalTimeSeconds());

    stopWatch = new StopWatch();
    stopWatch.start();
    inquiryAnswerRepository.saveAll(inquiryAnswerList);
    stopWatch.stop();

    System.out.println("RDB 10만건 저장 시간(초) >> " + stopWatch.getTotalTimeSeconds());
}

 

REDIS 1만건 저장 시간(초) >> 4.1529318
RDB 1만건 저장 시간(초) >> 8.9688153

 

REDIS 10만건 저장 시간(초) >> 16.6945214
RDB 10만건 저장 시간(초) >> 58.4224896

 

 

3. 대용량 데이터 조회

@Test
public void 조회_테스트_해보자(){
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    redisTemplate.opsForList().leftPop("inquiry:1:answer");
    stopWatch.stop();

    System.out.println("REDIS 10만건 조회 시간(초) >> " + stopWatch.getTotalTimeSeconds());

    stopWatch = new StopWatch();
    stopWatch.start();
    inquiryAnswerRepository.findByInquiryId(1L);
    stopWatch.stop();

    System.out.println("RDB 10만건 조회 시간(초) >> " + stopWatch.getTotalTimeSeconds());
}

 

REDIS 10만건 조회 시간(초) >> 1.0064242
RDB 10만건 조회 시간(초) >> 2.3960754

 

 

 

왜 이런 차이가 발생할까?

  • MySQL은 관계형 데이터베이스로, 데이터를 디스크에 저장하며 데이터를 조작할 때 디스크 I/O가 많이 발생합니다. 이러한 디스크 기반 데이터 저장 방식은 대량의 데이터를 효율적으로 관리할 수 있지만, 디스크 액세스는 메모리 액세스보다 상대적으로 느립니다.
  • Redis는 메모리 기반 데이터 구조 저장소로, 데이터를 주로 RAM에 저장합니다. 따라서 데이터에 대한 읽기 및 쓰기 작업이 매우 빠릅니다
  • 그러므로 Redis의 읽기 쓰기 작업이 MySQL에 비해 상대적으로 훨씬 빠릅니다.

'[개발] 데이터베이스 > Redis' 카테고리의 다른 글

[Redis] 분산락 처리하기  (1) 2025.01.05