[MySQL] JOIN절 최적화 0 2 1,667

by 내몸매GROUPBY [MySQL] [2019.05.30 20:12:25]


안녕하세요

제목이 없는 게시판 형태의 DB 설계를 하려고 합니다

 

USER(InnoDB)

SEQ| EMAIL | NICKNAME | IS_DEL | REG_TIME

PK(SEQ)

UK(EMAIL), UK(NICKNAME)

 

POST(InnoDB)

SEQ | USER_SEQ | CONTENT | CATEGORY | LIKE_COUNT | COMMENT_COUNT | IS_DEL | REG_TIME

PK(SEQ,)

INDEX(USER_SEQ)

 

COMMENT(InnoDB)

SEQ | POST_SEQ | USER_SEQ | CONTENT | LIKE_COUNT | IS_DEL | REG_TIME

PK(SEQ)

INDEX(POST_SEQ,)

INDEX(USER_SEQ)

 

위와 같은 테이블을 만들려고하고 있고 백엔드에서 사용되는 쿼리는

1. 게시글(POST)를 최신순으로 5개씩 출력

2. 특정 POST_SEQ에 대한 댓글(COMMENT) 리스트를 출력

이렇게 2가지라고 생각하고 있습니다.

 

QUERY

1. 게시글(POST)를 최신순으로 5개씩 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT
    P.SEQ
    ,P.CONTENT
    ,P.CATEGORY
    ,P.LIKE_COUNT
    ,P.COMMENT_COUNT
    ,P.REG_TIME,
    ,U.EMAIL
    ,U.NICKNAME
FROM(
    SELECT
        SEQ
    FROM POST
    WHERE
    SEQ < ?
    AND IS_DEL = FALSE
    ORDER BY SEQ DESC
    LIMIT 5
) A INNER JOIN POST P
ON A.SEQ = P.SEQ
INNER JOIN USER U
ON P.USER_SEQ = U.SEQ;

2. 특정 POST_SEQ에 대한 댓글(COMMENT) 리스트를 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
    C.SEQ
    ,C.CONTENT
    ,C.LIKE_COUNT
    ,C.REG_TIME
    ,U.NICKNAME
FROM(
    SELECT
        SEQ
    FROM COMMENT
    WHERE
    POST_SEQ = ?
    AND IS_DEL = FALSE
) A INNER JOIN COMMENT C
ON A.SEQ = C.SEQ
INNER JOIN USER U
ON C.USER_SEQ = U.SEQ;

 

QUESTION

SELECT SEQ FROM POST WHERE SEQ < ? AND IS_DEL = FALSE

SELECT SEQ FROM COMMENT WHERE POST_SEQ = ? AND IS_DEL = FALSE

이 부분때문에 커버링 인덱스가 되질 않을 것 같습니다(맞을까요?)

하지만 인라인뷰를 쓰지않으면 IS_DEL = FALSE인 모든 POST 레코드 또는 모든 COMMENT와 USER가 조인될 것 같아서요..

쿼리 개선 또는 테이블 구조를 개선할 점이 있을까요?ㅠㅠ

by 마농 [2019.05.31 08:42:55]

네. 그 조건 때문에 커버링이 안되네요.
인라인 뷰를 사용하되 어차피 커버링이 안되니 셀프조인을 빼는게 좋겠네요.


by 르매 [2019.05.31 12:33:38]

MySQL의 서브쿼리는 생각처럼 동작하지 않을 때가 많은데.. 지금 본문에서도 인라인뷰는 성능을 깎아먹는 요소입니다.
EXPLAIN 결과를 보시면 아시겠지만.. 생각하시는 것처럼 동작하지 않습니다.

아래와 같이 인라인 뷰를 모두 제거하시길 권해 드립니다.

1번

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT STRAIGHT_JOIN P.SEQ
    ,P.CONTENT
    ,P.CATEGORY
    ,P.LIKE_COUNT
    ,P.COMMENT_COUNT
    ,P.REG_TIME
    ,U.EMAIL
    ,U.NICKNAME
FROM POST P FORCE INDEX FOR JOIN (PRIMARY)
    INNER JOIN USER U FORCE INDEX FOR JOIN (PRIMARY) ON P.USER_SEQ = U.SEQ
WHERE P.SEQ < ?
    AND P.IS_DEL = FALSE
ORDER BY P.SEQ DESC
LIMIT 5;

위 쿼리에서 P.SEQ < ? AND P.IS_DEL = FALSE 는 인덱스 커버링이 필요하지 않은게.. 이미 P.SEQ가 PK이고 클러스터드라서 LOOK-UP 없이 빠르게 수행됩니다.

 

2번

1
2
3
4
5
6
7
8
9
SELECT STRAIGHT_JOIN C.SEQ
    ,C.CONTENT
    ,C.LIKE_COUNT
    ,C.REG_TIME
    ,U.NICKNAME
FROM COMMENT C FORCE INDEX FOR JOIN (POST_SEQ에 걸린 인덱스)
    INNER JOIN USER U FORCE INDEX FOR JOIN (PRIMARY) ON C.USER_SEQ = U.SEQ
WHERE C.POST_SEQ = ?
    AND C.IS_DEL = FALSE;

여기서는 인덱스 커버링이 되지 않기 때문에, COMMENT 테이블에 "POST_SEQ, IS_DEL" 의 순서로 복합 인덱스를 생성하고.. 기존에 POST_SEQ 단일 컬럼에 걸린 인덱스를 삭제하는 것이 좋겠습니다.

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입