4 분 소요

Redis란?

  • 시스템 메모리를 사용하여 Key - Value 구조로 비정형 데이터를 저장하고 관리하는 인메모리 데이터 저장소.
  • 메모리를 사용하다 보니 보통 캐시, 서버 용도로 많이 사용됨. => 캐시로 사용하여 성능을 향상시키는 것이 주 목적.

Redis의 특징

:메모리 DB의 특징을 가지게 됨.

  • 물리 DB(ex, mysql)를 거치지 않고 메모리에서 값을 가져오기 때문에 속도가 빠름.
  • 메모리이기 때문에 기본적으로 휘발성임. (영속성을 지원하긴 하지만, 서버의 물리 DB에 저장하는것보다 당연히 불안정함.)
  • 다양한 자료구조를 지원(ex, Lists, Sets, Hashes 등)

Redis 서버 구축방법

  1. Single

  2. Sentinel

  3. Cluster

    • 여러 인스턴스를 활용하며, 각 서버당 1개의 Master Node와 N-1개의 Slave Node를 둠.

    • Slave NodeMaster Node의 자료를 복제함. 단, 복제본은 같은 서버가 아니라 다른 서버에 둠.

    • 이렇게 구성하여 서버가 죽었을 때 다른 서버를 활용하여 정상 운영을 가능하게 함. (마치 NAS의 RAID 개념과 비슷하네)

      image-20220916222619022

Redis 명령어

set key value // 키와 벨류를 저장함
get key // 키에 해당하는 벨류를 호출
del key // 키-벨류 삭제
keys * // 모든 키 리턴
# redis 서비스 실행
redis-server

# redis-cli 동작
redis-cli
-> ip:port>

# (비밀번호 설정이 되어 있는 경우)
auth 비밀번호
-> OK

# 비밀번호(계정) 확인
config get requirepass
"requirepass"
"" -> 비밀번호 세팅 안 된 상태

# 비밀번호 세팅
config set requirepass 비밀번호
-> OK

# 비밀번호 세팅 적용
auth 비밀번호
-> OK

# 제대로 동작하는지 확인
ping
-> PONG

# 레디스 설정파일
sudo vi /etc/redis/redis.conf

# 외부에서 redis 접근방법
redis-cli -h 3.34.206.181 -p 6379

Springboot 프로젝트에서 Redis를 사용하여 캐시 사용

  • application.yml

    spring:
      redis:
        host: localhost
        port: 6379 (레디스 포트번호)
    
  • build.gradle

    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    
  • CacheConfig.java (config 클래스)

    package com.dayone.config;
      
    import lombok.RequiredArgsConstructor;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
      
    @Configuration
    @RequiredArgsConstructor
    public class CacheConfig {
      
      @Value("${spring.redis.host}")
      private String host;
      
      @Value("${spring.redis.port}")
      private int port;
      
      @Bean
      public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration conf = RedisCacheConfiguration.defaultCacheConfig()
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
    //.entryTtl(Duration.of(주기));  Redis 전체에 대한 Ttl 설정 (주기적으로 삭제)
      
        return RedisCacheManager.RedisCacheManagerBuilder
            .fromConnectionFactory(redisConnectionFactory)
            .cacheDefaults(conf)
            .build();
      }
      
      @Bean
      public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration conf = new RedisStandaloneConfiguration();
        conf.setHostName(host);
        conf.setPort(port);
        return new LettuceConnectionFactory(conf); // 이걸로 RedisConnectionFactory 빈으로 사용하게 됨.
      }
    }
      
    
  • FinanceService.java (캐시를 사용할 메서드가 있는 클래스)

    @Service
    @AllArgsConstructor
    public class FinanceService {
      
      private final CompanyRepository companyRepository;
      private final DividendRepository dividendRepository;
      
      // 캐시에 데이터가 있으면 로직수행하지 않고 캐시에서 가져옴. / 캐시에 데이터가 없으면 로직 수행하고 결과를 캐시에 저장.
      // 캐시는 내용이 업데이트되어서 새로운 정보가 필요하거나, 캐시 메모리가 다 차게 되면 비워줘야 함. -> 스캐쥴러 활용 가능.
      @Cacheable(key = "#companyName", value = CacheKey.KEY_FINANCE) //#companyName은 파라미터에서 주입받는 것.
      public ScrapedResult getDividendByCompanyName(String companyName) {
    		// 서비스 로직
      }
    }
    
  • 호출 시 실제 로그

    Hibernate: select companyent0_.id as col_0_0_ from company companyent0_ where companyent0_.ticker=? limit ?
    Hibernate: insert into company (id, name, ticker) values (null, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: insert into dividend (id, company_id, date, dividend) values (null, ?, ?, ?)
    Hibernate: select companyent0_.id as id1_0_, companyent0_.name as name2_0_, companyent0_.ticker as ticker3_0_ from company companyent0_ limit ?
    
    • 한번 insert 후 처음 조회시 select로 빼오지만, 그 이후에는 캐시에서 빼우기 때문에 아무리 조회해도 Redis 캐시에서 가져옴.
  • 캐시를 사용할지 말지?

    1. 요청이 자주 들어오는가? -> 요청이 빈번하면 캐시에서 꺼내오는 것이 효율적.
    2. 데이터가 자주 변경되는가? -> update 될때마다 캐시에 있는 데이터도 변경해주어야 하기 때문에 이럴 경우 캐시를 활용하지 않는 것이 좋을 수 있음.
  • [실무팁] 캐시 사용 이슈!!

    • 캐시 데이터도 제한된 용량이 있기 때문에 주기적으로 관리가 필요함 -> 스케쥴링 활용이 가능함!
    • 내용이 업데이트 될 경우, 캐시를 삭제한 후 다시 올리거나 혹은 캐시에 있는 데이터도 업데이트하여 데이터 정합성을 유지해야 함!

마지막 수정일시: 2022-11-07 21:59

카테고리:

업데이트:

댓글남기기