날씨 어플리케이션을 개발하고 서비스를 운영하는 도중에
날씨 정보 조회시 로딩 속도가 너무 느림을 확인했고, 날씨 정보 조회 시 매번 상당히 많은 데이터들을 db로부터 가져오기 때문에 db가 병목 지점이라고 생각했고
날씨 정보가 업데이트가 일어나는 주기 동안은 해당 정보는 동일하므로, 같은 쿼리가 db에 나가는 것이 낭비라고 생각하였습니다.
즉, 날씨 업데이트 주기 동안에 같은 요청이 들어왔을 때에 이를 미리 캐싱해두면, 데이터베이스로 접근하지 않고 캐싱된 정보를 줄 수 있으므로 조회 성능을 높일 수 있다고 예측했습니다.
파레토 법칙이란 전체 결과의 80%가 전체 원인의 20%에서 일어나는 현상을 가리킵니다.
서비스에 빗대어 표현하자면 서비스의 총 80%의 활동을 20%의 유저가 하기 때문에 20%의 데이터만 캐싱해도 서비스 대부분의 데이터를 커버할 수 있다는 의미입니다.
즉, 캐시에 모든 데이터를 저장할 필요 없이 "파레토 법칙"에 따라 많이 사용되는 일부만 저장해도 대부분의 데이터를 커버할 수 있다는 저장 지침입니다.
즉, 캐싱을 하는 데이터는 자주 사용되며, 자주 변경되지 않는 데이터를 기준으로 하는 것이 좋습니다.
현재 서비스는 시/군/구 단위까지의 세부적인 지역에 대한 날씨 정보를 제공하고 있습니다.
또한 모든 지역 단위에 대해 동일한 시간 간격으로 (동일한 업데이트 주기로) 날씨 데이터가 업데이트되므로, 사용자의 요청이 많은 지역에 대해서 캐시를 도입하기로 결정했습니다.
사용자의 요청과 트래픽 분포가 가장 많았던 서울 지역(총 25개 자치구)에 대해 캐시를 적용하기로 결정했습니다.
저는 docker 에서 Redis 서버를 띄우고, DJango에서 연결해서 이를 구현하였습니다.
간단히 그 방법을 소개해드리겠습니다.
docker pull redis:alpine
을 입력합니다.
다음으로는
docker network create redis-net
을 입력하여 redis network를 만들어줍니다.
docker run --name djangoredisserver -p 6379:6379 --network redis-net -d redis:alpine redis-server --appendonly yes
docker run -it --network redis-net --rm redis:alpine redis-cli -h djangoredisserver
다음 두 명령어까지 모두 입력하면, redis 서버가 정상적으로 작동함을 확인할 수 있습니다.
그 다음 Django에서 해주어야 할 것들은 다음과 같습니다.
먼저 settings.py에 캐시 관련 정보를 추가해줍니다.
# Redis Cache
CACHE = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379"
}
}
그리고 그 다음 pip install django-redis
로 라이브러리를 설치해주면 끝입니다!
이렇게 해 준 다음, 캐싱을 적용할 view에서
from django.core.cache import cache
를 이용하여 이를 써주면 됩니다.
튜토리얼이나 공식 문서에 이용법은 자세히 나와있으니, 이용법을 따로 설명하지는 않겠습니다
저는 서울시 25개의 자치구에 대해서 날씨 정보 조회 시 캐싱을 적용했고,
이를 적용하기 전과 후의 조회 성능 차이를 비교해보았습니다.
125ms -> 17ms 로 조회 성능이 크게 개선되었음을 확인할 수 있었습니다
날씨 정보 조회시 로딩 속도가 너무 느린 것을 db가 가장 큰 병목 지점이라고 예측했던 것도 예상에 부합한 것 같습니다.
현재의 사용자 요청이 서울 지역이 가장 많아 서울 지역을 중점으로 캐싱을 적용했지만, 이후 사용자의 접근 패턴이나 트래픽 분포에 따라 추가적인 다른 지역들에 대해서도 캐싱이 필요하다면 이를 도입하고자 합니다.
캐시 이용 시, 중요한 부분 중 하나는 바로 실제 데이터와의 정합성을 잘 고려해주어야 합니다.
저는 비동기 작업으로 데이터베이스에 있는 날씨 데이터가 업데이트가 일어나는 시점에 동시에 캐시의 데이터도 업데이트 시켜주어 데이터베이스와 캐시 데이터 간의 데이터 정합성에 문제가 발생하지 않도록 해주었습니다.
데이터베이스 업데이트와 Redis 업데이트는 atomic하게 관리되어야 합니다. 예를들어, Redis 업데이트는 성공했지만, 데이터베이스 업데이트가 실패하면 불일치 문제가 발생할 수 있습니다.
이의 문제를 해결하기 위해 저는 데이터베이스의 업데이트가 성공적으로 완료된 후에 Redis를 업데이트 하는 방식으로 이를 설계하였습니다.
즉, 정리하자면
1. 데이터베이스 먼저 업데이트
2. 데이터베이스 업데이트 후 Redis 업데이트
데이터베이스와 캐시 메모리 간의 정합성 및 동기화는 애플리케이션의 성능과 안정성에 큰 영향을 미치므로, 캐싱 전략을 구현할 때 중요하게 고려해야합니다.