•
Redis 캐시란
◦
Redis Cache는 Redis에서 제공하는 캐시 관리 기능
◦
사용자가 응용프로그램의 더 나은 성능을 위해 적용/사용
◦
엑세스 되는 데이터를 저장하는 캐시
◦
한번의 데이터 요청은 하나의 네트워크 호출인데, 이를 최소화 할 수 있음
•
@EnableCaching
◦
SpringBoot에게 캐싱기능이 필요하다고 전달
◦
SpringBoot Starter class에 적용
•
@Cacheable
◦
DB에서 애플리케이션으로 데이터를 가져오고 Cache에 저장하는데 사용
◦
DB에서 데이터를 가져오는 메서드에 적용
◦
사례
▪
@Cacheable(value = "popularBooks", key = "#user.id", unless = "#result.isEmpty()") :
▪
value는 캐시 이름을 지정하며, 이 예에서는 "popularBooks"로 설정했습니다.
▪
key = "#user.id"는 캐시 키를 지정하며, 여기서는 사용자 ID를 사용합니다.
▪
unless = "#result.isEmpty()"는 반환된 결과가 비어 있을 때 캐싱하지 않도록 설정합니다.
캐싱의 적용 대상이 되는 데이터
1. 빈번한 접근
캐싱은 위에서 파레토 법칙에서 보았듯이 자주 사용하는 데이터에 가장 효과적이다. 자주 사용되는 데이터의 복사본을 저장하여 이후 데이터의 요청이 올 때마다 원본의 데이터를 주기 위해 DB에 접근하는 것을 막아 성능을 향상할 수 있다.
2. 데이터를 처리하기 위해 복잡한 로직이 필요한 데이터
데이터를 반환하기 위해 복잡한 로직이나, DB에서 데이터를 가져오기 위해 쿼리문이 복잡한 경우에도 캐싱을 적용하면 성능을 향상 시킬 수 있다. 캐싱을 적용하면 또 복잡한 계산을 할 필요가 없기 때문이다.
3. 자주 변경되지 않는 데이터
만일 데이터가 자주 업데이트가 된다면 캐싱하기 적절한 데이터가 아니다. 자주 업데이트 되는 데이터에 캐싱을 적용하면 데이터 정합성 문제가 발생하게 된다.
데이터 정합성이란?
데이터 정합성은 같은 데이터임에도 불구하고 캐시와 DB에 있는 데이터의 정보가 다른 상황을 말한다.
쉽게 예시를 들면 계좌 조회를 하는데 처음 계좌 조회를 할떄는 10,000원을 조회하였는데
1,000원을 송금하여 계좌 DB에는 9,000원이 저장이 된다.
만일 이 상황에서 또다시 계좌 조회를 하게 되면 캐시를 반환하기 때문에 사용자는 10,000원을 조회하게 된다.
즉, 10,000(캐시) != 9,000(DB) 데이터가 일치하지 않는 데이터 정합성 문제가 발생하게 된다.
위와 같은 문제 때문에 자주 변경이 되지 않는 데이터에 캐싱을 적용해야 한다.
Plain Text
복사
•
레디스 캐싱 적용 시나리오
1.
해당 프로젝트의 경우 첫 메인화면 접속 시 인기 스터디, 인기 문제집을 요청한다. 인기 스터디의 경우 인원수를 기반으로, 인기 문제집은 북마크 수를 기반으로 조회를 하는데 트래픽이 많이 발생하지 않는 해당 프로젝트 경우 인기 스터디와 인기 문제집이 변경되는 경우가 많지 않기에 적절한 데이터라고 생각을 하고 적용을 해보았다.
2.
문제집, 스터디 서비스 중에서 복잡한 로직 또는 쿼리로 구성하여 데이터를 처리하는 메소드들은 캐싱을 활용하여 성능 개선을 하였다.
3.
자주 변경되는 데이터들 이를테면 사용자가 추가한 문제, 스터디를 보여주는 문제집 리스트, 스터디 리스트 등에는 캐싱을 적용하지 않았다.
•
레디스 캐싱 적용 메소드
◦
BookServiceImpl - 인기 문제집 조회 (1번 케이스)
▪
getPopularBooks
◦
BookServiceImpl - 문제집 카테고리별 조회 (2번 케이스)
▪
getBookListByCategory
◦
StudyServiceImpl - 조회 관련 기능 전체 적용 (1, 2번 케이스 - 인기 스터디 포함)
◦
StudyUserQueAnswerServiceImpl - 조회 관련 기능 전체 적용 (2번 케이스)
DB 로부터 데이터를 조회하는 경우, 동일한 데이터를 반복하여 조회함으로써 불필요한 일을 반복하는 문제가 발생한다. 캐시를 통해, DB 로부터 반복적으로 데이터를 조회해 오는 일에 대해, 최초 데이터를 조회하여 캐시에서 데이터를 조회해 오는 처리를 수행함으로써 API 의 성능을 올리며 응답시간을 단축하는 효율성을 가져올 수 있다.
레디스를 사용하는 이유 정리
1. 높은 성능: 레디스는 모든 데이터를 메모리에 저장하므로 디스크 I/O 오버헤드가 없다. 이로 인해 매우 빠른 읽기와 쓰기 작업을 수행할 수 있다. 또한 단일 쓰레드 모델을 사용하여 데이터베이스 작업을 순차적으로 처리하므로 복잡한 동시성 문제를 피할 수 있다.
2. 다양한 데이터 구조: 레디스는 단순한 키-값 구조 이상의 다양한 데이터 구조를 지원한다. 문자열, 해시, 리스트, 셋, 정렬된 집합 등 다양한 데이터 유형을 저장하고 조작할 수 있다. 이를 통해 레디스는 많은 응용 프로그램에서 데이터 구조를 효과적으로 모델링할 수 있다.
3. 캐싱: 레디스는 주로 데이터베이스 쿼리나 계산 결과를 캐싱하는 데 사용된다. 많은 쿼리 작업을 레디스에 저장하여 데이터베이스에 대한 부하를 줄이는 데 도움이 된다. 레디스는 인 메모리 데이터베이스이므로 빠른 응답 시간을 제공하여 애플리케이션 성능을 향상시킨다.
4. 메시지 브로커: 레디스는 Pub/Sub 모델을 지원하여 메시지 브로커로 사용될 수 있다. 다양한 컴포넌트나 서비스 간의 비동기 통신을 위해 사용되며, 실시간 채팅, 이벤트 기반 시스템, 실시간 분석 등에 적합하다.
5. 세션 저장소: 레디스는 세션 데이터를 저장하는 데 사용될 수 있다. 세션 클러스터링과 부하 분산을 지원하여 대규모 웹 애플리케이션에서 확장성과 고 가용성을 제공할 수 있다.
6. 대기열 관리: 레디스는 메시지 큐 시스템으로도 사용될 수 있다. 작업 처리를 비동기적으로 관리하기 위해 레디스의 리스트 데이터 구조를 활용하여 작업을 대기열에 추가하고, 워커 프로세스가 해당 작업을 처리하는 방식으로 사용할 수 있습니다. 이를 통해 작업의 순서와 우선순위를 조절하고, 작업 처리량을 제어할 수 있다.
7. 실시간 데이터 분석: 레디스는 실시간 데이터 처리 및 분석에 사용될 수 있다. 스트림(Stream) 데이터 구조를 활용하여 이벤트 시퀀스를 저장하고, 소비자가 실시간으로 이벤트를 처리하고 분석할 수 있다. 이를 통해 실시간 대시보드, 실시간 알림, 로그 분석 등 다양한 실시간 데이터 처리 시나리오를 구현할 수 있다.
8. 분산 환경 지원: 레디스는 마스터-슬레이브 복제, 클러스터링 등의 기능을 제공하여 데이터의 가용성과 확장성을 높일 수 있습니다. 여러 서버에 데이터를 분산 저장하고 복제하여 고가용성을 보장하고, 수평적인 확장이 가능하다.
9. 간편한 사용 및 다양한 언어 지원: 레디스는 간단하고 직관적인 명령어를 제공하며, 다양한 프로그래밍 언어에서 접근할 수 있는 클라이언트 라이브러리를 지원한다. 이를 통해 레디스를 편리하게 사용할 수 있으며, 다양한 환경과 언어로 개발된 애플리케이션과 통합할 수 있다.
이러한 이유들로 인해 레디스는 높은 성능, 다양한 데이터 구조, 캐싱, 메시지 브로커, 세션 저장소, 대기열 관리, 실시간 데이터 분석, 분산 환경 지원 등의 다양한 사용 사례에 적합한 선택지가 된다.