๊ด€๋ฆฌ ๋ฉ”๋‰ด

๐‘†๐‘ข๐‘›๐‘ โ„Ž๐‘–๐‘›๐‘’ ๐‘Ž๐‘“๐‘ก๐‘’๐‘Ÿ ๐‘Ÿ๐‘Ž๐‘–๐‘›โœง

[Redis] Redis๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์บ์‹ฑ& ์กฐํšŒ ์„ฑ๋Šฅ ๊ฐœ์„ ํ•˜๊ธฐ ๋ณธ๋ฌธ

๐—ฃ๐—ฟ๐—ผ๐—ด๐—ฟ๐—ฎ๐—บ๐—บ๐—ถ๐—ป๐—ด๐Ÿ’ป/๐‘๐ž๐๐ข๐ฌ

[Redis] Redis๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์บ์‹ฑ& ์กฐํšŒ ์„ฑ๋Šฅ ๊ฐœ์„ ํ•˜๊ธฐ

๐ŸคRyusun๐Ÿค 2024. 3. 12. 17:41

 Redis ํŠน์ง•๊ณผ ์žฅ/๋‹จ์ ์— ์•Œ์•„๋ณด์ž

  • Redis ํŠน์ง•
    • In-memory ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ key-value ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค. ๋‹ค์–‘ํ•œ command๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ single thread์ด๋‹ค.
  • ์žฅ์ 
    • ์—ฌ๋Ÿฌ instance๊ฐ€ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜์žˆ๋‹ค. ๋‹ค์–‘ํ•œ command๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • ๋‹จ์ 
    • Local caching์— ๋น„ํ•ด์„œ๋Š” ๋Š๋ฆฌ๋‹ค.

 

ํ˜„์žฌ ํ•„์ž์˜ ํ”„๋กœ์ ํŠธ๋Š” ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋งค๋ฒˆ ์š”์ฒญ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•ด์„œ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณ€ํ™”ํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋งค๋ฒˆ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•ด์„œ ๊ฐ€์ ธ์˜จ๋‹ค๋ฉด ์„ฑ๋Šฅ์— ๋ฌด๋ฆฌ๊ฐ€ ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๋ฐ์ดํ„ฐ๋ฅผ redis๋กœ ์บ์‹ฑ์„ ํ•ด๋‘๊ณ  ์กฐํšŒํ•  ๋•Œ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ redis๋กœ๋ถ€ํ„ฐ ์กฐํšŒ๋ฅผ ํ•ด์„œ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋„๋ก ๊ฐœ์„ ์„ ํ•ด๋ณด์ž.

๋˜ํ•œ Redis๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” failover์— ๋Œ€ํ•œ ๋ถ€๋ถ„์„ ๋ฐ˜๋“œ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ Redis๊ฐ€ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•ด์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋„ ํ•ด๋ณด์ž.

 


1. RedisTemplateService.java

cache๋ผ๋Š” ๋””๋ ‰ํ† ๋ฆฌ์•ˆ์— ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.
ํ•ด๋‹น ํด๋ž˜์Šค์—์„œ ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด ๋ณด๊ณ  ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋ณผ ๊ฒƒ์ด๋‹ค.


์ƒ์ˆ˜๋กœ ํ‚ค๊ฐ’์„ “PRODUCT” ๋กœ ์ง€์ •ํ•œ๋‹ค.
Redis ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ด์ „ ๊ธ€์— ๋งŒ๋“ค์—ˆ๋˜ Redis ํ…œํ”Œ๋ฆฟ์„ ๊ฐ€์ ธ์˜จ๋‹ค.
๋˜ํ•œ ๊ฐ์ฒด๋ฅผ redis ๋ฐ์ดํ„ฐ๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ ์œ„ํ•ด ObjectMapper๋„ ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ•œ๋‹ค. 
์šฐ๋ฆฌ๋Š” Redis์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ์ €์žฅ์„ ํ•  ๋•Œ, Serialize, Deserialize๋ฅผ Object Mapper๋ฅผ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ํ•˜๋‚˜์˜ ์‹ฑ๊ธ€ํ„ด์œผ๋กœ ๊ตฌ์„ฑ์„ ํ•ด๋†“๊ณ  ๋นˆ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋„๋ก ํ•˜์ž.

