00.Redis
01. Redis가 필요한 경우들
02. Redis 기본 구조
03. 캐시 읽기 전략 (Read Cache Strategy)
04. Django With Redis
00.Redis (Remote Dictionary Server): 데이터를 빠르게 저장하고 가져오는데 탁월한 성능을 발휘하는 도구
- 인-메모리(In-memory) 데이터베이스로 메모리를 저장소로 사용
- 한 마디로 아주 빠름 (Disk에 비해 약 1,000배 이상)
- value에 String, Set, Hash 등 다양한 종류의 타입을 지원
- 복잡한 쿼리가 필요 없음
- 그런데 잘 잊어버림 (인메모리라 휘발성, 근데? 보완책 있음)
- 한 번에 하나의 일만 처리할 수 있음 (어차피 빠르고, 순서를 보장해서 꼬일일이 없음. 장점!!)
01. Redis가 필요한 경우들
Ex01) 조회수, 방문자수 등을 우리가 보여줘야한다면?
- DB에 직접 저장할 수 있겠지만 1초에 너무 많은 업데이트가 필요한 경우, 많은 DB 리소스를 소모함. (때에 따라 처리가 안되거나 누락되기도 함.)
→ 빠르게 응답이 가능한 Redis로 처리. 모든 처리를 Redis를 이용하고, 영속적인 저장이 필요할 때 데이터를 DB로 옮김.
Ex02) 빈번하게 업데이트 되는 데이터라면?
- 좋아요/팔로우 등의 기능은 버튼을 누를때마다 DB에 읽기/쓰기 연산이 이루어짐
- 다량의 insert/update는 RDB의 성능을 크게 저하시킴
- 실제로 좋아요/팔로우를 DB 사용으로 구현해두고 2-3명이 동시에 버튼을 여러번 눌러보면 생각대로 동작하지 않는 것을 볼 수 있음.
- 이벤트를 하게되어 실시간 랭킹이 필요하다면?
- 이 역시 RDB로는 한계
→ Redis를 이용해서 처리하면 초당 16만건 이상을 처리할 수 있음.또한 내부적으로 key-value 형태를 이용하여 각 사용자당 하나의 좋아요/팔로우만 할 수 있게 하는등의 처리가 용이
Ex03) 세션 / 실시간 검색 …
- 실시간으로 로그인 해있는 유저를 파악하는 것이 필요하다면?
- 모든 유저가 검색하는 것에 대한 실시간 파악이 필요하다면?
→ 즉, RDB로 처리하기에는 너무나 많은 쿼리가 발생할 것 같을때
→ RDB에 바로 저장해야할 만큼 중요하지 않은 데이터일때
→ 너무 데이터의 크기가 크지 않을때(메모리 쓰기때문에 귀중한 자원임)
Redis 사용을 생각해 볼 수 있음.
Ex04) 서버가 여러개로 많아질 때...
캐싱(Caching)
우리가 많이 사용하는 Relational Database는 빠르지만, 또한 빨라졌지만 아직 느림.
- 고객의 수가 많아질수록, 요청이 증가할수록 우리의 DB는 점점 부하가 걸림.
예시) 우리가 도서 관련 서비스를 하는데 항상 유저에게 보여줘야하는 도서 데이터가 있다면? (월간 베스트 셀러)
- 모든 유저에게 매번 데이터베이스에서 읽어서 보여주는게 아니라, 미리 불러오기(Load)해서 Redis에 저장을 해두고 사용하자 !→ 캐싱 Caching
- → 데이터베이스를 거치지 않고 Redis에서 바로 가져와서 응답이 가능
단점
- Redis는 메모리를 사용하기 때문에 빠른 것
- 하지만 💰이 정말 많다면 가능한 이야기일수도...?(인스타그램 등)
02. Redis 기본 구조
1️⃣ (선택) DB에서 필요한 데이터를 미리 Caching (Cache warming)
2️⃣ 요청이 들어오면 Caching 되어 있는지 확인 (cache-key 이용)
2-1. Caching 되어 있다면 바로 조회해서 처리 (Cache Hit)
2-2. Caching 되어 있지않다면 (Cache Miss)
(1) DB에서 데이터를 조회해서
(2) Caching 하고
(3) 요청을 처리
- Redis는 결국 또 다른 데이터베이스를 사용하는 것.
- 이는 데이터를 관리하는 곳이 두 곳으로 늘어난다는 것.
- 두 데이터베이스 사이에 정합성이 깨지지 않게 관리가 필요하게 된다는 것을 의미
캐싱을 고려하려면? 🤔
- 데이터가 한 번 작성된 이후 여러번 읽혀지는 경우인가?
- 반복적으로 조회되는 데이터가 고유한 데이터인가?
- 데이터의 크기가 크지는 않은가?
03. 캐시 읽기 전략 (Read Cache Strategy)과 캐시 쓰기 전략 (Write Cache Strategy)
- 캐시 읽기 전략 (Read Cache Strategy)
1️⃣ Look Aside(Cache Aside) 패턴
- 요청이 들어와서 데이터를 찾을때 캐시를 먼저 찾아봄
- 만약 캐싱되지 않았다면 DB를 조회
- 가장 기본이 되는 캐시 전략
- 캐시와 DB가 분산되어 운용되므로 redis가 죽어도 서비스에 문제가 없음
- 하지만 redis가 죽었을때 요청이 한번에 DB로 몰리면 서비스 장애 가능성 있음
- 최초에 캐시로 데이터를 넣어주는 작업 필요 (Cache Warming)
2️⃣ Read Through 패턴
- Look Aside와 비슷하지만 캐시만 바라보고 데이터를 조회하는 것
- 캐싱하는 로직은 직접 처리하지 않고 다른 라이브러리에게 위임 즉, 자동으로 DB와의 데이터가 동기화가 이루어지도록 하는 방식
- 캐싱을 적극적으로 이용할 수 있으나, Redis가 다운될 경우 서비스가 중지됨 (라이브러리가 캐시 데이터를 DB에 저장하기 전에 다운되면 데이터도 유실됨)
- 캐시 쓰기 전략 (Write Cache Strategy)
1️⃣ Write Back(Write Behind) 패턴
- 데이터를 저장할 때 바로 DB에 저장하는 것이 아닌 캐시에 모아 두었다가 한 번에 저장
- (N개의 데이터를 하나씩 저장하는 것보다, bulk create 하는것이 더 빠르기 때문)
- 캐시에서 장애가 발생할 시 데이터 누락의 가능성이 있음
2️⃣ Write Through 패턴
- 데이터를 캐시에도 저장하고, DB에도 저장하는 방식
- 캐시를 이용해서 DB를 동기화
- 캐시의 데이터가 항상 최신 데이터로 유지됨
- 두번의 저장이 이루어지기 때문에 데이터 유실에 민감한 로직에서 사용
3️⃣ Write Around 패턴
- 모든 데이터는 DB에 바로 저장
- cache miss가 발생했을때만 캐시와 DB에 저장
- 캐시와 DB간 데이터 불일치 가능성
- cache miss가 발생하기 전까지 DB에서 내용이 수정되었다면 서비스에 반영되지 않음 (cache가 만료될 때까지 유지됨)
- cache의 만료시간(TTL)을 짧게 잡고 사용
일반적으로 Look Aside - Write Around 전략을 씀.
03. Redis 가볍게 사용하기
- Redis도 결국 하나의 프로그램
- 배포 환경에서는 Redis만 설치되어있는 컴퓨터를 따로 사용 (DB Server처럼, Redis Server를 따로 둠)
- 하지만 우리는 배우는 단계이므로, 내 컴퓨터에 Redis를 설치하고 띄우는 것
Redis 설치(Window. Mac은 다른 글 보세요.... ㅎㅎ)
링크: https://github.com/microsoftarchive/redis/releases접속 후 .msi 다운
redis-cli 사용
- C / Program Fiels / Redis / redis-cli.exe 를 실행하면 redis-cli를 볼 수 있다.
- redis-cli는 redis에 명령을 입력할 수 있는 도구 !
Redis 환경 변수 등록하기 (optional)
- 항상 redis를 클릭해서 접속하는 것이 귀찮다면 환경 변수에 등록
- 이제 명령 프롬프트 (not GitBash) 에서 redis-cli 만 입력해도 실행이 가능
- ping 을 입력해서 PONG 이라는 답이오면 redis가 잘 실행되고 있는것 !
keys * # 모든 key 조회
- keys * 명령어는 redis가 가지고 있는 모든 key를 조회하는 큰 부하가 들어가는 명령어
- redis는 한 번에 하나의 일을 처리하기에 keys * 같은 명령어는 정말 조심 !
- (※특히 프로덕션 환경에서는 결코 사용해서는 안됨 ※ )
아래와 같이 key값으로 조회해서 하나의 명령만 시켜야 부하가 안걸림
get key # Key에 해당하는 value 조회
Create
set key value # key value 저장
setex key seconds value # seconds초 뒤에 삭제되는 key value를 저장
Update
rename key newkey # key 이름 변경
expire key seconds # 해당 Key의 데이터 만료시간을 seconds초로 설정
Delete
del key1 [key2 ...] # key 삭제
04. Django With Redis
- Django는 기본적으로 caching 기능을 내장
준비하기
seed를 이용해서 1만개의 product 데이터 만들기
python manage.py seed products --number=10000
모든 product를 조회하는 API를 만들기
api_pjt/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
...
path("api/v1/products/", include("products.urls")),
]
...
products/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.product_list),
]
products/serializers.py
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = "__all__"
products/views.py
from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Product
from .serializers import ProductSerializer
# Create your views here.
@api_view(["GET"])
def product_list(request):
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
그냥 조회해보기
캐싱 사용하기
from django.core.cache import cache
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Product
from .serializers import ProductSerializer
@api_view(["GET"])
def product_list(request):
cache_key = "product_list"
if not cache.get(cache_key):
print("cache miss")
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
json_data = serializer.data
cache.set(cache_key, json_data, 10)
response_data = cache.get(cache_key)
return Response(response_data)
10초후 만료되는 데이터 캐싱
→ 엇? 그런데 Redis에 내가 저장한 key가 없다 ?? 👀
Redis를 Cache로 사용하기
Local Cache
- 서버 컴퓨터의 자원을 사용해서 각 서버에 캐시를 저장. 즉, 서버가 여러대로 늘어난다면 서버마다 개별 캐시가 있음
- 서버 내에서 동작하기 때문에 속도가 빠름
- 서버 수가 늘어날수록 모든 서버간 캐시 동기화를 위해 추가 리소스 소모
Global Cache
- 별도의 캐시 서버를 두고 운용하는 방법으로 서버 간 데이터 공유가 쉬움
- 데이터를 분산해서 저장할 수 있음. 자세한건 Replication과 Sharding을 키워드로 검색
- 캐시 서버를 공유하고 있으므로 캐시 동기화에 추가적인 리소스x. 서버 수가 늘어나는 경우 효과적인 방법
- Django는 기본적으로 시스템의 메모리를 이용해서 캐시를 저장
- https://docs.djangoproject.com/en/4.2/ref/settings/#caches
- Redis를 캐싱으로 사용하기 위해서는 설정이 필요.
→ 우리가 Redis를 사용해도 각 서버에서 별도의 Redis를 운용하면 Local Cache
- Django와 Redis 사이를 연결해주는 미들웨어 설치
- django-redis는 django가 기본으로 가지고 있는 cache 기능을 활용해서 편하게 캐싱을 구현할 수 있도록 지원
- django-redis를 설치하지 않아도 redis 사용가능 (Django 문서 참고)
- https://github.com/jazzband/django-redis
pip install django-redis
...
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
...
그런데 왜 product_list 가 아니라 :1: 이 붙는거지? 👀
https://docs.djangoproject.com/en/4.2/topics/cache/#cache-key-transformation
- 포트폴리오에 들어가야 하는것은 Redis를 써봤다! 가 아니라 아래의 고민이 들어가야 함.
내가 어떤 상황에서 어떤 문제를 발견했고, 해당 문제를 해결할 방법들을 고민하던 중 이러한 해결방안이 필요했고, 그 결과 Redis를 선택했다. Redis를 선택한 이유는 무엇이며, Redis를 이용해 캐싱을 적용한 결과 어떠한 향상을 얻을 수 있었다.
- 물론, 프로젝트를 하면서 Redis를 써야할만큼의 부하가 발생하지 않을 수 있기에, 부하를 만듦
- 데이터베이스에 아주 많은(몇십만건) 데이터를 넣어두고
- API 요청도 Postman으로 한두개 누르는게 아닌, 가짜 요청을 엄청 많이 만들 수 있음
- 이런걸 ‘부하테스트’라고 함
- 더 자세한건 Locust 를 검색
'Django' 카테고리의 다른 글
Django 서버 배포하기 (4) | 2024.09.03 |
---|---|
외부 API 연동하기 (1) | 2024.09.01 |
Django ORM 최적화 (0) | 2024.08.30 |
Django ORM (심화) (0) | 2024.08.30 |
JSON Web Token, JWT (0) | 2024.08.30 |