-
Notifications
You must be signed in to change notification settings - Fork 5
BE 쿼리, 데이터 관리(09 27 ver)
TABLE_NAME | INDEX_NAME | COLUMN_NAME | SEQ_IN_INDEX | NON_UNIQUE | INDEX_TYPE |
---|---|---|---|---|---|
member | PRIMARY | id | 1 | 0 | BTREE |
mission | PRIMARY | id | 1 | 0 | BTREE |
discussion | PRIMARY | id | 1 | 0 | BTREE |
discussion | fk_discussion_member | member_id | 1 | 1 | BTREE |
discussion | fk_discussion_mission | mission_id | 1 | 1 | BTREE |
discussion_comment | PRIMARY | id | 1 | 0 | BTREE |
discussion_comment | fk_discussion_comment_discussion | discussion_id | 1 | 1 | BTREE |
discussion_comment | fk_discussion_comment_member | member_id | 1 | 1 | BTREE |
discussion_comment | fk_discussion_comment_discussion_comment | parent_comment_id | 1 | 1 | BTREE |
discussion_hash_tag | PRIMARY | discussion_id | 1 | 0 | BTREE |
목표
- 대량의 데이터에서 옵티마이저의 실행 계획을 확인하고 인덱스를 걸었을 때 유의미한 차이가 있는 조회 쿼리가 있다면 인덱스를 적용합니다.
- 데이터 적재 : 아래와 같은 이유로 운영DB로 하려 했으나 로컬DB로 변경했습니다.
- 실제 운영 데이터 없는 점
- 실제 운영 데이터베이스에 하면 결과 성능이 실제 성능과 동일할 것으로 예상
- 데이터 적재량 : 각 테이블에 10만건을 먼저 적재해보고 유의미한 개선이 나오면 적용합니다. 아니면 데이터를 더 넣어서 유의미한 개선을 할 수 있을지 확인해봅니다.
- 페이지네이션 : 아직 구현되지 않았으므로 고려하지 않았습니다.
진행 방식(목표랑 중복 제거)
- 데이터 적재 테이블 : solution, discussion, discussion_comment, solution_comment, discussion_hash_tag
- 테이블을 선정한 근거 :
- 사용자가 직접 적재할 수 있는 테이블
- 사용자가 많을 수록 더 많은 조회가 이루어질 것이라고 판단되는 테이블
-
각 데이터는 이런 기준으로 생성했습니다.
-
discussion
- discussion 은 총 99001 행입니다.
- discussion.mission_id 는 1~6, member_id 는 1~3 을 랜덤으로 조합하여 생성했습니다.
- discussion.create_at은 1분 간격으로 생성했습니다.
-
discussion_comment
- discussion_comment 은 총 100000 행입니다.
- discussion_comment.discussion_id 는 1~99001, member_id 는 1~3 을 랜덤으로 조합하여 생성했습니다.
- discussion_comment.parent_comment_id 는 null 값과 1~10000 의 값을 랜덤으로 사용했습니다.
- discussion_comment.deleted_at 은 모두 null 입니다.
- discussion_comment.create_at은 1분 간격으로 생성했습니다.
-
discussion_hash_tag
- discussion_hash_tag 은 총 99294 행입니다.
- discussion_hash_tag.discussion_id 는 1~99000, hash_tag_id 는 1~5 을 랜덤으로 조합하여 생성했습니다.
-
solution
- solution 은 총 100000 행입니다.
- solution.mission_id 는 1~6, member_id 는 1~3 을 랜덤으로 조합하여 생성했습니다.
- solution.status 는 ‘COMPLETED’, ‘IN_PROGRESS’ 의 두 값을 랜덤으로 사용했습니다.
- solution.create_at은 1분 간격으로 생성했습니다.
-
solution_comment
- solution_comment 은 총 100000 행입니다.
- solution_comment.solution_id 는 1~32768, member_id 는 1~3 을 랜덤으로 조합하여 생성했습니다.
- solution_comment.parent_comment_id 는 null 값과 1~10000 의 값을 랜덤으로 사용했습니다.
- solution_comment.deleted_at 은 모두 null 입니다.
- solution_comment.create_at은 1분 간격으로 생성했습니다.
-
-
테스트는 로컬 DB에서 진행했습니다.
- 근거 :
- 운영 DB를 사용하면 운영 WAS에서 페이지네이션을 처리안하니까 OOM 발생
- 개발 DB를 사용하면 좋은 성능 발휘할 수 없고, 데이터 적재 속도가 느리고, 현재 개발 DB를 프론트엔드에서 사용중
- 근거 :
- 테스트 데이터는 스크립트로 적재했습니다.
- swagger를 보면서 적재한 테이블과 연관된 api 목록을 추출했습니다.
- api 를 호출하면서 발생하는 쿼리들을 수집했습니다.
- 쿼리들의 실행 계획을 모았습니다.
적용 및 결과
- 대부분의 쿼리들이 fk와 pk를 이용해서 인덱스를 사용하기 때문에 인덱스를 생성하지 않았습니다.
- 몇몇 쿼리들(필터링 관련 쿼리)은 해시태그를 조회하는 서브쿼리**(다른 곳도 진짜 풀 테이블 스캔인가?)**에서 풀 테이블 스캔이 발생합니다. → 추가 검증 필요
인덱스를 생성하지 않은 부분
- 필터링 관련 쿼리들은 해시태그를 조회하는 서브쿼리에서 테이블 스캔이 발생했습니다.
- 해시태그 이름에 인덱스를 생성하면 커버링 인덱스를 사용할 수 있습니다.
- 하지만, 행 개수가 적은 테이블에서는 Full Table Scan을 이용하는 게 나을 수 있기 때문에 적용하지 않았습니다. 실제로도 유의미한 성능 향상을 확인하지 못했습니다. (인덱스를 사용하는 경우 오히려 성능이 떨어지거나 오차범위 이내의 차이였습니다.)
- 유니크 인덱스를 생성해도 커버링 인덱스를 사용할지 궁금하여 유니크 제약 조건을 추가했습니다.
- 유니크 제약 조건을 설정하면 조회 속도가 약 18%정도 향상된 것을 확인할 수 있었습니다.
인덱스 제약 조건 성능 가설
- 해시 조인이라고 생각하였으나..
데이터를 추가해보기 - 1
근거 : 유니크 제약조건 성능 향상이 드라마틱한지 확신을 얻기 위해 더 많은 데이터를 적재해 실험했습니다.
추가 데이터 : 해시 태그 100개, 미션 100개
결과 : 유니크 인덱스 vs hash tag name 인덱스, 인덱스 없음 모두 동일한 성능
-
유니크 인덱스
-> Sort: s1_0.id DESC, ht1_0.id (actual time=12073.248..12394.620 rows=125673 loops=1) -> Table scan on <temporary> (cost=0.01..117.72 rows=9219) (actual time=0.197..517.966 rows=125673 loops=1) -> Temporary table with deduplication (cost=98729.09..98846.80 rows=9219) (actual time=10743.114..11311.378 rows=125673 loops=1) -> Remove duplicate (m1_0, ht1_0, s1_0) rows using temporary table (weedout) (cost=97807.19 rows=9219) (actual time=3.284..3278.439 rows=125673 loops=1) -> Inner hash join (s1_0.mission_id = mht1_0.mission_id) (cost=97807.19 rows=9219) (actual time=3.183..1009.431 rows=125673 loops=1) -> Filter: (s1_0.`status` = 'COMPLETED') (cost=221.83 rows=1980) (actual time=0.236..700.179 rows=50218 loops=1) -> Table scan on s1_0 (cost=221.83 rows=99000) (actual time=0.193..566.615 rows=100000 loops=1) -> Hash -> Nested loop inner join (cost=267.99 rows=233) (actual time=1.721..2.628 rows=28 loops=1) -> Inner hash join (ht1_0.mission_id = mht1_0.mission_id) (cost=237.43 rows=233) (actual time=1.618..2.308 rows=28 loops=1) -> Table scan on ht1_0 (cost=0.40 rows=291) (actual time=0.166..0.590 rows=291 loops=1) -> Hash -> Nested loop inner join (cost=4.35 rows=8) (actual time=0.858..1.090 rows=8 loops=1) -> Index lookup on mht1_0 using fk_mission_hash_tag_hash_tag (hash_tag_id='2') (cost=1.55 rows=8) (actual time=0.665..0.724 rows=8 loops=1) -> Single-row index lookup on m1_0 using PRIMARY (id=mht1_0.mission_id) (cost=0.26 rows=1) (actual time=0.039..0.039 rows=1 loops=8) -> Single-row index lookup on ht2_0 using PRIMARY (id=ht1_0.hash_tag_id) (cost=0.03 rows=1) (actual time=0.009..0.010 rows=1 loops=28)
-
hash tag name 인덱스
-> Sort: s1_0.id DESC, ht1_0.id (actual time=11303.050..11597.919 rows=125673 loops=1) -> Table scan on <temporary> (cost=0.01..2163.18 rows=172854) (actual time=0.263..480.797 rows=125673 loops=1) -> Temporary table with deduplication (cost=216063.83..218226.99 rows=172854) (actual time=10095.696..10621.958 rows=125673 loops=1) -> Nested loop inner join (cost=198778.41 rows=172854) (actual time=1305.129..2336.105 rows=125673 loops=1) -> Inner hash join (ht1_0.mission_id = `<subquery2>`.mission_id) (cost=181485.74 rows=172854) (actual time=1304.898..1937.772 rows=125673 loops=1) -> Table scan on ht1_0 (cost=0.01 rows=291) (actual time=0.638..1.153 rows=291 loops=1) -> Hash -> Nested loop inner join (cost=8615.54 rows=5940) (actual time=1.502..981.931 rows=41891 loops=1) -> Nested loop inner join (cost=4.79 rows=3) (actual time=1.220..18.252 rows=8 loops=1) -> Table scan on <subquery2> (cost=0.85..2.54 rows=3) (actual time=0.021..0.133 rows=8 loops=1) -> Materialize with deduplication (cost=2.55..4.24 rows=3) (actual time=1.111..1.269 rows=8 loops=1) -> Nested loop inner join (cost=1.40 rows=3) (actual time=0.844..0.950 rows=8 loops=1) -> Covering index lookup on ht3_0 using idx_hash_tag_name (name='객체지향') (cost=0.35 rows=1) (actual time=0.395..0.405 rows=1 loops=1) -> Index lookup on mht1_0 using fk_mission_hash_tag_hash_tag (hash_tag_id=ht3_0.id) (cost=1.05 rows=3) (actual time=0.425..0.514 rows=8 loops=1) -> Single-row index lookup on m1_0 using PRIMARY (id=`<subquery2>`.mission_id) (cost=0.12 rows=1) (actual time=2.095..2.097 rows=1 loops=8) -> Filter: (s1_0.`status` = 'COMPLETED') (cost=2868.75 rows=1980) (actual time=0.638..116.882 rows=5236 loops=8) -> Index lookup on s1_0 using fk_solution_mission (mission_id=`<subquery2>`.mission_id) (cost=2868.75 rows=19800) (actual time=0.527..104.063 rows=10424 loops=8) -> Single-row index lookup on ht2_0 using PRIMARY (id=ht1_0.hash_tag_id) (cost=0.00 rows=1) (actual time=0.001..0.002 rows=1 loops=125673)
-
인덱스 없음
-> Sort: s1_0.id DESC, ht1_0.id (actual time=11488.641..11790.120 rows=125673 loops=1) -> Table scan on <temporary> (cost=0.01..477.84 rows=38028) (actual time=0.234..494.489 rows=125673 loops=1) -> Temporary table with deduplication (cost=63839.05..64316.88 rows=38028) (actual time=10148.223..10690.227 rows=125673 loops=1) -> Nested loop inner join (cost=60036.25 rows=38028) (actual time=1151.747..2151.108 rows=125673 loops=1) -> Inner hash join (ht1_0.mission_id = `<subquery2>`.mission_id) (cost=56232.74 rows=38028) (actual time=1151.398..1758.675 rows=125673 loops=1) -> Table scan on ht1_0 (cost=0.01 rows=291) (actual time=0.323..0.971 rows=291 loops=1) -> Hash -> Inner hash join (s1_0.mission_id = `<subquery2>`.mission_id) (cost=18188.65 rows=1307) (actual time=2.691..896.256 rows=41891 loops=1) -> Filter: (s1_0.`status` = 'COMPLETED') (cost=11820.09 rows=1980) (actual time=0.157..776.015 rows=50218 loops=1) -> Table scan on s1_0 (cost=11820.09 rows=99000) (actual time=0.148..627.283 rows=100000 loops=1) -> Hash -> Nested loop inner join (cost=32.56 rows=33) (actual time=2.049..2.200 rows=8 loops=1) -> Table scan on <subquery2> (cost=0.09..2.91 rows=33) (actual time=0.007..0.012 rows=8 loops=1) -> Materialize with deduplication (cost=26.19..29.01 rows=33) (actual time=1.934..1.943 rows=8 loops=1) -> Nested loop inner join (cost=22.80 rows=33) (actual time=1.452..1.784 rows=8 loops=1) -> Filter: (ht3_0.`name` = '객체지향') (cost=11.25 rows=11) (actual time=0.913..1.172 rows=1 loops=1) -> Table scan on ht3_0 (cost=11.25 rows=110) (actual time=0.782..0.992 rows=110 loops=1) -> Index lookup on mht1_0 using fk_mission_hash_tag_hash_tag (hash_tag_id=ht3_0.id) (cost=0.78 rows=3) (actual time=0.474..0.541 rows=8 loops=1) -> Single-row index lookup on m1_0 using PRIMARY (id=`<subquery2>`.mission_id) (cost=0.01 rows=1) (actual time=0.030..0.030 rows=1 loops=8) -> Single-row index lookup on ht2_0 using PRIMARY (id=ht1_0.hash_tag_id) (cost=0.00 rows=1) (actual time=0.001..0.002 rows=1 loops=125673)
데이터를 추가해보기 - 2
근거 : 해시 태그, 미션 데이터가 16배 가량 증가했으니, 솔루션 데이터도 160만개 추가했다.
추가 데이터 : 솔루션 170만개 추가
결과 : 유니크 인덱스 vs hash tag name 인덱스, 인덱스 없음 모두 동일한 성능
데이터를 추가해보기 - 3
근거 : 앞선 실험의 결과가 어떤 데이터의 변화에 의한 것인지 판단한다.
추가 데이터 : 솔루션 40만개
결과 : 유니크 인덱스 vs hash tag name 인덱스, 인덱스 없음 모두 동일한 성능
근거 :
- 데이터가 평균 10만건인 상황에서 조금 더 나은 성능을 보였지만, 그 이상의 환경에서는 유니크 인덱스가 성능이 안좋았다가, 또 그 이상의 상황에서는 동등해지기도 한다. 불확실성이 강하다.
- 인덱스를 생성하기 보다 쿼리 튜닝부터 선행되어야 한다.
select
distinct s1_0.id,
s1_0.created_at,
s1_0.description,
s1_0.member_id,
s1_0.mission_id,
m1_0.id,
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title,
s1_0.url,
s1_0.status,
s1_0.title
from
solution s1_0
join
mission m1_0
on m1_0.id=s1_0.mission_id
join
mission_hash_tag ht1_0
on m1_0.id=ht1_0.mission_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
s1_0.status='COMPLETED'
and exists(select
1
from
mission_hash_tag mht1_0
join
hash_tag ht3_0
on ht3_0.id=mht1_0.hash_tag_id
where
mht1_0.mission_id=s1_0.mission_id
and (lower(?)='all'
or ht3_0.name=?))
order by
s1_0.id desc,
ht1_0.id
BEFORE 실행 계획
-
실행계획 1 : 서브쿼리 풀 테이블 스캔
-
Using Temporary : 쿼리 결과를 생성하기 위해 MySQL이 임시 테이블 사용 ➡️ 메모리에 생성되는데 데이터가 많으면 디스크에 생성될 수도 있음 ➡️ 성능 저하 가능성 있음
-
Using Filesort : ORDER BY 절이 사용 ➕ 인덱스를 사용하여 정렬할 수 없음 ➡️ 데이터의 양이 메모리에 비해 많으면 디스크를 사용하여 정렬 ➡️ 성능 저하 가능성 있음
- 솔루션 내림차순 정렬 고려 :
CREATE INDEX index_solution_desc ON solution (id DESC);
- 복합 인덱스 적용 고려 : CREATE INDEX idx_mission_hash_tag_id ON mission_hash_tag(mission_id, id);
-
-
실행계획 2 : m1_0(mission) 프라이머리키 인덱싱
-
실행계획 3 : fk_solution_mission 외래키 인덱싱
-
실행계획 4 : fk_mission_hash_tag_hash_tag(hash_tag) 외래키 인덱싱
-
실행계획 5 : ht2_0(hash_tag) : 프라이머리키 인덱싱
-
실행계획 6 :
MATERIALIZED
서브쿼리 / where 절 풀 테이블 스캔where mht1_0.mission_id=s1_0.mission_id and (lower(?)='all' or ht3_0.name=?))
-
실행계획 7 :
MATERIALIZED
서브쿼리 / fk_mission_hash_tag_hash_tag(hash_tag) 외래키 인덱싱MATERIALIZED
서브쿼리도 메모리 소모 가능성 있음- 복합 인덱스 적용 고려 :
CREATE INDEX idx_mission_hash_tag_combined ON mission_hash_tag(mission_id, hash_tag_id);
➡️ WHERE 및 JOIN 최적화
- 복합 인덱스 적용 고려 :
<aside>
- 임시 테이블 생성: 서브쿼리의 결과가 메모리나 디스크 상의 임시 테이블에 저장됩니다. 이후 이 테이블이 여러 번 사용될 수 있습니다.
- 성능 최적화: 동일한 서브쿼리를 여러 번 실행하지 않기 때문에 중복 작업을 줄이고 성능을 향상시킵니다.
-
중복 실행 방지:
MATERIALIZED
는 중복 계산을 방지하여 쿼리를 최적화하려는 의도로 사용됩니다. </aside>
explain analyze
select
distinct s1_0.id,
s1_0.created_at,
s1_0.description,
s1_0.member_id,
s1_0.mission_id,
m1_0.id,
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title,
s1_0.url,
s1_0.status,
s1_0.title
from
solution s1_0
join
mission m1_0
on m1_0.id=s1_0.mission_id
join
mission_hash_tag ht1_0
on m1_0.id=ht1_0.mission_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
s1_0.status='COMPLETED'
and exists(select
1
from
mission_hash_tag mht1_0
join
hash_tag ht3_0
on ht3_0.id=mht1_0.hash_tag_id
where
mht1_0.mission_id=s1_0.mission_id
and (lower('객체지향')='all'
or ht3_0.name='객체지향'))
order by
s1_0.id desc,
ht1_0.id;
select
distinct s1_0.id,
s1_0.created_at,
s1_0.description,
s1_0.member_id,
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id,
s1_0.mission_id,
m2_0.id,
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
m2_0.url,
m2_0.summary,
m2_0.thumbnail,
m2_0.title,
s1_0.url,
s1_0.status,
s1_0.title
from
solution s1_0
join
member m1_0
on m1_0.id=s1_0.member_id
join
mission m2_0
on m2_0.id=s1_0.mission_id
join
mission_hash_tag ht1_0
on m2_0.id=ht1_0.mission_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
s1_0.id=?
order by
ht1_0.id
BEFORE 실행계획
- 실행 계획 1 : solution / const(primary) / 기본 키 또는 고유키에 의한 조회
- 실행 계획 2 : member / const(primary) / 기본 키 또는 고유키에 의한 조회
- 실행 계획 3 : mission / const(primary) / 기본 키 또는 고유키에 의한 조회
- 실행 계획 4 : mission_hash_tag / ref(fk_mission_hash_tag_mission) / 고유 키가 아닌 인덱스
- 실행 계획 5 : hash_tag / eq_ref(primary) / 해시 태그 테이블의 기본 키에 의한 조회, 외래키와 기본 키 간의 정확한 1:1 대응일 때 사용
select
d1_0.id,
d1_0.content,
d1_0.created_at,
ht1_0.discussion_id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
d1_0.member_id,
m1_0.id,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title,
d1_0.title
from
discussion d1_0
join
mission m1_0
on m1_0.id=d1_0.mission_id
join
discussion_hash_tag ht1_0
on d1_0.id=ht1_0.discussion_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
(
lower(?)='all'
or m1_0.title=?
)
and (
lower(?)='all'
or exists(select
1
from
discussion_hash_tag dht1_0
join
hash_tag ht3_0
on ht3_0.id=dht1_0.hash_tag_id
where
dht1_0.discussion_id=d1_0.id
and ht3_0.name=?)
)
order by
ht1_0.discussion_id,
ht1_0.hash_tag_id
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
BEFORE 실행 계획
select
d1_0.id,
d1_0.content,
d1_0.created_at,
ht2_0.discussion_id,
ht2_0.hash_tag_id,
ht3_0.id,
ht3_0.name,
d1_0.member_id,
m2_0.id,
m2_0.created_at,
m2_0.email,
m2_0.image_url,
m2_0.name,
m2_0.provider,
m2_0.social_id,
m1_0.id,
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title,
d1_0.title
from
discussion d1_0
join
mission m1_0
on m1_0.id=d1_0.mission_id
join
mission_hash_tag ht1_0
on m1_0.id=ht1_0.mission_id
join
member m2_0
on m2_0.id=d1_0.member_id
join
discussion_hash_tag ht2_0
on d1_0.id=ht2_0.discussion_id
join
hash_tag ht3_0
on ht3_0.id=ht2_0.hash_tag_id
where
d1_0.id=?
order by
ht2_0.discussion_id,
ht2_0.hash_tag_id,
ht1_0.id
BEFORE 실행 계획
select
sc1_0.id,
sc1_0.content,
sc1_0.created_at,
sc1_0.deleted_at,
sc1_0.member_id,
sc1_0.parent_comment_id,
sc1_0.solution_id
from
solution_comment sc1_0
where
sc1_0.solution_id=?
order by
sc1_0.created_at
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
BEFORE 실행 계획
- 실행 계획 1 : solution_comment / ref(fk_solution_comment_solution) / 고유 키가 아닌 인덱스
- 실행 계획 2 : member / const(primary) / 기본 키 또는 고유키에 의한 조회
/discussions/{discussionId}/comments
select
dc1_0.id,
dc1_0.content,
dc1_0.created_at,
dc1_0.deleted_at,
dc1_0.discussion_id,
dc1_0.member_id,
dc1_0.parent_comment_id
from
discussion_comment dc1_0
where
dc1_0.discussion_id=?
order by
dc1_0.created_at
2024-09-25 17:30:12.694 | http-nio-8080-exec-1 | traceId=b00d91b7-8a48-4981-ab69-94f3e723be4b | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
BEFORE 실행 계획
- 실행 계획 1 : discussion_comment / ref(fk_discussion_comment_discussion) / 고유 키가 아닌 인덱스
- 실행 계획 2 : member / const(primary) / 기본 키 또는 고유키에 의한 조회
/discussions/{discussionId}/comments
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
2024-09-25 17:39:46.442 | http-nio-8080-exec-2 | traceId=fc40d324-52cf-4252-8c1c-aea996c3ff1f | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:39:46.443 | http-nio-8080-exec-2 | traceId=fc40d324-52cf-4252-8c1c-aea996c3ff1f | DEBUG | o.s.orm.jpa.JpaTransactionManager | Found thread-bound EntityManager [SessionImpl(274023016<open>)] for JPA transaction
2024-09-25 17:39:46.443 | http-nio-8080-exec-2 | traceId=fc40d324-52cf-4252-8c1c-aea996c3ff1f | DEBUG | o.s.orm.jpa.JpaTransactionManager | Participating in existing transaction
[Hibernate]
select
d1_0.id,
d1_0.content,
d1_0.created_at,
d1_0.member_id,
d1_0.mission_id,
d1_0.title
from
discussion d1_0
where
d1_0.id=?
2024-09-25 17:39:46.444 | http-nio-8080-exec-2 | traceId=fc40d324-52cf-4252-8c1c-aea996c3ff1f | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:39:46.446 | http-nio-8080-exec-2 | traceId=fc40d324-52cf-4252-8c1c-aea996c3ff1f | DEBUG | o.s.orm.jpa.JpaTransactionManager | Found thread-bound EntityManager [SessionImpl(274023016<open>)] for JPA transaction
2024-09-25 17:39:46.446 | http-nio-8080-exec-2 | traceId=fc40d324-52cf-4252-8c1c-aea996c3ff1f | DEBUG | o.s.orm.jpa.JpaTransactionManager | Participating in existing transaction
[Hibernate]
insert
into
discussion_comment
(content, created_at, deleted_at, discussion_id, member_id, parent_comment_id, id)
values
(?, ?, ?, ?, ?, ?, default)
BEFORE 실행 계획
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
- 1번 쿼리(멤버 조회): const(프라이머리 키) 조회
select
d1_0.id,
d1_0.content,
d1_0.created_at,
d1_0.member_id,
d1_0.mission_id,
d1_0.title
from
discussion d1_0
where
d1_0.id=?
- 2번 쿼리(디스커션 조회) : const(프라이머리 키) 조회
insert
into
discussion_comment
(content, created_at, deleted_at, discussion_id, member_id, parent_comment_id, id)
values
(?, ?, ?, ?, ?, ?, default)
- 3번 뭐리(디스커션 댓글 추가) : ALL
select
dc1_0.id,
dc1_0.content,
dc1_0.created_at,
dc1_0.deleted_at,
dc1_0.discussion_id,
dc1_0.member_id,
dc1_0.parent_comment_id
from
discussion_comment dc1_0
where
dc1_0.id=?
2024-09-25 17:40:49.757 | http-nio-8080-exec-4 | traceId=3cdb2753-1d08-42f7-9ba0-371a57766fe5 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [9]
2024-09-25 17:40:49.761 | http-nio-8080-exec-4 | traceId=3cdb2753-1d08-42f7-9ba0-371a57766fe5 | DEBUG | o.s.orm.jpa.JpaTransactionManager | Initiating transaction commit
2024-09-25 17:40:49.761 | http-nio-8080-exec-4 | traceId=3cdb2753-1d08-42f7-9ba0-371a57766fe5 | DEBUG | o.s.orm.jpa.JpaTransactionManager | Committing JPA transaction on EntityManager [SessionImpl(2044199012<open>)]
[Hibernate]
update
discussion_comment
set
content=?,
deleted_at=?,
discussion_id=?,
member_id=?,
parent_comment_id=?
where
id=?
BEFORE 실행계획
select
dc1_0.id,
dc1_0.content,
dc1_0.created_at,
dc1_0.deleted_at,
dc1_0.discussion_id,
dc1_0.member_id,
dc1_0.parent_comment_id
from
discussion_comment dc1_0
where
dc1_0.id=?
2024-09-25 17:41:32.018 | http-nio-8080-exec-5 | traceId=540ec63f-56ff-4309-9c84-7948a794a070 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [8]
[Hibernate]
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
2024-09-25 17:41:32.020 | http-nio-8080-exec-5 | traceId=540ec63f-56ff-4309-9c84-7948a794a070 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:41:32.022 | http-nio-8080-exec-5 | traceId=540ec63f-56ff-4309-9c84-7948a794a070 | DEBUG | o.s.orm.jpa.JpaTransactionManager | Initiating transaction commit
2024-09-25 17:41:32.022 | http-nio-8080-exec-5 | traceId=540ec63f-56ff-4309-9c84-7948a794a070 | DEBUG | o.s.orm.jpa.JpaTransactionManager | Committing JPA transaction on EntityManager [SessionImpl(2010075036<open>)]
[Hibernate]
update
discussion_comment
set
content=?,
deleted_at=?,
discussion_id=?,
member_id=?,
parent_comment_id=?
where
id=?
BEFORE 실행 계획
select
dc1_0.id,
dc1_0.content,
dc1_0.created_at,
dc1_0.deleted_at,
dc1_0.discussion_id,
dc1_0.member_id,
dc1_0.parent_comment_id
from
discussion_comment dc1_0
where
dc1_0.id=?
- 1번 쿼리(디스커션 댓글 조회): const(프라이머리 키) 조회
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
- 2번 쿼리(멤버 조회): const(프라이머리 키) 조회
update
discussion_comment
set
content=?,
deleted_at=?,
discussion_id=?,
member_id=?,
parent_comment_id=?
where
id=?
- 3번 쿼리(디스커션 댓글 수정) : range(프라이머리 키 특정 범위 행만 검색)
- 프라이머리 키라서 const가 나와야할 것 같은데 왜 range 조회하는지?
- 다른 테이블에서 외래키로 사용되어서일 가능성이 가장 높다.
- 프라이머리 키라서 const가 나와야할 것 같은데 왜 range 조회하는지?
select
dc1_0.id,
dc1_0.discussion_id,
dc1_0.content,
dc1_0.created_at,
d1_0.title,
count(dc1_0.id)
from
discussion_comment dc1_0
join
discussion d1_0
on d1_0.id=dc1_0.discussion_id
where
dc1_0.member_id=?
and dc1_0.deleted_at is null
group by
dc1_0.id
- 실행 계획 1 : discussion_comment / ref(fk_discussion_comment_member) / 고유 키가 아닌 인덱스
- 실행 계획 2 : discussion / eq_ref(primary) / 조인에서 처음 읽은 테이블의 칼럼값을, 다음 읽을 테이블의 PK나 유니크 키 칼럼의 검색 조건에 사용
select
d1_0.id,
d1_0.content,
d1_0.created_at,
ht1_0.discussion_id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
d1_0.member_id,
m2_0.id,
m2_0.created_at,
m2_0.email,
m2_0.image_url,
m2_0.name,
m2_0.provider,
m2_0.social_id,
m1_0.id,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title,
d1_0.title
from
discussion d1_0
join
mission m1_0
on m1_0.id=d1_0.mission_id
join
member m2_0
on m2_0.id=d1_0.member_id
join
discussion_hash_tag ht1_0
on d1_0.id=ht1_0.discussion_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
d1_0.member_id=?
order by
ht1_0.discussion_id,
ht1_0.hash_tag_id
- 실행 계획 1 : member / const(primary) / 기본 키 또는 고유키에 의한 조회
- 실행 계획 2 : mission / all / rows가 낮아서 그런가?
- 실행 계획 3 : discussion / ref(fk_discussion_mission) / 고유 키가 아닌 인덱스
- 실행 계획 4 : mission_hash_tag / ref(primary) / 고유 키가 아닌 인덱스, using index(커버링 인덱스)
- 실행 계획 5 : hash_tag / eq_ref(primary) / 조인에서 처음 읽은 테이블의 칼럼값을, 다음 읽을 테이블의 PK나 유니크 키 칼럼의 검색 조건에 사용
select
m1_0.id,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title
from
mission m1_0
where
m1_0.id=?
2024-09-25 17:45:03.778 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:45:03.780 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | DEBUG | o.s.orm.jpa.JpaTransactionManager | Found thread-bound EntityManager [SessionImpl(1067221385<open>)] for JPA transaction
2024-09-25 17:45:03.780 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | DEBUG | o.s.orm.jpa.JpaTransactionManager | Participating in existing transaction
[Hibernate]
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
2024-09-25 17:45:03.780 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0
where
ht1_0.id in (?)
2024-09-25 17:45:03.784 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:45:03.785 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | DEBUG | o.s.orm.jpa.JpaTransactionManager | Found thread-bound EntityManager [SessionImpl(1067221385<open>)] for JPA transaction
2024-09-25 17:45:03.785 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | DEBUG | o.s.orm.jpa.JpaTransactionManager | Participating in existing transaction
[Hibernate]
insert
into
discussion
(content, created_at, member_id, mission_id, title, id)
values
(?, ?, ?, ?, ?, default)
2024-09-25 17:45:03.787 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:VARCHAR) <- [string]
2024-09-25 17:45:03.787 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (2:TIMESTAMP) <- [2024-09-25T17:45:03.786059]
2024-09-25 17:45:03.787 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (3:BIGINT) <- [1]
2024-09-25 17:45:03.787 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (4:BIGINT) <- [1]
2024-09-25 17:45:03.787 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (5:VARCHAR) <- [string]
[Hibernate]
select
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id
from
mission_hash_tag ht1_0
where
ht1_0.mission_id=?
order by
ht1_0.id
2024-09-25 17:45:03.788 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0
where
ht1_0.id=?
2024-09-25 17:45:03.790 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [2]
[Hibernate]
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0
where
ht1_0.id=?
2024-09-25 17:45:03.790 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [3]
2024-09-25 17:45:03.790 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | DEBUG | o.s.orm.jpa.JpaTransactionManager | Initiating transaction commit
2024-09-25 17:45:03.790 | http-nio-8080-exec-2 | traceId=538f4406-1fbe-4d63-b526-50c8b3db31ab | DEBUG | o.s.orm.jpa.JpaTransactionManager | Committing JPA transaction on EntityManager [SessionImpl(1067221385<open>)]
[Hibernate]
insert
into
discussion_hash_tag
(discussion_id, hash_tag_id)
values
(?, ?)
- 1번 쿼리(특정 미션 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
- 2번 쿼리(특정 멤버 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
- 3번 쿼리(해시태그 IN 조회) : range(primary) / 인덱스 특정 범위 행 접근
- 4번 쿼리(디스커션 삽입) : ALL
- 5번 쿼리(미션 아이디 기반 미션 해시 태그 조회) : ref(fk_mission_hash_tag_mission) / 고유 키가 아닌 인덱스
- 6번 쿼리(특정 해시 태그 조회) : const(primry) / 기본 키 또는 고유키에 의한 조회
- 7번 쿼리(특정 해시 태그 조회) : DicussionWriteService.create N + 1 확인 (save의 반환값으로 응답 만들기 때문)
- 8번 쿼리(디스커션 해시 태그 삽입) : ALL
select
sc1_0.id,
sc1_0.content,
sc1_0.created_at,
sc1_0.deleted_at,
sc1_0.member_id,
sc1_0.parent_comment_id,
sc1_0.solution_id
from
solution_comment sc1_0
where
sc1_0.solution_id=?
order by
sc1_0.created_at
2024-09-25 17:56:56.083 | http-nio-8080-exec-8 | traceId=de8e5400-5b25-44fa-ae5d-1b1a4aa405dc | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
- 1번 쿼리(특정 solution comment 조회) : ref(fk_solution_comment_solution) / 고유 키가 아닌 인덱스 / filesort → create_at 정렬
- 2번 쿼리(사용자 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
2024-09-25 17:58:28.722 | http-nio-8080-exec-10 | traceId=3909f511-eca4-4ceb-8362-182b3674087b | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:58:28.724 | http-nio-8080-exec-10 | traceId=3909f511-eca4-4ceb-8362-182b3674087b | DEBUG | o.s.orm.jpa.JpaTransactionManager | Found thread-bound EntityManager [SessionImpl(1904910575<open>)] for JPA transaction
2024-09-25 17:58:28.724 | http-nio-8080-exec-10 | traceId=3909f511-eca4-4ceb-8362-182b3674087b | DEBUG | o.s.orm.jpa.JpaTransactionManager | Participating in existing transaction
[Hibernate]
select
s1_0.id,
s1_0.created_at,
s1_0.description,
s1_0.member_id,
s1_0.mission_id,
s1_0.url,
s1_0.status,
s1_0.title
from
solution s1_0
where
s1_0.id=?
2024-09-25 17:58:28.725 | http-nio-8080-exec-10 | traceId=3909f511-eca4-4ceb-8362-182b3674087b | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:58:28.727 | http-nio-8080-exec-10 | traceId=3909f511-eca4-4ceb-8362-182b3674087b | DEBUG | o.s.orm.jpa.JpaTransactionManager | Found thread-bound EntityManager [SessionImpl(1904910575<open>)] for JPA transaction
2024-09-25 17:58:28.727 | http-nio-8080-exec-10 | traceId=3909f511-eca4-4ceb-8362-182b3674087b | DEBUG | o.s.orm.jpa.JpaTransactionManager | Participating in existing transaction
[Hibernate]
insert
into
solution_comment
(content, created_at, deleted_at, member_id, parent_comment_id, solution_id, id)
values
(?, ?, ?, ?, ?, ?, default)
- 1번 쿼리(특정 사용자 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
- 2번 쿼리(특정 솔루션 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
- 3번 쿼리(특정 댓글 삽입) : ALL / 실행 계획 볼 수 있는 곳인가?
select
sc1_0.id,
sc1_0.content,
sc1_0.created_at,
sc1_0.deleted_at,
sc1_0.member_id,
sc1_0.parent_comment_id,
sc1_0.solution_id
from
solution_comment sc1_0
where
sc1_0.id=?
2024-09-25 17:59:24.460 | http-nio-8080-exec-1 | traceId=e8b3ed3e-7ead-4a74-baa8-2efcd54ee920 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 17:59:24.462 | http-nio-8080-exec-1 | traceId=e8b3ed3e-7ead-4a74-baa8-2efcd54ee920 | DEBUG | o.s.orm.jpa.JpaTransactionManager | Initiating transaction commit
2024-09-25 17:59:24.462 | http-nio-8080-exec-1 | traceId=e8b3ed3e-7ead-4a74-baa8-2efcd54ee920 | DEBUG | o.s.orm.jpa.JpaTransactionManager | Committing JPA transaction on EntityManager [SessionImpl(505973396<open>)]
[Hibernate]
update
solution_comment
set
content=?,
deleted_at=?,
member_id=?,
parent_comment_id=?,
solution_id=?
where
id=?
- 1번 쿼리(특정 solution comment 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
- 2번 쿼리(특정 댓글 업데이트) : range(primary) / 인덱스 특정 범위 행 접근
select
sc1_0.id,
sc1_0.solution_id,
sc1_0.content,
sc1_0.created_at,
s1_0.title,
count(sc1_0.id)
from
solution_comment sc1_0
join
solution s1_0
on s1_0.id=sc1_0.solution_id
where
sc1_0.member_id=?
and sc1_0.deleted_at is null
group by
sc1_0.id
- 실행 계획 1 : solution_comment / ref(fk_solution_comment_member) / 고유 키가 아닌 인덱스
- 실행 계획 2 : solution / eq_ref(primary) / 조인에서 처음 읽은 테이블의 칼럼값을, 다음 읽을 테이블의 PK나 유니크 키 칼럼의 검색 조건에 사용
select
distinct m1_0.id,
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title
from
mission m1_0
join
mission_hash_tag ht1_0
on m1_0.id=ht1_0.mission_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
exists(select
1
from
mission_hash_tag mht1_0
join
hash_tag ht3_0
on ht3_0.id=mht1_0.hash_tag_id
where
mht1_0.mission_id=m1_0.id
and (lower(?)='all'
or ht3_0.name=?))
order by
ht1_0.id
-> Sort: ht1_0.id (actual time=8.057..8.073 rows=20 loops=1)
-> Table scan on <temporary> (cost=0.04..3.76 rows=102) (actual time=0.005..0.026 rows=20 loops=1)
-> Temporary table with deduplication (cost=168.31..172.03 rows=102) (actual time=7.170..7.197 rows=20 loops=1)
-> Nested loop inner join (cost=158.11 rows=102) (actual time=5.318..6.166 rows=20 loops=1)
-> Nested loop inner join (cost=122.55 rows=102) (actual time=5.197..5.876 rows=20 loops=1)
-> Nested loop inner join (cost=111.65 rows=34) (actual time=4.770..4.961 rows=6 loops=1)
-> Table scan on <subquery2> (cost=0.09..2.92 rows=34) (actual time=0.005..0.008 rows=6 loops=1)
-> Materialize with deduplication (cost=26.40..29.24 rows=34) (actual time=4.439..4.445 rows=6 loops=1)
-> Nested loop inner join (cost=22.91 rows=34) (actual time=3.719..4.134 rows=6 loops=1)
-> Filter: (ht3_0.`name` = '객체지향') (cost=11.25 rows=11) (actual time=3.288..3.462 rows=1 loops=1)
**-> Table scan on ht3_0 (cost=11.25 rows=110) (actual time=2.893..3.198 rows=110 loops=1)**
-> Index lookup on mht1_0 using fk_mission_hash_tag_hash_tag (hash_tag_id=ht3_0.id) (cost=0.78 rows=3) (actual time=0.291..0.419 rows=6 loops=1)
-> Single-row index lookup on m1_0 using PRIMARY (id=`<subquery2>`.mission_id) (cost=79.10 rows=1) (actual time=0.082..0.082 rows=1 loops=6)
-> Index lookup on ht1_0 using fk_mission_hash_tag_mission (mission_id=`<subquery2>`.mission_id) (cost=1.04 rows=3) (actual time=0.143..0.150 rows=3 loops=6)
-> Single-row index lookup on ht2_0 using PRIMARY (id=ht1_0.hash_tag_id) (cost=8.55 rows=1) (actual time=0.013..0.013 rows=1 loops=20)
- 서브 쿼리 내부 hash_tag 테이블 스캔
select
distinct m1_0.id,
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id,
ht2_0.id,
ht2_0.name,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title
from
mission m1_0
join
mission_hash_tag ht1_0
on m1_0.id=ht1_0.mission_id
join
hash_tag ht2_0
on ht2_0.id=ht1_0.hash_tag_id
where
m1_0.id=?
order by
ht1_0.id
- 실행 계획 1 : mission / const(primary) / 기본 키 또는 고유키에 의한 조회
- 실행 계획 2 : mission_hash_tag / ref(fk_mission_hash_tag_mission) / 고유 키가 아닌 인덱스
- 실행 계획 3 : hash_tag / eq_ref(primary) / 조인에서 처음 읽은 테이블의 칼럼값을, 다음 읽을 테이블의 PK나 유니크 키 칼럼의 검색 조건에 사용
select
s1_0.id,
s1_0.created_at,
s1_0.description,
s1_0.member_id,
s1_0.mission_id,
s1_0.url,
s1_0.status,
s1_0.title
from
solution s1_0
where
s1_0.member_id=?
and s1_0.status=?
2024-09-25 19:59:06.683 | http-nio-8080-exec-2 | traceId=cbea50b2-9e6d-415f-aea3-16124b2c90b2 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
2024-09-25 19:59:06.683 | http-nio-8080-exec-2 | traceId=cbea50b2-9e6d-415f-aea3-16124b2c90b2 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (2:VARCHAR) <- [IN_PROGRESS]
[Hibernate]
select
m1_0.id,
m1_0.url,
m1_0.summary,
m1_0.thumbnail,
m1_0.title
from
mission m1_0
where
m1_0.id=?
2024-09-25 19:59:06.684 | http-nio-8080-exec-2 | traceId=cbea50b2-9e6d-415f-aea3-16124b2c90b2 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
ht1_0.mission_id,
ht1_0.id,
ht1_0.hash_tag_id
from
mission_hash_tag ht1_0
where
ht1_0.mission_id=?
order by
ht1_0.id
2024-09-25 19:59:06.685 | http-nio-8080-exec-2 | traceId=cbea50b2-9e6d-415f-aea3-16124b2c90b2 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0
where
ht1_0.id=?
2024-09-25 19:59:06.685 | http-nio-8080-exec-2 | traceId=cbea50b2-9e6d-415f-aea3-16124b2c90b2 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [1]
[Hibernate]
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0
where
ht1_0.id=?
2024-09-25 19:59:06.686 | http-nio-8080-exec-2 | traceId=cbea50b2-9e6d-415f-aea3-16124b2c90b2 | TRACE | org.hibernate.orm.jdbc.bind | binding parameter (1:BIGINT) <- [2]
[Hibernate]
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0
where
ht1_0.id=?
1번 쿼리(특정 사용자의 status 기반 solution 조회)/ ref(fk_solution_member) / 고유 키가 아닌 인덱스
2번 쿼리(사용자 조회) : const(primary) / 기본 키 또는 고유키에 의한 조회
3번 쿼리(특정 미션에 태그된 해시 태그 조회) : ref(fk_mission_hash_tag_mission) / 고유 키가 아닌 인덱스
4번 쿼리(특정 해시 태그 조회) : const(primry) / 기본 키 또는 고유키에 의한 조회
5번 쿼리(특정 해시 태그 조회) : N + 1 (SolutionRepository.findAllByMember_IdAndStatus)
6번 쿼리(특정 해시 태그 조회) : N + 1
select
m1_0.id,
m1_0.created_at,
m1_0.email,
m1_0.image_url,
m1_0.name,
m1_0.provider,
m1_0.social_id
from
member m1_0
where
m1_0.id=?
select
ht1_0.id,
ht1_0.name
from
hash_tag ht1_0