๋˜ํ•œ ์šฐ๋ฆฌ๋Š” ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋Š” @PostConstruct๋ฅผ ์ด์šฉํ•ด์„œ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ํ•˜๊ณ  ๋‚œ ํ›„์— redisTemplate์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•ด์‹œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•  ๊ฒƒ์ด๋‹ค. 


ObjectMapperConfig ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด ์ค€๋‹ค. ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ ObjectMapper๋ฅผ ์‚ฌ์šฉํ•ด์„œ serialize, deserialize๋ฅผ ์ง„ํ–‰ํ•  ๊ฒƒ์ด๋‹ค.

 

 

HashOperation๋Š” redisTemplate์ด ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์šฐ๋ฆฌ๋Š” ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋Š” @PostConstruct๋ฅผ ์ด์šฉํ•ด์„œ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ํ•˜๊ณ  ๋‚œ ํ›„์— redisTemplate์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•ด์‹œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•  ๊ฒƒ์ด๋‹ค. 

 

์šฐ๋ฆฌ๋Š” HashOperation์„ ์ด์šฉํ•ด์„œ ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์„ ๊ฑด๋ฐ 

HashOperation<String, String, String> ์—์„œ
์ฒซ๋ฒˆ์งธ String์€ ์•ž์„œ ์ •์˜ํ•œ key๊ฐ’(=CACHE_KEY)
๋‘๋ฒˆ์งธ์™€ ์„ธ๋ฒˆ์งธ ํ‚ค๊ฐ’์€ subkey(=db์˜ pk๊ฐ’), value(์ƒํ’ˆdto๋ฅผ string๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•œ ๊ฐ’) ๋ฅผ ๋‹ด์„ ๊ฒƒ์ด๋‹ค. ๋งˆ์ง€๋ง‰ value๊ฐ’์€ ์ƒํ’ˆ dto๋ฅผ String๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•  ๊ฒƒ์ด๋‹ค. Object Mapper๋ฅผ ์ด์šฉํ•ด์„œ Serialize๋ฅผ ํ•ด์„œ json ํ˜•ํƒœ๋กœ ์ €์žฅํ•  ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ serialize, deserialize ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž

 

ObjectMapper.writeValueAsString() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ์ฒด๋ฅผ Jsonํ˜•ํƒœ๋กœ Serializeํ•ด์ค€๋‹ค.
Deserializeํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ObjectMapper์—์„œ ์ œ๊ณตํ•˜๋Š” ObjectMapper.readValue()๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด Jsonํƒ€์ž…์„ ์ƒํ’ˆ DTO๋กœ ๋งตํ•‘์„ ํ•ด์„œ ๋ณ€ํ™˜ํ•ด ์ค€๋‹ค.
ObjectMapper.readValue(๋ณ€ํ™˜์‹œํ‚ค๊ณ ์ž ํ•˜๋Š” ๊ฐ’, ๋ณ€ํ™˜๋  ๊ฐ’)

 

์šฐ๋ฆฌ๋Š” ์ด serialize, deserialize๋ฅผ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ Redis์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ, ์กฐํšŒ ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

์ด์ œ Redis์— ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

 

 

์ €์žฅํ•˜๋Š” ๋ฉ”์„œ๋“œ์—๋Š” ์•ž์„œ ์ •์˜ํ•œ ์ˆœ์„œ๋Œ€๋กœ 
์บ์‹œ ํ‚ค๊ฐ’์—๋Š” ์ƒ์ˆ˜๋กœ ์„ ์–ธํ•œ ํ‚ค๊ฐ’,  ์„œ๋ธŒํ‚ค, ๋ฐธ๋ฅ˜๊ฐ’์„ ๋ณ€ํ™˜์„ ํ•ด์„œ ํ•ด์‹œ์— ๋„ฃ์–ด์ค€๋‹ค.

 

 

์กฐํšŒ๋ฉ”์„œ๋“œ๋Š” ์•ž์„œ ์ €์žฅํ•œ key์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ jsonํ˜•ํƒœ๋ฅผ ์ƒํ’ˆ dto๋กœ deserialize ํ•ด์ค€๋‹ค. hashOperations.entries()๋Š” ์‚ฌ์šฉํ•ด์„œ ์ „์ฒด value๊ฐ’์„ ๋ชจ๋‘ ๊ฐ€์ ธ์˜จ ํ›„ ์ƒํ’ˆ dto ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜์„ ํ•œ๋‹ค.

