프로젝트를 기획 및 설계하고, 또 DB를 설계하고 프론트엔드에서 사용할 API를 만들려고 할 때 이 API가 느리게 돌면 혹은 너무 많은 요청이 들어와서 응답을 제대로 못하면 어떻게하지? 라는 생각을 안해볼 수가 없다. 그러면 다양한 상황이 존재할 때 각각 어떤 방법으로 대응하는 것이 좋을까?
로직 자체가 느린 경우
알고리즘, 자료구조 개선
- n^2 -> nlogn 등의 개선방안 찾기
- 리스트 검색 -> 딕셔너리 검색 등
DB 쿼리 최적화
- N+1 문제, 불필요한 조인 체크
- DB 인덱스 설정 등으로 처리
캐싱
- 특정 무거운 계산을 캐싱해두고 재활용
- 만료 정책, 무효화로직 필요
비동기 처리
- 작업 큐를 이용해서 비동기로 백그라운드에서 처리(celery)
- 사용자에게 작업 완료 알림 필요
서버 분리
- 무거운 작업을 처리하는 서버를 분리
- 무거운 로직은 독립적으로 확장 가능한 곳으로 배치
- ex) 오토스케일링을 좀 더 자유롭게 가능
하드웨어 or 인프라
- 서버의 사양 혹은 인스턴스의 사양을 올리는 Vertical Scaling
- 더 많은 서버 혹은 인스턴스를 통한 Horizontal Scaling
타 언어 사용
- 해당 로직이 빠르게 동작할 수 있거나 그런 라이브러리가 구축된 언어 사용 ex)C/C++/Rust
- 위 기능을 C 익스텐션으로 사용하거나 RPC를 이용하여 호출(gRPC 사용)
요청이 많은 경우
수평 확장
- 더 많은 서버 혹은 인스턴스를 통한 Horizontal Scaling
- k8s 등을 사용하여 오토 스케일링 가능
요청 제한
- Rate Limiting과 Throttling을 이용한 과도한 요청 방지
- Django에선 DRF Throttle, django-ratelimit 등 사용 가능
- WEB서버 단에서 처리하고 싶으면 nginx ratelimit 사용 가능
- Django 인스턴스가 여러개, 워커가 여러개인 경우 cache를 로컬 메모리로 사용하는 경우 각기 다른 Rate Limiting이 적용된다.
DB 병목이 생길 경우
- DB 쿼리 최적화
- 데이터베이스 파티셔닝, 샤딩
- 데이터베이스 리드 레플리카
- postgres에선 Primary, Standby 로 구분한다
- SELECT 쿼리와 같은 Read 작업만 수행
캐싱
- django 캐싱
- Reverse Proxy caching(nginx)
정적 요청이 많은 경우
(작성중)