안녕하세요. 주니어 개발자입니다.
다름이 아니라 계층형 게시판 개인 프로젝트를 하다가 잘 안되는 부분이 있어 질문 드립니다.
먼저 제가 설계한 board 테이블은 아래와 같습니다.
(JPA를 이용한것이라 Entity에 의해 자동으로 만들어진 테이블입니다.)
create table board
(
board_id bigint auto_increment
primary key,
create_time datetime(6) not null,
update_time datetime(6) not null,
content longtext not null,
group_no bigint not null,
group_order int not null,
hit int not null,
indent int not null,
status varchar(255) not null,
title varchar(255) not null,
member_id bigint not null,
constraint FKsds8ox89wwf6aihinar49rmfy
foreign key (member_id) references member (member_id)
);
간단한 테이블이라 따로 설명드릴건 없지만, pk는 board_id이지만 계층형으로 보여주기 위해 group_no와 group_seq라는 두개의 컬럼을 이용합니다.
결론적으로 페이징 쿼리는 단순하게 아래와 같이 만들어집니다.
select
board_id,
create_time,
update_time,
content,
group_no,
group_order,
hit,
indent,
member_id,
status
from
board
order by
group_no,
group_order
limit 0, 20;
첫번째 페이지는 limit 0,20 그 다음은 20,40 그 다음은 40,60과 같이 offset과 limit 값으로 페이징을 처리합니다. 단, 정렬기준이 pk가 아닌 보시는것처럼 group_no과 group_order입니다.
데이터가 얼마 없을 땐 전혀 문제가 없었는데 강제로 더미데이터를 몇백만건 넣고나니 위 쿼리문의 속도가 현저하게 낮아졌습니다.(뒤쪽 페이지는 1~2초 이상 걸려서 순간 렉걸린듯한 느낌ㅠㅠ)
그래서 저는 단순히 group_no를 인덱스로 설정해주면 이 문제를 해결할 수 있을 거라고 생각했습니다. 또는 group_no와 group_seq를 복합인덱스로요. 하지만 어떤 INDEX를 생성해도 위 쿼리문은 계속 Full-scan을 하고 성능은 나아지지 않습니다.
제가 어떤 부분을 놓치고 있는건지, 또한 해결방법은 무엇일까요? 선배님들께 도움 요청드립니다. 감사합니다
추가로 group_no는 글을 쓸 때 board_id와 같은값을 갖고, 답글을 쓸때는 부모글의 group_no를 그대로 가집니다.
그리고 같은 group_no를 갖는 묶음끼리 순서를 결정하기 위한 컬럼이 group_seq입니다.
그래서 group_no가 상대적으로 카디널리티가 훨씬 높고 group_seq는 낮을것같습니다.
그래서 group_no만 인덱스를 걸어줘도 충분히 쿼리성능이 해결되지 않을까? 라고 추측했습니다
현재 group과 seq 두 컬럼으로 복합인덱스로 설정한 결과
limi 0,20처럼 약 offset 10000정도까지는 index를 잘 타는것 같습니다!!
그런데 그 뒤쪽으로 갈수록(offset 값이 커지면 다시 풀스캔을 하고 있습니다!)
페이징 쿼리에서 뒤로 갈수록 느려지는 것은 어느정도 감수해야 하는 부분입니다.
위 쿼리에서 개선할 수 있는 방법으로는 인덱스만을 이용한 페이징 방법이 있습니다.
다른 항목을 제외하고 오로지 인덱스만 이용하여 페이징 합니다.
페이징 결과만 가지고 한번더 테이블과 조인하는 방안
MySQL 의 인덱스는 PK 를 포함하는 것으로 압니다.
따라서 pk 를 포함하여 정렬하시면 됩니다.
SELECT a.board_id
, b.create_time
, b.update_time
, b.content
, a.group_no
, a.group_order
, b.hit
, b.indent
, b.member_id
, b.status
FROM (-- 인덱스만을 이용한 페이징
SELECT group_no
, group_order
, board_id
FROM board
ORDER BY group_no, group_order
LIMIT 0, 20
) a
INNER JOIN board b
ON a.board_id = b.board_id
ORDER BY group_no, group_order
;