알림
|

페이지네이션 기능이 생각외로 성능을 많이 잡아먹죠. (기능제안)

페이지 정보

작성자 HideD 172.♡.214.228
작성일 2024.04.02 13:18
563 조회
5 댓글
9 추천
글쓰기

본문

게시판이 위주인 사이트에서 목록 띄우고, 글 보여주고, 댓글 보여주면 땡일 것 같은데,

왠지 모르게 느리다면.. 보통 글 하단에 보이는

"그 글과 비슷한 시기에 작성된 글 목록"을 보여주는 기능 때문일 확률이 높죠.



1. 단순 구현 (대부분의 게시판)

아주 단순하게 구현하면 어떻게 동작하느냐면


1) 현재 글(예: 23456번)이 몇번째로 오래된 글인가를 확인

   - 23456번 글 이후로 작성된 글의 수 (예: 12345개!)

2) 몇번째 페이지인지를 계산

   - ceil(글 수 / 페이지 당 글 수) + 1.  (예: 1235페이지!)

3) 근처의 글 가져오기

   - 가장 최근 글 기준 1234 * 페이지당 글 수

   - 댓글 관련 정보도 가져오기 (서브쿼리 혹은 join)


이 중에서 1번, 3번 절차가 DB에 테이블 읽기 락을 걸기 때문에 문제입니다. ( count()와 2단 LIMIT )

row 락 정도에서 해결을 봐야 빠르게 처리가 될텐데, 단순한 작업에 테이블 락이 걸려버리니, 뒤의 작업들이 줄줄이 대기하게 됩니다.


테이블 읽기 락이 테이블 쓰기 락 뿐만 아니라 row 쓰기 락도 잠그는 걸로 알고있는데,

글 조회수가 같은 테이블에 있다면, 조회수 올리는 작업이 다른 게시물의 목록 조회 때문에 지연되는 거죠.

그리고 다른 글의 테이블 읽기 락은 row 쓰기 락이 끝날때까지 기다려야 하니까..


결국 글을 하나도 쓰지 않고 읽기만 하는 상태이더라도, 싱글 스레드마냥 처리하게 됩니다(!!!)


2. 최적화하기


그래서 이 부분을 최적화 하려면 어떻게 해야하는가? 하면 크게 두가지 전제가 필요합니다.


- 글을 삭제하더라도 페이지 목록에는 남겨둔다. ( "삭제된 게시물입니다" )

- 글의 게시판 이동이 거의 일어나지 않는 작업이라 가정한다.

 

이를 활용하면 아래처럼 구현할 수 있습니다.

 

* 게시판 테이블에 "글 순서" 개념을 추가합니다.

  - 요새는 어떤 게시판이든 하나의 테이블에 넣기 때문에 순서가 섞이는데, 게시판마다 +1식 증가하는 column이 있으면 됩니다.

* 글을 하나씩 추가할 때 마다 가장 최신 글의 글 번호보다 +1을 해줍니다.

  - count() 명령이 아님을 주의합니다.

  - 글 순서만을 관리하는 전용 테이블을 만들고 auto_increment를 거는 방법도 괜찮다고 생각합니다.

  - redis같은 녀석에서 atomic increase를 쓰는 방법도 있겠습니다.

* 글의 페이지 번호를 아주 단순하게 계산할 수 있습니다.

  - ceil((가장 마지막 글의 순서 번호 - 글 순서 번호) / 페이지 당 글 수) + 1

  - 마지막 글 순서 번호는 캐시해 두고 있어야 합니다.

* 글을 삭제할 땐 "삭제된 글입니다" 처리만 합니다.

  - 이로 인해 글 순서 번호만으로 페이지 번호를 알 수 있게 됩니다.

* 부득이하게 글을 다른 게시판으로 옮겨야 한다면, "삭제 후 새로 작성" 처럼 처리합니다.

  - 이전에 작성된 댓글을 옮기는 작업이 필요합니다.


이렇게 동작하면, 하단 글 목록을 보여주는데 row 락만으로 해결 할 수 있습니다.


