Spring

Redis (Cashing 캐싱/성능 높히기)

sehunbang 2024. 3. 21. 21:16

Redis란? 

Key, Value 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈 소스 기반의 비관계형 데이터 베이스 관리 시스템 (DBMS)이다. 

데이터베이스, 캐시, 메세지 브로커로 사용되며 인메모리 데이터 구조를 가진 저장소이다. 

 

 

RedisTemplate

Redis config

package com.sparta.scv.global.config;

import java.time.Duration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

//@EnableRedisRepositories // redis 활성화
@EnableCaching
@Configuration
public class RedisConfig {

  @Value("${spring.data.redis.host}")
  private String host;

  @Value("${spring.data.redis.port}")
  private int port;

  // 연결정보
  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
    redisStandaloneConfiguration.setHostName(host);
    redisStandaloneConfiguration.setPort(port);
    return new LettuceConnectionFactory(redisStandaloneConfiguration);
  }

  // 직렬화 / 역직렬화
  @Bean
  public RedisTemplate<String, Object> redisTemplate() {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory());
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new StringRedisSerializer());
    return redisTemplate;
  }

  // 캐시 매니저
  @Bean
  public CacheManager cacheManager() {
    RedisCacheManager.RedisCacheManagerBuilder builder =
        RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory());

    RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer 변경
        .disableCachingNullValues()
        .entryTtl(Duration.ofMinutes(10L));

    builder.cacheDefaults(configuration);

    return builder.build();
  }

}

 

 

 

Redis Cashe 에 등록 + Cashing 적용. (+ 동시 접근/동시성 제어도 추가 해봄)

// 사실 회원 정보 수정에 캐싱 이나 동시 접근을 할필요가 없지만 공부 해볼겸 해봤습니다.
@Transactional
@CachePut(value = "User", key = "#user.getId", cacheManager = "cacheManager")
public Long update(UpdateRequestDto requestDto, User user) {
  RLock lock = redissonClient.getFairLock(LOCK_KEY);
  Long returnlong = 404L;
  try {
    boolean isLocked = lock.tryLock(10,60, TimeUnit.SECONDS);
    if(isLocked){
      try {
        User updateuser=userRepository.findById(user.getId()).orElseThrow(NoSuchElementException::new);
        updateuser.update(requestDto);
        returnlong = updateuser.getId();
      }
      finally {
        lock.unlock();
      }
    }
  } catch (InterruptedException e){
    Thread.currentThread().interrupt();
  }
  return returnlong;
}

redis cashe 에서 삭제시

@Transactional
@CacheEvict(value = "User", key = "#user.getId", cacheManager = "cacheManager")
public Long delete(User user) throws NoSuchElementException {
  try {
    userRepository.delete(user);
  }catch (Exception e){
    throw new NoSuchElementException("해당 유저를 지우는데 실패");
  }
  return user.getId();
}

 

 

성능 차이 :

처음 Cashing 넣을때는 큰차이 없고 등록 된후 반복 작업을 할때

 

전 :

후 :

 

252 ms -------> 17 ms

번외

@RedisHash?

1. @RedisHash(value)

리프레시 토큰이 생성된 후 Redis에 저장될 Domain Object를 Redis Hash 자료구조로 변환하는 방식이다.

@RedisHash 어노테이션의 value에 특정한 값을 넣어줌으로써, 데이터가 저장될 때 key의 prefix에 붙을 문자열이 정해질 수 있다. 또한, timeToLive 옵션을 통해 입력한 숫자만큼 초 단위로 유효기간을 지정할 수 있다.

@RedisHash(value = "refreshToken", timeToLive=60)

2. @Id

@Id가 적용된 필드의 값은 Redis 해시의 키로 사용되어, 객체를 고유하게 식별되는 것에 사용된다. (prefix:id)와 같은 형식으로 저장된다.

@Getter
@RedisHash(value = "people", timeToLive = 30)
public class Person {

  @Id
  private String id;
  private String name;
  private Integer age;
  private LocalDateTime createdAt;

  public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
    this.createdAt = LocalDateTime.now();
  }
}

 

 

 

@Cache(usage = CacheConcurrencyStrategy......)

@Transactional
@CacheEvict(value = "User", key = "#user.getId", cacheManager = "cacheManager")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public Long readonly(User user) throws NoSuchElementException {