프로젝트 개선 중 nGrinder로 부하테스트를 거치며 성능 개선을 위해 캐시를 적용하기로 했다.
캐시
캐시는 데이터를 임시로 저장해준 뒤, 동일한 데이터 요청이 오면 DB에 접근하지 않고 저장된 캐시 데이터를 반환해주는 방식이다. DB 접근 없이 요청을 처리할 수 있기 때문에 성능 개선 측면에서 많은 이점이 있다.
캐시 적용을 위해 고려해야할 사항
1. 어떤 데이터에 캐시를 적용할 것인가
캐시를 적용하는 것이 무조건 성능 개선을 가져오는 것이 아니다.
캐시는 생성, 수정, 삭제 연산보다는 조회 연산이 많은 데이터에 적합하다.
자주 변경되는 데이터에 캐시를 적용하면 데이터 정합성 문제가 발생한다.
💡 데이터 정합성 : 같은 데이터인데 캐시와 DB에 있는 정보가 서로 다른 경우
- 변경이 많은 데이터일수록 데이터 일관성을 유지하기 위한 연산이 지속적으로 필요하기 때문에
변경보다는 조회가 많은 데이터가 캐시를 적용하는 데 적절하다.
2. 캐시 설계 전략
적절한 캐시 설계 전략을 선택해야 데이터 정합성 문제를 해결하면서도 성능 개선을 할 수 있다.
캐시 읽기 전략
- Look Aside : 데이터가 캐시에 있는 지 확인한 후 없으면 DB에서 조회
- Read Through : 데이터가 캐시에 있는지 확인 -> 없을 경우 DB에서 데이터를 조회하여 캐시 업데이트 -> 다시 캐시에서 데이터 조회
- Look Aside와 비슷하지만 캐시 저장소에 캐시를 저장하는 주체가 DB이다.
- 직접적으로 DB에 접근하는 것을 막을 수 있지만 캐시에 장애가 발생하면 서비스가 전체 중단된다.
캐시 쓰기 전략
- Write Back : 모든 데이터를 캐시 저장소에 저장 -> 일정 시점마다 DB에 저장
- 데이터를 캐시에 모아두었다가 일정 시점마다 DB에 반영하는 방법으로 쓰기 쿼리 횟수를 줄일 수 있다
- 캐시 저장소에 장애가 발생하면 DB에 저장되기 전에 데이터가 유실될 수 있는 문제가 있다.
- Write Through : 데이터 캐시 저장소에 저장 -> 바로 DB에 저장
- 데이터를 캐시와 DB에 동시에 저장하는 방식으로 동기화가 유지되어 데이터 일관성을 유지할 수 있다.
- 매 요청마다 2번의 쓰기 작업이 발생하므로 상대적으로 속도가 느린 성능 문제가 있다.
- 캐시에 넣은 데이터를 사용하지 않는 경우에는 리소스가 낭비도니다.
- Write Around : 모든 데이터는 DB에 저장
- 캐시를 갱신하지 않고 모든 데이터가 DB에 저장되는 방식이다.
- Write Through 보다 훨씬 빠른 속도를 갖는다.
- 캐시와 DB의 데이터가 다른 데이터 정합성 문제가 발생할 수 있다. 따라서 DB의 데이터가 수정, 삭제될 때마다 캐시도 함께 수정하거나 삭제해야한다.
다음 세가지의 조합을 일반적으로 많이 사용한다.
- Look Aside + Write Around
- Read Through + Write Around
- Read Through + Write Through
나는 프로젝트에서 1번 Look Aside + Write Around 조합으로 캐싱을 적용하려고 한다.
3. Local Cache vs Global Cache
캐시를 적용할 때 캐시를 각 서버에 저장할지 외부 서버에 따로 캐시 저장소를 둘 지 결정해야 한다.
Local Cache
서버마다 캐시를 따로 저장하는 방법이다.
네트워크 통신이나 데이터를 외부에서 가져오는 등의 오베헤드가 없기 때문에 속도가 빠르다
하지만 다중 서버 환경에서 데이터 정합성 문제가 발생하며, 동기화를 위한 비용이 발생한다.
Global Cache
외부에 따로 캐시 저장소를 두는 방법이다.
외부 저장소에 접근하여 데이터를 가져오는 과정에서 네트워크 통신이 발생하기 때문에 로컬 캐시보다는 느리다.
다중 서버 환경에서 모든 서버의 인스턴스가 동일한 캐시 저장소에 접근하기 때문에 데이터의 일관성을 보장할 수 있다.
서버의 인스턴스가 추가되어도 동일한 비용이 들기 때문에 서버가 고도화 될수록, 캐시 데이터 크기가 많을 수록 글로벌 캐시가 유리하다.
데이터 정합성 문제가 서비스에 영향을 준다면 글로벌 캐싱 전략을 선택하는 것이 바람직하다.
나는 적용하려는 이커머스 프로젝트가 서버의 스케일 아웃 상황을 가정하고 있고, 특히 이커머스 플랫폼의 상품 같은 경우는 가격 정보가 동기화되지 않으면 서비스에 심각한 결함이 생기기 때문에 글로벌 캐싱 전략을 선택했다.
추가적으로 글로벌 캐싱을 위한 외부 저장소를 선택해야한다.
난 Redis를 쓰기로 결정했는데,
- Redis는 인메모리 DB로 속도가 빠르고
- Redis 다양한 자료구조를 지원하기 때문에 다양한 캐싱 시나리오에 유리하며,
- 이미 프로젝트에 Redis가 적용되어있어 추가적인 설정이 필요 없었다.
다음 포스트에서 계속..
참고
https://inpa.tistory.com/entry/REDIS-📚-캐시Cache-설계-전략-지침-총정리
'트러블슈팅' 카테고리의 다른 글
[Spring] Redis 캐시 적용하기 (2/2) (0) | 2023.10.22 |
---|---|
[Spring] Redis를 통한 세션 불일치 문제 해결 (2/2) (0) | 2023.10.14 |
[Spring] Redis를 통한 세션 불일치 문제 해결 (1/2) (0) | 2023.10.12 |
[Spring] 재고처리 동시성 이슈 해결하기 (2/2) (0) | 2023.07.22 |
[Spring] 재고처리 동시성 이슈 해결하기 (1/2) (0) | 2023.07.22 |