ks.dgoon.lee log

dboard - django orm 쿼리 최적화 ing


2024/01/12 23:23:40 #django #dev #annotate #comment #bulletin board #postgresql #database #cache #dboard #select_related #prefetch_related #annotation #orm #query optimization
Attachments:

django ORM 가지고 일단 동작하게 만들자는 생각으로 뚝딱뚝딱 만들고 있다가, 지금쯤 한번 확인해봐야지 생각이 들어서 판도라의 상자를 열어봤다. 과연 안에 희망이 있는가 고통과 좌절이 있는가 ...

ORM 이 쿼리를 만들어 주니까 실제로 어떤 쿼리를 어떻게 보내는지 내가 보지 못하는 상태였다. 대책없이 비효율적인 최악의 경우라면 FK(ForeignKey) 로 연결된 애들 접근할때마다 쿼리 하나씩 하고 있을 수도 있다. 아직 개발 단계고, 나 혼자만 보고 있어서 크게 느리지 않아 보이지만 당장 동시 접속자가 열댓이 게시판 글 목록만 열어도 쿼리 수천 수만개가 생길 수도 있지, 라는 생각을 하니 식은땀이 난다. (아직 캐시같은것도 전혀 안붙임)

1. 상태 확인을 위한 도구: silk

https://github.com/jazzband/django-silk

5분정도 구글링 후다닥 해보고, silk 라는 녀석이 실제 DB 쿼리를 살펴볼 수 있게 도와줄 것 같아서 써보기로 정했다. silk 자체가 퍼포먼스에 영향을 주기는 하는데, 나는 순전히 DB 쿼리만 보고 싶은 거고 실제 latency, throughput 등을 지금 볼 생각은 없기 때문에 상관 없었다. django-silk 를 세팅하는 방법은 인터넷을 뒤지면 많이 나온다. 요약하자면,

  • pip install django-silk
  • INSTALLED_APPSsilk 추가 (나는 제일 밑에 넣었다)
  • MIDDLEWAREsilk.middleware.SilkyMiddleware 추가 (나는 제일 밑에 넣었다)
  • urls.pypath('silk/', include('silk.urls', namespace='silk'))추가
  • 그리고 서버를 띄우고 여기저기 들어가면 silk 미들웨어가 여러가지 정보를 DB 에 다 때려 넣는다
  • http://127.0.0.1/silk 를 띄우면 대강 아래를 닮은 화면이 뜬다.
  •  여기서 각 Request 상세화면으로 가면 요청 처리중 발생한 쿼리를 볼 수 있는 화면이 있다.
  • 실제 SQL 까지 볼 수 있음

2. 현재 상태에 대한 고찰

  • 여러개의 중복 쿼리가 존재. ID 가 다른 것도 아니고, Key-Value 로 일종의 설정값을 넣어두는 모델이 심했다. 사실 DB 에 이런 설정을 넣을때부터 이럴거라고 생각은 했었는데 ... -> 이건 캐싱하면 거의 완벽하게 해결됨
  • 위에서 적은대로 FK 따라갈때마다 쿼리를 계속 새로 하고 있었다. 그 결과로 글 목록 을 출력하는게 지옥이었다. 25개의 글 목록, 각 글의 댓글/추천/비추천 개수, 각 글의 블라인드 상태 등을 출력하기 위해 수없이 많은 쿼리를... 글 목록 하나 그리는데 대략 150개의 쿼리를 하고 있다.
  • 글 내용을 보여주면서 아래에 같은 페이지 글 목록도 보여주게 했는데, post 는 댓글 개수에 따라 다르지만 글 목록에서 약 150개니까, 게시물 하나 읽으면 쿼리가 200개 실행된다.
  • 쿼리 목록을 쭉 보면 JOIN 이란게 전혀 없다. 그러니까 DB 를 그냥 memory-dict 처럼 여기고 쓰고 있는 것이지...
  • DB와 Django 인스턴스가 같은 VPC 에 있기에 망정이지 조금만 멀었다면 개발도중에도 지옥이 펼쳐질 수도 있었을 것 같다.

3. 닥치고 쿼리 개수를 줄이자

  • 자주 변하지 않고, 변경이 단 한 곳에서만 일어나는 모델 -> CACHING. 3분 걸렸다.
  • POST_LIST 이터레이션하면서 각 POST 에 대해 FK 따라가며 쿼리하는 것 -> select_related, prefetch_related
  • @propertytemplatetags 에서 DB 에 쿼리날리는 것들 -> 가능한건 모두 annotation 으로 변경

여기까지는 금방 했는데, COMMENT 의 경우에는 아직 sort_order 를 안 넣어두어서 tree traversal 하면서 찍어주고 있는데, 미리 prefetch 해두게 만드는게 좀 어렵다. 찍는 방식부터 바꿔야 최적화할 수 있을 것 같아서 이건 다음으로 미루기로.

4. 결과

몇몇 페이지에 대해서 before, after 를 비교해보면 아래와 같다.

  • 160 queries -> 30 queries
  • 42 queries -> 6 queries
  • 42 queries -> 4 queries

silk 에서 보이는 걸로는 대략 이렇다.

이랬던 화면이

이렇게 바뀌었다


아직 댓글 보여주는 부분 최적화를 안 했기 때문에 /post/<int:post_id>/ 에 여전히 쿼리가 좀 많은(하지만 전에 비해서는 거의 반반반토이다...)걸 볼 수 있다. 그리고 /myinfo/comment_noti 는 배가 고파서 아직 손을 안 대어 거의 그대로인 상태다. 아직 갈일은 하루쯤 더 삽질하면 그래도 화면 하나에 쿼리 10개는 안 넘게 할 수 있을 것 같다.


제로보드, 라이믹스나, 그누보드 아니면 phpbb, misago, discourse 같은 유명한 게시판/포럼 시스템들 누가 잘 뜯어서 벤치마크 해놓은거 없나 궁금하다. 캐싱은 논외로 하고, 글 내용, 글 목록 뿌려주는데 DB 접근을 어떻게 하고 있으려나.


---


추가: /myinfo/comment_noti 부분도 손 좀 댔다.



댓글 0개