개발에 직접 참여하면 좋겠지만, 시간이 없어 그냥 아이디어만 던지고 갑니다 ㅜㅜ


+ 조회수도 엄청나게 성능을..!!!

+ 물론 그누보드5에 "게시글 순서" column이 있지만, 빠르지 않죠.
   전제사항의 첫번째인 "삭제 상태 유지"를 지키지 않기 때문에, count와 2단 LIMIT을 써야하기 때문에 별 차이가 없습니다.

댓글 5 / 1 페이지

시민님의 댓글

작성자 시민 (110.♡.14.113)
작성일 04.02 13:33
고마우신 조언입니다. 감사합니다

firebird님의 댓글

작성자 no_profile firebird (172.♡.218.176)
작성일 04.02 15:10
전체 테이블을 페이징 하는 것은 최적화 방법이 많은데, 조건 걸린 테이블의 페이징은 어떻게 성능향상이 가능할까요? 최소한 인덱스 스켄이라도 결려야 할듯 한데요...

HideD님의 댓글의 댓글

대댓글 작성자 HideD (172.♡.210.98)
작성일 04.02 16:13
@firebird님에게 답글 (1) 카테고리
게시판내 카테고리는 위 방법을 응용할 수 있을 것 같습니다.
이 경우엔 게시판마다 번호매기는 테이블을 하나씩 두고,
게시판 내 게시 순서는 자동으로 매기고, 카테고리 내 게시 순서는 sql procedure로 하는 형태가 적절할 것 같습니다.

카테고리를 바꾸는 기능이 문제인데,

- 일정 시간 내에만 카테고리를 바꿀 수 있도록 하거나, 소규모 게시판일 경우에만 카테고리 바꾸는 기능을 허용하고,
- 카테고리를 옮길 때, 카테고리 내 순서를 다시 계산하는

형태이면 될 것 같습니다.


(2) 검색 쿼리

쿼리 걸린 테이블의 페이징은 페이지 번호를 포기하거나, 한 페이지에 갯수를 맞춰서 보여주겠다는 마음가짐을 버리면 될것 같습니다.

(물론 전문 검색 라이브러리를 쓰는게 더 적절할 수도 있겠습니다만!)
예를 들어 "모공"이라는 단어가 제목에 있는 검색을 한다면,

- "모공"이라는 단어가 들어간 글을 최대 20개 검색,
- <다음페이지>는 앞서 검색한 마지막 글보다 오래된 글을 20개 가져오는 기능
  - 예: /free/search?title=모공&after=2024-04-02T07:12:43
- 이전페이지는 서버 세션에 남겨두든, 브라우저의 SessionStorage에 남겨두든, 트릭으로 해결하고, 아무튼 반쯤 포기

로 커버 가능할 것 같습니다.


(3) 30추글

특정 threshold의 추천이 쌓인 글들만 보여주는 케이스들도 있는데,

- 꼭 작성일자 순서대로 보여줄 필요는 없으니, 추천 수 도달 시점을 기준으로 보여주는 걸로 처리
- 오래된 글들은 추천 금지
- 추천 번복을 허용하는 경우에는, 일단 30추천에 도달했다면 29가 되더라도 유지.

추천 횟수를 자유롭게 필터링하는 경우(49추글..?)는 모르겠습니다. 그건 진짜 깡성능으로 밀어붙이거나 검색 쿼리에 제한한 것처럼 페이지 번호를 포기해야 하지 않을까요


덧. document 테이블에 조회수와 추천이 붙어있는건 이 시점에서 그렇게 좋은 아이디어는 아닌것 같습니다.

코끼리대파님의 댓글

작성자 코끼리대파 (49.♡.67.28)
작성일 04.07 20:46
페이지에서 row 가 없어지는 문제를 해결하려면요
회원이 탈퇴해서 게시글 자체를 지워야 한다면 기본키만 두고 다날리게 하는거죠?

HideD님의 댓글의 댓글

대댓글 작성자 HideD (210.♡.32.25)
작성일 04.08 11:55
@코끼리대파님에게 답글 네네 기본키만 남겨놓고, 작성자 정보든 뭐든 다 날려야죠.
글쓰기
전체 검색