ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Redis로 Cache Server를 만들어 보자! (feat. django-cacheops)
    💻 프로그래밍/Django 2021. 2. 18. 07:28

    안녕하세요! 운동하는 개발자 Jay입니다 😄

    오늘은 이름하여 c.a.c.h.e 를 Django에서 사용해보려고 합니다.

    지금 진행 중인 토이 프로젝트가 있는데, 이 프로젝트에서 캐시를 써보려고 해요! 캐시란 무엇이고 왜 사용하는지를 하나씩 알아가 보겠습니다!

     

    1. What is Cache? 💰 (Cache is Cash.... 는 드립)


    캐시(Cache)자주사용하는 데이터를 미리 복사해 놓은 저장소를 말합니다. 쉽게 설명하자면 내 키(key와 키를 이용해 뒤에서 설명까지 이어지기 위한 라임 this is 언어유희) 를 알고 싶은데, 매번 키를 재는 게 아닌 처음 재논 키의 정보를 저장해놔서 원할 때마다 그 정보를 보는 게 캐시입니다.

    Cache, DB 설명

    좀 더 Django와 연관 지어서 설명해보겠습니다. Django에서 ORM을 사용하게 되면 DB에 query가 나가게 됩니다. 사용자가 많아지고 그만큼 query가 많아지면 DB부하가 많아지고 서비스가 느려질 수 있습니다. 

     

    이럴 때 캐시를 사용하면 동일한 ORM을 호출할 때 이미 캐시에 저장되어 있는 경우 query를 하지 않고 캐시에서 ORM의 결과를 가져오게 됩니다. 그럼 DB부하가 줄어들겠죠?!

     

    이런 캐시 시스템(?), 서비스 들 중 Memcached, Redis 등이 있습니다. 캐시는 하드웨어적, 애플리케이션 등에 따라 다양한 종류가 있는 것 같습니다.  But 제가 실제로 사용해본 캐시 DBRedis(Remote Dictionary Server)라서 이번 포스팅에서는 Redis를 기준으로 캐시를 적용하는 방법에 대해서 알아보겠습니다.

     

    2. What is Redis? 🎈


    Redis"key-value" 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈소스 기반의 DBMS입니다. 여기서 중요한 건 "key-value" 구조라는 것이다. key값으로 value를 꺼내올 수 있다는 것이다 (dictionary 같은?)

     

    Redis에 대해서 자세한 설명은 여기서 확인하면 좋을 것 같고, 이번 포스팅에서는 개인적으로 중요하다고 생각하는 RedisLRU(Least recently used) Cache에 대해서 집중 설명하겠습니다.

     

    LRU Cache는 오래된 캐시를 어떤 정책과 방식으로 삭제할지에 대한 옵션입니다. 

     

    1. maxmemory

    redis에서 사용할 수 있는 최대 메모리를 설정해줍니다.  0인 경우 제한 없음입니다.

    ex) maxmemory 100mb 


    2. maxmemory-policy

    최대 메모리를 넘어가는 경우 어떻게 처리할지에 대한 정책입니다.

     

    - noeviction : 메모리 제한에 도달했을 때, 클라이언트에서 그 이상의 메모리를 사용하려고 하면 오류를 반환합니다. (대부분 쓰기 명령이지만 DEL과 몇 예외가 있음)

     

    - allkey-lru : 새로 추가된 데이터의 메모리 확보를 위해서 최근에 덜 사용된 (LRU) key를 제거합니다.

     

    - volatile-lru : erpire_set(만료시간)이 있는 key 중에서 최근에 덜 사용된(LRU) key를 제거합니다. 

     

    - allkeys-random : 메모리 확보를 위해서 랜덤으로 key를 제거합니다.

     

    - volatile-random : expire_set이 있는 key 중에서 랜덤으로 제거합니다.

     

    - volatile-ttl : erpire_set이 있는 key를 제거합니다. 다만, TTL(Time To Live)이 짧은 key를 제거합니다.

     

    일단은 maxmemory, maxmemory-policy만 알아도 기본적인 튜닝은 끝입니다! (아마두?!)

     


    3. Redis 설치해보자! ⚙️


    Ubuntu 서버에 Redis-server를 설치해 보겠습니다.

    apt-get으로 설치
    sudo apt-get update
    sudo apt-get upgrade
    sudo apt-get install -y redis-server
    server 메모리 확인 후 메모리 할당 (vmstat -s)

    사실 봐도 헷갈린다...

    일단 Total은 4 Gbyte이고, free memory만 봤을 때 0.19 Gbyte...?

     

    아니 아무것도 안 하는데? htop으로 봐도 그렇게 사용 안 하는데... 흠 이건 찾아보니 리눅스는 사용하지 않은 메모리도 캐시, 버퍼로 사용한다고 하더라고요!! 

     

    free -m 명령어로 확인

    buff/cache가 3.1 Gbyte라....! 여기서 봐야 할 곳은 avaliable인데 avaliableswapping 없이 새로운 애플리케이션을 실행 가능한 가용 메모리의 크기라고 하네요.

     

    뭐... 여하튼 전 대략 avaliable이 3 Gbyte 되니까, Redis maxmemory1 Gbyte로 잡겠습니다.

    메모리 관련해서 자세한 설명은 여기에서 참고해주세요!! (봐도 봐도 헷갈리네요 ㅋㅋ)

     

    sudo vi /etc/redis/redis.conf로 cofig를 수정합니다.
    maxmemory = 1G 
    maxmemory-policy = volatile-lru

    이렇게 설정합니다. maxmemory-policy = volatile-lru로 설정한 이유는 Cacheops 설명을 하면서 추가로 설명하겠습니다.

     

    설정 완료 후 마지막으로 redis를 재실행, 서버가 시작되면 자동 시작되도록 설정합니다.

    sudo systemctl restart redis-server.service
    sudo systemctl enable redis-server.service

    Redis가 동작하는지 한번 확인해보겠습니다.

     

    redis-cli

    를 입력하면 redis-server에 접속해서 캐시를 get, set 할 수 있습니다. 저는 접속 확인만 하고 따로 명령어를 통해 캐시를 만들지 않겠습니다. (뒤에서 직접 api 호출해서 확인할 예정) 자세한 명령어를 알고 싶으신 분들은 공식문서에서  확인해보세요!

     

    4. DjangoRedis Cache 적용해보기 🎉


    이제 Django ORMCache를 적용해보겠습니다. 일반적인 캐시 설정도 있지만 이번 포스팅에서는 ORM 캐시를 할 수 있는 라이브러리를 사용해보겠습니다. Django ORM 캐시 라이브러리는 CachealotCacheops가 있습니다. 저는 이 두 가지만 써봐서... 아마 이 두 가지가 제일 유명하지 않을까 생각되네요!

     

    저희는 Cacheops를 사용할 겁니다. 그 이유는 Cacheopsrow단위로 캐시가 가능해서 효율이 더 높다고 알고 있습니다. 

    Cachealot 문서에서 설명하는 각 캐시 라이브러리 특징

    뭐, 이런저런 특징을 비교해놓은 게 있는데 cachealot에서 쓴 거라서 믿음은 그렇게 가지 않네요 ㅋㅋㅋ 무튼 회사에서도 cachealot을 사용하다가 cacheops로 넘어왔는데, 그 과정에서 있었던 트러블 슈팅도 함께 설명드리겠습니다.

     

    Cacheops Readme

    pip install django-cacheops

    명령어로 라이브러리를 설치해줍니다.

    공식문서를 보면 매우 친절하게 settings 방법에 대해서 설명해주고 있습니다. 

    INSTALLED_APPS += ['cacheops']
     
    CACHEOPS_LRU = True # maxmemory-policy: volatile-lru 설정 
                        # (직접 redis config에서 수정하긴 했는데, 그렇게 안해도 되는지는 잘 모르겠네요)
     
    CACHEOPS_REDIS = "redis://127.0.0.1:6379/1" # local redis
     
    CACHEOPS_DEFAULTS = {
        'timeout'60 * 60 * 1# 1시간
        'ops''all'# get, fetch ... 모든 동작 ex) 'ops': 'get' 이러면 get 할 때만 캐시
        'cache_on_save'False # save()할때 캐시 할건지 (굳이 필요없을 것 같아서 False 로함)
    }
     
    CACHEOPS = {
        '*.*': {}, # 모든 앱에대해서 캐시적용
    }
     
    cs

    기본적인 세팅은 이렇습니다!! (주석과 공식문서 참고하시길 바라요)

    이제 api를 직접 호출해 보겠습니다.

     

    왼쪽: 동일한 api를 여러본 호출했을때  오른쪽: redis server에 저장된 캐시 메모리

    첨부된 이미지에서 확인할 수 있듯이 처음 api를 호출했을 때 select query들이 실행되고, 그다음부터는 query실행 없이 api에서 data를 전달해 줍니다. redis server에서 캐싱된 데이터를 확인할 수 있습니다.

     

    ❗️ 여기서 잠깐!!!

    이제 위에서 잠시 언급했던, maxmemory-policy = volatile-lru로 설정한 이유에 대해서 말씀드리겠습니다. 

    우리가 걱정해야 할 부분은 redis maxmemory가 가득 찬 경우, 클라이언트에서 그 이상의 메모리를 캐싱하려고 할 때입니다. 

     

    몇 가지 시나리오에 대해서 말하자면 maxmemory-policy 설정 중  allkeys-random, volatile-random이 있습니다. 이 경우 문제가 되는 부분은 말 그대로 random(무작위)으로 key를 지울 때입니다.

     

    이렇게 무작위로 key를 지우게 되면 cacheops에서 query를 캐싱할 때 몇 가지 단계에 걸쳐 캐싱을 하게 되는데, 쉽게 a-b-c 이런 단계에서 무작위로 삭제되는 key 중 b가 삭제되게 되면 a, c만 남은 상황에서 정상적인 캐시 동작을 보장할 수 없습니다.

     

    update를 해도 캐시 동기화가 안된다던지, 이상한 데이터로 보인다던지 하는 이슈가 생길 수 있습니다. 그렇기 때문에 voliate-lru 혹은 volatile-ttl로 maxmemory-policy을maxmemory 이상의 동작에서 expired_set 이 설정된 key를 삭제하고 expired 시간을 짧게 세팅해서 이슈를 (나름, 잘?) 해결하라는 말입니다.

     

    제가 알기로는 cachealota-b-c (schemes, confj, q) 이렇게 캐싱을 하는 게 아닌 값을 통째로 하나 캐싱하는 걸로 알 고있습니다. 그래서 아무거나 지워도 전체 캐싱된 데이터가 날아가서 큰 문제가 없었던 걸로(?) 기억하네요 ㅎㅎ 캐시 key가 삭제되면 다시 DB에 query 나가면 되니 데이터는 정상적으로 보입니다.

     

    여하튼, 요점은 ORM Cache 라이브러리, 서비스, server memory 특성들에 따라 적절하게 Redis를 튜닝해야 한다는 점입니다!! 처음에 이 부분을 찾지 못해서 CS지옥에 빠졌던 기억이 새록새록 피어나네요! ㅋㅋㅋㅋ (그땐 악몽)

     

    5. 마치며


    개인적으로 redis, nginx, gunicorn 같은 것들의 튜닝은 회사에서 쉽게 건들기 힘든 부분이라 토이 프로젝트를 하면서 많이 건드려 보는 게 경험도 쌓을 수 있고 좋은 것 같습니다. 

     

    그리고 default로만 사용하는 것보다 왜 이런 옵션을 사용했고 왜 이렇게 동작하는지에 대해 알고 사용한다면, 그만큼 실무에서도 효율적인 퍼포먼스와 개인 능력치로 나타날 것이라고 생각되네요!!

     

    그럼 오늘 하루도 즐거운 개발 하시길 바래요~👋

     

     

    - 참고 문서, 블로그
    redis.io/documentation

     

    Redis

    *Documentation Note: The Redis Documentation is also available in raw (computer friendly) format in the redis-doc github repository. The Redis Documentation is released under the Creative Commons Attribution-ShareAlike 4.0 International license. *Programmi

    redis.io

    junghwanta.tistory.com/26

     

    Redis 설치 (Ubuntu 환경)

    Redis 설치 레디스를 우분투 서버에 설치를 하려한다. EC2에 우분투 인스턴스를 하나 만들어서 레디스 서버로 활용을 하려고 하는데, 서버 환경을 변경할 수 있으니 기록하는 습관이 익숙해지도록

    junghwanta.tistory.com

    zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EB%A9%94%EB%AA%A8%EB%A6%AC_%EC%82%AC%EC%9A%A9%EB%A5%A0_%ED%99%95%EC%9D%B8

     

    리눅스 메모리 사용률 확인 - 제타위키

     

    zetawiki.com

    medium.com/29cm/cacheops-orm%EC%97%90-redis-cache-%EC%89%BD%EA%B2%8C-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-966249a1c615

     

    CacheOps — ORM에 Redis Cache 쉽게 적용하기

    29CM 백엔드 서버는 Django으로 구성되어있습니다. 이 글에서는 Django ORM Cache으로 Cacheops를 도입하면서 분석한 자료를 공유드리고자 합니다.

    medium.com

     

    댓글

운동하는 개발자 JAY-JI