๋งŒ์•ฝ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ค€๋‹ค.

 

์ „์ฒด ์ฝ”๋“œ

@Slf4j
@Service
@RequiredArgsConstructor
public class SearchRedisTemplateService {
    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    private static final String CACHE_KEY = "PRODUCT";

    private HashOperations<String, String, String> hashOperations;

    @PostConstruct
    public void init() {
        this.hashOperations = redisTemplate.opsForHash();
    }

    public void save(SearchResultDto resultDto) {
        if(Objects.isNull(resultDto) || Objects.isNull(resultDto.getProductId())) {
            log.error("Required Values must not be null");
            return;
        }

        try {
            hashOperations.put(CACHE_KEY,
                    resultDto.getProductId(),
                    serializeDto(resultDto));
            log.info("[RedisTemplateService save success] id: {}", resultDto.getProductId());
        } catch (Exception e) {
            log.error("[RedisTemplateService save error] {}", e.getMessage());
        }
    }

    public List<SearchResultDto> findAll() {

        try {
            List<SearchResultDto> list = new ArrayList<>();
            for (String value : hashOperations.entries(CACHE_KEY).values()) {
                SearchResultDto resultDto = deserializeDto(value);
                list.add(resultDto);
            }
            return list;

        } catch (Exception e) {
            log.error("[RedisTemplateService findAll error]: {}", e.getMessage());
            return Collections.emptyList();
        }
    }

    public void delete(String id) {
        hashOperations.delete(CACHE_KEY, id);
        log.info("[RedisTemplateService delete]: {} ", id);
    }

    private String serializeDto(SearchResultDto productDto) throws JsonProcessingException {
        return objectMapper.writeValueAsString(productDto);
    }

    private SearchResultDto deserializeDto(String value) throws JsonProcessingException {
        return objectMapper.readValue(value, SearchResultDto.class);
    }

}

 

 

2. Redis๋กœ ์กฐํšŒํ•˜๊ธฐ

 

 

๋งŒ์•ฝ ์ „์ฒด ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๋ถˆ๋ฆฌ์—ˆ์„๋•Œ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ Redis์—์„œ ๋จผ์ € ์กฐํšŒ๋ฅผ ํ•˜๊ณ  ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋„๋ก ์ง„ํ–‰ํ•  ๊ฒƒ์ด๋‹ค. 
Redis๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฐฉ๊ธˆ ๋งŒ๋“  RedisTemplateService๋ฅผ ์ฃผ์ž…์‹œํ‚จ๋‹ค.

 

 

์ด์ œ Redis์— ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•˜๋Š” ์‹ค์Šต์„ ํ•ด๋ณด์ž!

 

ํ•„์ž๋Š” ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋ฅผ ์ด์šฉํ•ด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ˜ํ…์ŠคํŠธ๋ฅผ ๋กœ๋“œํ•˜์—ฌ Redis์™€ ์—ฐ๊ฒฐํ•˜์—ฌ ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•  ๊ฒƒ์ด๋‹ค.

 

 

์šฐ์„  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒํ’ˆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ Redis์— ์ €์žฅํ•ด ๋ณด์ž

 

ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผ๋˜๊ณ 

Redis ์ปจํ…Œ์ด๋„ˆ์— ๋“ค์–ด๊ฐ€์„œ ํ™•์ธํ•ด ๋ณด๋ฉด

 ์ž˜ ์ €์žฅ์ด ๋œ ๊ฑธ ํ™•์ธํ•  ์ˆ˜๊ฐ€ ์žˆ๋‹ค.

 

์ด์ œ ์กฐํšŒํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰์‹œ์ผœ ๋ณด์ž.

 

์กฐํšŒํ•˜๋Š” ๋ฉ”์„œ๋“œ๋„ ํ†ต๊ณผํ•œ๋‹ค!!

 

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ ˆ๋””์Šค ํŽ˜์ผ์˜ค๋ฒ„ ๋ฐœ์ƒ ์‹œ db์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”์ง€๋„ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์ž.

 

ํ…Œ์ŠคํŠธ ํ†ต๊ณผ!!!^_^

 

๋!