[Redis] Redis๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ ์บ์ฑ& ์กฐํ ์ฑ๋ฅ ๊ฐ์ ํ๊ธฐ
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์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋์ง๋ ํ ์คํธ ํด๋ณด์.
ํ ์คํธ ํต๊ณผ!!!^_^
๋!