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

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개씩 출력

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) 리스트를 출력

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번

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번

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() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입