13.[DRF] Cache 캐시의 개념과 사용법
캐싱
- 복잡한 계산의 결과를 저장해둬서 다음에 반복하지 않게끔
- 저장 장소는 데이터베이스, 파일 시스템, 메모리 - 각각 성능이 다름
- 설정에서 지정 - BACKEND와 LOCATION
메모리 저장 캐시
Memcached
- 데이터베이스 접근 횟수를 줄임
- 메모리에 저장됨
- 서버 무너지면 데이터도 유실됨
- 캐싱이나 메모리는 어차피 영구적인 데이터 저장목적은 아님
- 여러 서버에서 하나의 캐시로 공유 가능
레디스
- 레디스 서버가 로컬/리모트에서 돌아가야함
- 메모리에 저장됨
- 레디스 서버 여러개 돌릴 수 있음
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379',
}
}
데이터베이스 캐시
- 빠르고 인덱싱이된 데이터베이스가 있으면 유리함
- 만료된 캐시는 내가 함수로 호출해야함 - 자동이 아님
- 커맨드로 DB 캐시테이블 생성 가능
- 커맨드는 1개의 테이블만 생성
python manage.py createcachetable
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}
DB가 여러개인 경우
- DB 캐시 테이블들을 위한 라우팅이 필요
파일시스템 캐시
- 위치에 실제로 폴더가 존재해야하고 읽고 쓰기가 가능해야함
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'c:/foo/bar',
}
}
로컬 메모리 캐시
- 디폴트 캐싱
- 메모리캐싱의 빠른 속도를 원하지만 Memcached같은 걸 돌릴 능력이 없을 때 씀
- Location 생략가능
- 로컬 메모리 캐싱이 여러개면 적어도 한개는 이름 지정해서 분리해줘야함
- LRU(Least Recently Used) 컬링(골라모으는) 방법 사용
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
더미 캐시
- 프로덕션쪽에는 무거운 캐싱 의무가 있지만 개발/테스트쪽에선 안 할 때
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
캐시 변수
- TIMEOUT - 캐시 만료 (초), None 이면 만료안됨
- OPTIONS - 서드파티들이 옵션을 보냄
- MAX_ENTRIES
- CULL_FREQUENCY
- KEY_PREFIX - 모든 캐시 앞에 특정 문자열 붙일 수 있음
- VERSION - 캐시 키 버전 넘버
- KEY_FUNCTION
사이트 전체 캐싱
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
뷰 캐싱
- URL path에다가 cache_page 래핑하기
- view에다가 데코레이터 달아서 하는 방법도 있지만 비추천
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]
캐시 접근하는 방법(공통)
- 딕셔너리방식
- 없으면
InvalidCacheBackendError
fromdjango.core.cacheimport caches
cache1 = caches['myalias']
cache2 = caches['myalias']
cache1is cache2
True
디폴트 캐시 가져오기
from django.core.cache import cache #caches (복수) 아니고 cache (단수)
사용법
- 키는 문자열이여야하고, 밸류는 아무 객체나 가능
cache.set('my_key', 'hello, world!', 30)
키, 밸류, 타임아웃, 버전
cache.get('my_key')
# 'hello, world!'
# 30초 후에 만료됨
sentinel
- 해당 캐시에 값이 만료가 됐는지 확인할 때 + None을 저장했을 때
**sentinel** = object()
cache.get('my_key', **sentinel**) is sentinel
False
# Wait 30 seconds for 'my_key' to expire...
cache.get('my_key', sentinel)is sentinel
True
‘has expired’
- 캐시 안에 값이 있는지 확인
cache.get('my_key', **'has expired'**)
'has expired'
cache.add
- 키가 없을 때 추가
- 키가 이미 있다면 업데이트 하지 않음
cache.set('add_key', 'Initial value')
cache.**add**('add_key', 'New value')
cache.get('add_key')
'Initial value'
cache.get_orset
- 있으면 가져오고 아니면 저장하기
cache.get('my_new_key')# returns None
cache.get_or_set('my_new_key', 'my new value', 100)
'my new value'
cache.get_many
- 딕셔너리로 모든 키 가져옴
cache.set('a', 1)
cache.set('b', 2)
cache.set('c', 3)
cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
cache.set_many
- 딕셔너리로 키 여러개 저장
cache.set_many({'a': 1, 'b': 2, 'c': 3})
cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
cache.delete
cache.delete('a')
# True
cache.delete_many
- 지정한 키 여러개 삭제
cache.delete_many(['a', 'b', 'c'])
cache.clear
- 키 모두 삭제
cache.clear()
cache.touch
- 만료시간 수정
- 수정 성공 시 True, 실패 시 False
cache.touch('a', 10)
# True
cache.incr
cache.decr
- 값이 숫자인 경우, 값을 1 증가/감소 시킨다
cache.set('num', 1)
cache.incr('num')
2
cache.incr('num', 10)
12
cache.decr('num')
11
cache.decr('num', 5)
6
cache.close
- 캐시 연결 닫기
cache.close()
비동기 캐시
- 새로나옴
- 기본 함수들 다 쓸 수 있음
- 기본 함수 앞에 a를 붙임
await cache.aset('num', 1)
await cache.ahas_key('num')
True
캐시 컨트롤
- 2가지 캐시
- 내 브라우저 캐시 (프라이빗 캐시)
- 다른 제공자의 캐시 (퍼블릭 캐시)
- 여러사람이 사용하고 컨트롤해서 보안에 취약함
cache_control
- 프라이빗 캐시와 퍼블릭 캐시를 구분해준다
from django.views.decorators.cache import cache_control
**@cache_control(private=True)**
def my_view(request):
...
patch_cache_control
fromdjango.views.decorators.cacheimport patch_cache_control
fromdjango.views.decorators.varyimport vary_on_cookie
@vary_on_cookie
def list_blog_entries_view(request):
if request.user.is_anonymous:
response = render_only_public_entries()
patch_cache_control(response, public=True)
else:
response = render_private_and_public_entries(request.user)
patch_cache_control(response, private=True)
return response
Elasticache
- redis의 주소를 받으려면 AWS의 elasticache를 생성해서 엔드포인트를 받아야함
참고