์ด์ Velog์ ๋์ฉ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์กฐํ ์ฑ๋ฅ์ ๊ฐ์ ํด๋ณด์ (1) : ์ธ๋ฑ์ค ์ ์ฉ (2023.10.27) ๋ก๋ถํฐ ๋ง์ด๊ทธ๋ ์ด์ ๋ ๊ธ์ ๋๋ค.
๋๊ธฐ
์ง๋ ๋ฒ์ ๋ค์ํ JPA ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ๋นํจ์จ์ ์ผ๋ก ์์ฑ๋๋ ์ฟผ๋ฆฌ๋ฅผ ์ต์ ํํด ์ ์ฒด์ ์ธ ์๋ฒ ์ฑ๋ฅ์ด ๋ํญ ํฅ์๋์๋ค. ํ์ง๋ง ๋ฆฌ๋ทฐ ์กฐํ API์ ์ผ๋ถ ์์ฒญ์ ๊ฒฝ์ฐ ๋ํญ ํฅ์๋์์์๋ ๋ง์กฑ์ค๋ฌ์ด ์ฟผ๋ฆฌ ์ฒ๋ฆฌ ์๊ฐ์ ๋๋ฌํ์ง ๋ชปํ๋ค. ๋ค์ํ ์ ๋ ฌ๊ณผ ํํฐ๋ง์ ์ง์ํ๋ ์กฐํ ์ฟผ๋ฆฌ์ธ๋งํผ ๋๋์ ๋ฐ์ดํฐ๋ฅผ ์กฐ๊ฑด์ , ์ ๋ ฌ ์ ์ฉํ๋๋ฐ ๋ง์ ๋น์ฉ์ด ๋ค๊ณ , ํนํ ๋ฆฌ๋ทฐ์ ๋ถ์ ๊ฒฐ๊ณผ ์ค '์ฒญ๊ฒฐ' ์์ฑ์ด ํฌํจ๋ ๋ฆฌ๋ทฐ๋ค์ด๋ ๋ฆฌ๋ทฐ์ ๋ถ์ ๊ฒฐ๊ณผ ์ค '์ฒญ๊ฒฐ' ์์ฑ์ '๋จผ์ง' ํค์๋๊ฐ ํฌํจ๋ ๋ฆฌ๋ทฐ๋ค๊ณผ ๊ฐ์ด review ํ ์ด๋ธ๊ณผ ์ฐ๊ด๋์์ผ๋ฉด์ ๊ฐ์ฅ ๋ง์ ๋ฐ์ดํฐ๊ฐ ์ ์ฌ๋ review_tag ํ ์ด๋ธ์ ์ฌ์ฉํ๋ ์ฟผ๋ฆฌ๋ค์ ๊ธฐ๋ณธ์ ์ผ๋ก ์กด์ฌํ๋ ํด๋ฌ์คํฐ๋ง ์ธ๋ฑ์ค์ ์ธ๋ํค ์ธ๋ฑ์ค๋ก๋ ์ธ๋ฑ์ค๋ฅผ ํ์ง ์์ ๋ค๋ฅธ ์ฟผ๋ฆฌ ๋๋น 80๋ฐฐ ์ ๋ ๋ ๊ธด ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ์ด ์์๋์๋ค. ์ด์ ๋ฐ๋ผ ์ฟผ๋ฆฌ๋ฅผ ํ๋ํ๋ ํ๋กํ์ผ๋งํด๋ณด๋ฉฐ ์์ ํ๊ณ ์ธ๋ฑ์ค๋ฅผ ์ ์ฉํด ์ฑ๋ฅ์ ๊ฐ์ ํด๋ณด์.
์ฑ๋ฅ ํ ์คํธ
๋์๋ณด๋ API ์ฑ๋ฅ์ ์ธก์ ํ๋ ๊ณผ์ ์์ ์ฐ๋จ์์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์, ์ฑ๋ฅ์ ๋ค์ ์ธก์ ํด์ผํ๋ค. ํ ์คํธ ๋ฐ์ดํฐ์น๊ณ ๋ ์กฐ๊ธ ๋ง์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์์ง๋ง ๋ฆฌ๋ทฐ 54๋ง ๊ฐ๋ ํ๋ก๋์ ์์ ์์ ํ ์ ์๋ ์ค์ผ์ผ์ด๋ผ๊ณ ์๊ฐํ๋ค.
ํ ์คํธ ๋ฐ์ดํฐ
- ์ฌํ์ํ: 30 (๊ฐ ์นดํ ๊ณ ๋ฆฌ 10๊ฐ์ฉ, ์์, ์๋น, ๋ ํฐ์นด)
- ์์ฝ: 540,000 (์นดํ
๊ณ ๋ฆฌ ๋ณ 180,000)
- ์ฌํ์ํ ๋ง๋ค 18,000๊ฐ์ ์์ฝ
- ๋ฆฌ๋ทฐ: 540,000 (์นดํ
๊ณ ๋ฆฌ ๋ณ 180,000)
- ๋ชจ๋ ์์ฝ์ ๋ฆฌ๋ทฐ ์์ฑ
- ๋ฆฌ๋ทฐ ํ๊ทธ: 2,700,000 (์นดํ
๊ณ ๋ฆฌ ๋ณ 900,000)
- ๊ฐ ๋ฆฌ๋ทฐ๋น 5๊ฐ์ ๋ฆฌ๋ทฐ ํ๊ทธ
ํ ์คํธ ๊ฒฐ๊ณผ
์ฒซ ์ฑ๋ฅ ํ ์คํธ์ ๋นํด 4๋ฐฐ์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ์์ฒญ์ด ์ ํํ 4๋ฐฐ์ ์๊ฐ์ด ์์๋์๋ค. ์ธ๋ฑ์ค๋ฅผ ํ์ง ์๊ณ Full Table Scanํ๊ณ ์๋ค๋ ๊ฒ์ ๊ฐ์ ์ ์ผ๋ก๋ ์ ์ ์์๋ค. ํนํ, 2543 ms, 7147 ms๋ ๋น์ ์์ ์ธ๋ฐ, ํด๋น ์์ฒญ๋ค์ด ๋ฆฌ๋ทฐ ํ๊ทธ์ ์์ฑ๊ณผ ํค์๋๋ฅผ ํตํด ํํฐ๋งํ๋ ์์ฒญ์ด๋ค. ๊ทธ๋ผ ํด๋น ์์ฒญ๋ค์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ต์ ํํด๋ณด์.
2023-10-27 04:19:58.851 INFO 99446 --- [ Test worker] PERFORMANCE : ===== ์ฑ๋ฅ ํ
์คํธ ์์ =====
2023-10-27 04:19:58.851 INFO 99446 --- [ Test worker] PERFORMANCE : (4/5) ๋ฆฌ๋ทฐ API
2023-10-27 04:19:59.152 INFO 99446 --- [o-auto-1-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-10-27 04:19:59.152 INFO 99446 --- [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-10-27 04:19:59.152 INFO 99446 --- [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
2023-10-27 04:20:01.909 WARN 99446 --- [o-auto-1-exec-1] PERFORMANCE : uri: '/api/widget/v1/goodchoice.kr/products/123/reviews', method: 'GET', ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ: 2741.021375 ms, ์ฟผ๋ฆฌ ๊ฐ์: 14, ์ฟผ๋ฆฌ ์๊ฐ: 2543.819333 ms
2023-10-27 04:20:09.110 WARN 99446 --- [o-auto-1-exec-2] PERFORMANCE : uri: '/api/widget/v1/goodchoice.kr/products/123/reviews', method: 'GET', ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ: 7177.475416 ms, ์ฟผ๋ฆฌ ๊ฐ์: 14, ์ฟผ๋ฆฌ ์๊ฐ: 7147.840791 ms
2023-10-27 04:20:09.152 WARN 99446 --- [o-auto-1-exec-3] PERFORMANCE : uri: '/api/widget/v1/goodchoice.kr/products/123/reviews', method: 'GET', ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ: 31.841500 ms, ์ฟผ๋ฆฌ ๊ฐ์: 14, ์ฟผ๋ฆฌ ์๊ฐ: 18.417707 ms
2023-10-27 04:20:09.492 WARN 99446 --- [o-auto-1-exec-4] PERFORMANCE : uri: '/api/widget/v1/goodchoice.kr/products/123/reviews', method: 'GET', ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ: 332.011875 ms, ์ฟผ๋ฆฌ ๊ฐ์: 14, ์ฟผ๋ฆฌ ์๊ฐ: 314.414834 ms
2023-10-27 04:20:09.638 WARN 99446 --- [o-auto-1-exec-5] PERFORMANCE : uri: '/api/widget/v1/goodchoice.kr/products/4e16aa67-a2fd-4c92-ad11-775a3ad190e4/statistics/reviews', method: 'GET', ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ: 139.288167 ms, ์ฟผ๋ฆฌ ๊ฐ์: 2, ์ฟผ๋ฆฌ ์๊ฐ: 112.839875 ms
2023-10-27 04:20:10.348 WARN 99446 --- [o-auto-1-exec-6] PERFORMANCE : uri: '/api/widget/v1/goodchoice.kr/products/4e16aa67-a2fd-4c92-ad11-775a3ad190e4/statistics/tags', method: 'GET', ์์ฒญ ์ฒ๋ฆฌ ์๊ฐ: 701.198125 ms, ์ฟผ๋ฆฌ ๊ฐ์: 2, ์ฟผ๋ฆฌ ์๊ฐ: 689.250418 ms
์ต์ ํ
์์ธ ๋ถ์
๋ฌธ์ ๊ฐ ๋๋ ํด๋น ์์ฒญ์ "goodchoice.kr" ๋๋ฉ์ธ์ id๊ฐ 123์ธ ์ฌํ ์ํ์ ๋ฑ๋ก๋ ๋ฆฌ๋ทฐ ์ค ์ฒญ๊ฒฐ(CLEANNESS) ํ๊ทธ๋ฅผ ๊ฐ์ง ๋ฆฌ๋ทฐ๋ค์ 10๊ฐ ์กฐํํ๋ ์์ฒญ์ด๋ค.
GET /api/widget/v1/goodchoice.kr/products/123/reviews
{"property": "CLEANNESS"}
๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ ์ฟผ๋ฆฌ๋ค๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
1. ์กฐ๊ฑด์ ๋ง์กฑํ๋ review๋ค์ ์กฐํํ๋ ์ฟผ๋ฆฌ
2์ด ๊ฐ๋ ๊ฑธ๋ฆฌ๋ ์์ฒญ์์ 1683ms๊ฐ ์์๋๋ ์ฟผ๋ฆฌ๋ก์, ์ฑ๋ฅ ์ ํ์ ์ฃผ์ ์์ธ์ด ๋๋ ์ฟผ๋ฆฌ์ด๋ค. ํนํ ๋ฆฌ๋ทฐ์ ํฌํจ๋ ํ๊ทธ๋ค ์ค ํ๋๋ผ๋ CLEANNESS๊ฐ ํฌํจ๋์๋ค๋ฉด ์กฐํ๋ผ๋ ์ฟผ๋ฆฌ๋ฅผ QueryDSL any()๋ฅผ ํตํด ๊ตฌํํ์๋๋ฐ ์๋ธ์ฟผ๋ฆฌ๋ก์ ์คํ๋๋ค. ์ฟผ๋ฆฌ ์คํ ๊ณํ์ ๋ณด๋ฉด <subquery2>์ reviewtags3_์ด ์ธ๋ฑ์ค๋ ์์ด ์์ฒญ๋ ์์ rows๋ฅผ ๋ค๋ฃจ๊ณ ์๋ค. ๋ฐ๋ผ์ ์ด ๋ถ๋ถ์ด ์ต์ ํ ํฌ์ธํธ 1์ด๋ค.
select
review0_.review_id as review_i1_8_,
review0_.created_at as created_2_8_,
review0_.updated_at as updated_3_8_,
review0_.content as content4_8_,
review0_.negative_tags_count as negative5_8_,
review0_.polarity as polarity6_8_,
review0_.positive_tags_count as positive7_8_,
review0_.rating as rating8_8_,
review0_.title as title9_8_
from
review review0_
where
exists (
select
1
from
review_tag reviewtags3_
where
review0_.review_id=reviewtags3_.review_id
and reviewtags3_.property=?
)
order by
review0_.created_at desc limit ?
10 rows retrieved starting from 1 in 1 s 683 ms (execution: 1 s 667 ms, fetching: 16 ms)
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
1 | SIMPLE | review0_ | null | ALL | PRIMARY | null | null | null | 448427 | 100 | Using filesort |
1 | SIMPLE | <subquery2> | null | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 8 | reviewmate.review0_.review_id | 1 | 100 | null |
2 | MATERIALIZED | reviewtags3_ | null | ALL | FKea2voymuynf2rmdx7ph30cwoe | null | null | null | 2177808 | 10 | Using where |
2. ๋ฆฌ๋ทฐ์ ์์ฑ์ ์ด๋ฆ์ ์ํด ์ฐ๊ด๋ reservation, customer ํ ์ด๋ธ์ ๊ทธ๋ํ ํ์ํ๋ ์ฟผ๋ฆฌ
review ํ ์ด๋ธ์ ์ง์ฐ ๋ก๋ฉ๋์ง๋ง ์์ฑ์์ ์ด๋ฆ์ ์๊ธฐ ์ํด 2๊ฐ์ ํ ์ด๋ธ์ ๊ทธ๋ํ ํ์ํ๊ธฐ ์ํด (๋ฆฌ๋ทฐ ๊ฐ์ * 2) ๋งํผ์ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์๋๊ณ ์๋ค. ํ์ง๋ง review ํ ์ด๋ธ์ ์์ฑ์ ์ด๋ฆ์ ๋น์ ๊ทํํ์ง ์๋ ์ด์ ๋ถ๊ฐํผํ ํ์์ด๋ฉฐ, ๋ณธ ํฌ์คํ ๊ณผ ๋ฌด๊ดํ์ง๋ง fetch join์ ํตํด ์ฟผ๋ฆฌ ๊ฐ์๋ฅผ ์ค์ด๋ ์๋๋ ํ์์ง๋ง, reservation๊ณผ customer์ ์๋ ๋ง์ ์ปฌ๋ผ๋ค์ด ๋ถํ์ํ๊ฒ ์กฐํ๋๋ ๊ฒ์ด ์คํ๋ ค ๋ง์ ์๊ฐ์ผ ์๋ชจ์์ผฐ๋ค. ์ด ์ฟผ๋ฆฌ๋ค์ ์๊ฐ๋ 134 ms๋ก ์ค์ํ๋ฏ๋ก, ๋์ด๊ฐ๋๋ก ํ์.
select
reservatio0_.reservation_id as reservat1_7_0_,
reservatio0_.created_at as created_2_7_0_,
reservatio0_.updated_at as updated_3_7_0_,
reservatio0_.customer_id as customer7_7_0_,
reservatio0_.end_date_time as end_date4_7_0_,
reservatio0_.live_feedback_id as live_fee8_7_0_,
reservatio0_.live_satisfaction_id as live_sat9_7_0_,
reservatio0_.partner_custom_id as partner_5_7_0_,
reservatio0_.review_id as review_10_7_0_,
reservatio0_.start_date_time as start_da6_7_0_,
reservatio0_.travel_product_id as travel_11_7_0_
from
reservation reservatio0_
where
reservatio0_.review_id=?
-- X 10
select
customer0_.customer_id as customer1_0_0_,
customer0_.created_at as created_2_0_0_,
customer0_.updated_at as updated_3_0_0_,
customer0_.kakao_id as kakao_id4_0_0_,
customer0_.name as name5_0_0_,
customer0_.partner_company_id as partner_8_0_0_,
customer0_.partner_custom_id as partner_6_0_0_,
customer0_.phone_number as phone_nu7_0_0_
from
customer customer0_
where
customer0_.customer_id in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
10 rows retrieved starting from 1 in 134 ms (execution: 111 ms, fetching: 23 ms)
3. Pagination์ totalCount๋ฅผ ์ํด ์กฐ๊ฑด์ ๋ง์กฑํ๋ review๋ค์ ์นด์ดํ ํ๋ ์ฟผ๋ฆฌ
IN ์ฟผ๋ฆฌ๋ก ์ ์ต์ ํ๋์ด ์๋ค.
select
count(review0_.review_id) as col_0_0_
from
review review0_
where
review0_.review_id in (
? , ? , ? , ? , ? , ? , ? , ? , ? , ?
)
4. ์กฐํ๋ review์ ์ฐ๊ด๋ review_tag๋ค์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ธฐ ์ํ ์ถ๊ฐ ์ฟผ๋ฆฌ
response DTO๋ฅผ ๋ง๋๋ ๊ณผ์ ์์ ์์ฒญ๋๋ ์ถ๊ฐ ์กฐํ ์ฟผ๋ฆฌ์ง๋ง, ๋ง์ฐฌ๊ฐ์ง๋ก IN ์ฟผ๋ฆฌ๋ก ์ ์ต์ ํ๋์ด ์๋ค.
select
reviewtags0_.review_id as review_i9_10_1_,
reviewtags0_.review_tag_id as review_t1_10_1_,
reviewtags0_.review_tag_id as review_t1_10_0_,
reviewtags0_.created_at as created_2_10_0_,
reviewtags0_.updated_at as updated_3_10_0_,
reviewtags0_.end_index as end_inde4_10_0_,
reviewtags0_.keyword as keyword5_10_0_,
reviewtags0_.polarity as polarity6_10_0_,
reviewtags0_.review_id as review_i9_10_0_,
reviewtags0_.property as property7_10_0_,
reviewtags0_.start_index as start_in8_10_0_
from
review_tag reviewtags0_
where
reviewtags0_.review_id in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
๊ฐ์
select
review0_.review_id as review_i1_8_,
review0_.created_at as created_2_8_,
review0_.updated_at as updated_3_8_,
review0_.content as content4_8_,
review0_.negative_tags_count as negative5_8_,
review0_.polarity as polarity6_8_,
review0_.positive_tags_count as positive7_8_,
review0_.rating as rating8_8_,
review0_.title as title9_8_
from
review review0_
where
exists (
select
1
from
review_tag reviewtags3_
where
review0_.review_id=reviewtags3_.review_id
and reviewtags3_.property=?
)
order by
review0_.created_at desc limit ?
10 rows retrieved starting from 1 in 1 s 683 ms (execution: 1 s 667 ms, fetching: 16 ms)
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
1 | SIMPLE | review0_ | null | ALL | PRIMARY | null | null | null | 448427 | 100 | Using filesort |
1 | SIMPLE | <subquery2> | null | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 8 | reviewmate.review0_.review_id | 1 | 100 | null |
2 | MATERIALIZED | reviewtags3_ | null | ALL | FKea2voymuynf2rmdx7ph30cwoe | null | null | null | 2177808 | 10 | Using where |
1. review_tag์ (review_id, property) ์ธ๋ฑ์ค ์ ์ฉ
์ฐ์ , ์๋ธ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ณ ๊ฐ์ฅ ๋ง์ rows๋ฅผ ๋ค๋ฃจ๊ณ ์๋ review_tag ๋ถ๋ถ๋ถํฐ ์ ์ฉํด๋ณด์. ์กฐ๊ฑด์ ์์ review_id์ property๋ฅผ and ๋ก ์ ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์, ๋ณตํฉ ์ธ๋ฑ์ค๋ฅผ ์์์ ๋ง๊ฒ ์์ฑํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ธ๋ฑ์ค๋ฅผ ํ์ง ์๋๋ค.
create index review_tag_review_id_property_index on review_tag (review_id, property);
10 rows retrieved starting from 1 in 1 s 180 ms (execution: 1 s 166 ms, fetching: 14 ms)
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | fileted | Extra |
1 | SIMPLE | review0_ | null | ALL | PRIMARY | null | null | null | 448427 | 100 | Using filesort |
1 | SIMPLE | <subquery2> | null | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 8 | reviewmate.review0_.review_id | 1 | 100 | null |
2 | MATERIALIZED | reviewtags3_ | null | index | review_tag_review_id_property_index | review_tag_review_id_property_index | 775 | null | 2177808 | 10 | Using where; Using index |
์์ฑํ ์ธ๋ฑ์ค๊ฐ ์ ์์ ์ผ๋ก ํ์ก์ผ๋ฉฐ, ์ธ๋ฑ์ค์ ํ์ํ review_tag์ ์ปฌ๋ผ๋ค์ด ๋ชจ๋ ์์ผ๋ฏ๋ก ์ปคํผ๋ง ์ธ๋ฑ์ค๊ฐ ์๋ํ ๋ชจ์ต์ด๋ค. ์ค์ ๋ก 1667 ms -> 1166 ms๋ก 30%๊ฐ ๋จ์ถ๋๋ค.
Extra์ ์ฌ๋ฌ ์ ๋ต๋ค์ด ์๊ฒผ๋๋ฐ,
- Using where : ์คํ ๋ฆฌ์ง ์์ง์ด ๋๊ฒจ ์ค ๋ฐ์ดํฐ (์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํด ๊ฑธ๋ฌ์ง ๋ฐ์ดํฐ) ์ค์์ MySQL ์ค์ง์ด ํ๋ฒ ๋ ๊ฑธ๋ ค์ผ ๋๋ ์กฐ๊ฑด (ํํฐ๋ง ํน์ ์ฒดํฌ ์กฐ๊ฑด)์ด ์๋ค๋ ํ๊ธฐ๋ค. ํด๋น ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ฆฌ๋ทฐ ํ๊ทธ๊ฐ ์๋์ง ํ์ธํ๊ธฐ ์ํด ์ฌ์ฉํ exist()๋ก ์ธํด ๋ฐ์๋๋๋ฏ ํ์ง๋ง ๋ถ๊ฐํผํ๋ค.
- Using Index : ์ปค๋ฒ๋ง ์ธ๋ฑ์ค๊ฐ ์ ์ฉ๋์๋ค๋ ํ๊ธฐ๋ก์จ, ์ฌ์ฉ๋๋ ์ปฌ๋ผ์ด ์์์ ๋ง๊ฒ ๋ชจ๋ ์ธ๋ฑ์ค์ ํฌํจ๋์ด ๋ฐ์ดํฐ์ ์ง์ ์ ๊ทผํ ํ์๊ฐ ์์ด ์ฟผ๋ฆฌ ํจ์จ์ด ์ข๋ค.
- Using filesort : ์ธ๋ฑ์ค๋ก ์ ๋ ฌ๋์ด ์์ง ์์ ๋ ์ฝ๋๋ฅผ ์ ๋ ฌ๋์๋ค๋ ํ๊ธฐ๋ค. ORDER BY created_at๋ก ์ธํ ์ ๋ ฌ์ ๋ปํ๋ค.
2. review์ (created_at) ์ธ๋ฑ์ค ์ ์ฉ
review ํ ์ด๋ธ์ Using filesort๊ฐ ํ๊ธฐ๊ฐ ์์ผ๋ฏ๋ก, ์ ๋ ฌ์ ์ฌ์ฉ๋๋ created_at ๋ํ ์ธ๋ฑ์ค๋ฅผ ์ ์ฉํด๋ณด์.
create index review_created_at_index on reviewmate.review (created_at);
10 rows retrieved starting from 1 in 998 ms (execution: 985 ms, fetching: 13 ms)
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
1 | SIMPLE | review0_ | null | index | PRIMARY | review_created_at_index | 9 | null | 10 | 100 | Backward index scan |
1 | SIMPLE | <subquery2> | null | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 8 | reviewmate.review0_.review_id | 1 | 100 | null |
2 | MATERIALIZED | reviewtags3_ | null | index | review_tag_review_id_property_index | review_tag_review_id_property_index | 775 | null | 2177808 | 10 | Using where; Using index |
๋ง์ฐฌ๊ฐ์ง๋ก ์์ฑํ ์ธ๋ฑ์ค๊ฐ ์ ์์ ์ผ๋ก ํ์ก์ผ๋ฉฐ, 1166 ms -> 998 ms๋ก 15%๊ฐ ๋จ์ถ๋๋ค. ํ์ง๋ง ์ด๋ฒ์๋ ์๋ก์ด Extra๊ฐ ์๊ฒผ๋ค.
3. Descending index ์ ์ฉ
Backward index scan๋ ์ธ๋ฑ์ค ๋ฆฌํ ๋ ธ๋๋ฅผ ์ค๋ฅธ์ชฝ ํ์ด์ง๋ถํฐ ์ผ์ชฝ์ผ๋ก ์ค์บํ๊ณ ์๋ค๋ ๋ป์ด๋ค. ๋ค๋ฅธ ์ธ๋ฑ์ค๋ค์๋ ๋ํ๋์ง ์๋ ์คํ๊ณํ์ธ๋ฐ, ์ ์ฌ๊ธฐ์๋ง ๋ํ๋ ๊น? ๊ต์ฅํ ๊ฐ๋จํ๋ฐ, ๊ทธ ์ ์ ์ด์ ๊ด๋ จ๋ ์ฉ์ด๋ฅผ ์ ๋ฆฌํด๋ณด์
- Ascending index : ์์ ๊ฐ์ ์ธ๋ฑ์ค ํค๊ฐ B-Tree์ ์ผ์ชฝ์ผ๋ก ์ ๋ ฌ๋ ์ธ๋ฑ์ค
- Descening index : zms ๊ฐ์ ์ธ๋ฑ์ค ํค๊ฐ B-Tree์ ์ผ์ชฝ์ผ๋ก ์ ๋ ฌ๋ ์ธ๋ฑ์ค
- Forward index scan : (์ธ๋ฑ์ค ํค์ ํฌ๊ณ ์์์ ๊ด๊ณ์์ด) ์ธ๋ฑ์ค ๋ฆฌํ ๋ ธ๋์ ์ผ์ชฝ ํ์ด์ง๋ถํฐ ์ค๋ฅธ์ชฝ์ผ๋ก ์ค์บ
- Backward index scan : (์ธ๋ฑ์ค ํค์ ํฌ๊ณ ์์์ ๊ด๊ณ์์ด) ์ธ๋ฑ์ค ๋ฆฌํ ๋ ธ๋์ ์ค๋ฅธ์ชฝ ํ์ด์ง๋ถํฐ ์ผ์ชฝ์ผ๋ก ์ค์บ
MySQL 8.0 ์ด์ ์๋ ๋ฌธ๋ฒ๋ง ์กด์ฌํ๊ณ ์ง์ํ์ง ์๋ Descending index์ธ๋ฐ, ์๊ทธ๋๋ ์ธ๋ฑ์ค๊ฐ ๋ง์์๋ก ํธ๋ ์ด๋ ์คํ๊ฐ ํฐ ์ธ๋ฑ์ค์ธ๋ฐ Ascending index๋ฅผ ์ค์ ํด ์ค์บ ๋ฐฉํฅ๋ง ๋ฐ๊พธ๋ฉด ๋์ง ์ ๋ณ๋์ ์ธ๋ฑ์ค๊ฐ ์กด์ฌํ๋๊ณ ์๊ฐํ ์ ์๋ค. ๋ํ InnoDB ์คํ ๋ฆฌ์ง ์์ง์์ Forward & Backward index scan ํ์ด์ง ๊ฐ์๋ Double linked list๋ก ์ด๋ฃจ์ด์ ธ ์์ผ๋ฏ๋ก, ์ค์บ ๋ฐฉํฅ์ ๋ฐ๋ฅธ ์ฑ๋ฅ ์ฐจ์ด๋ ์ดํด๊ฐ ๋์ง ์๋๋ค. ํ์ง๋ง Backward๊ฐ Forward์ ๋นํด์ ๋๋ฆด ์ ๋ฐ์ ์๋ 2๊ฐ์ง ์ด์ ๊ฐ ์๋ค.
3-1. Forward index scan์๊ฒ ์ ํฉํ ํ์ด์ง ์ ๊ธ ๊ตฌ์กฐ
InnoDB์ B-Tree ๋ฆฌํ ํ์ด์ง๋ Double linked list๋ก ์ฐ๊ฒฐ๋์ด ์๊ธฐ ๋๋ฌธ์, ์ฌ์ค ์ด๋ ๋ฐฉํฅ์ด๋ ์ง ์ด๋ ์์ฒด๋ ์ฐจ์ด๊ฐ ์๋ค. ํ์ง๋ง InnoDB ์คํ ๋ฆฌ์ง ์์ง์์๋ ํ์ด์ง ์ ๊ธ ๊ณผ์ ์์ ๋ฐ๋๋ฝ์ ๋ฐฉ์งํ๊ธฐ ์ํด B-Tree์ ์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ ๋ฐฉํฅ(Forward)๋ก๋ง ์ ๊ธ์ ํ๋ํ๋๋ก ํ๊ณ ์๋ค. ๋ฐ๋ผ์ Forward index scan์์๋ ๋ค์ ํ์ด์ง ์ ๊ธ ํ๋์ด ๋งค์ฐ ๊ฐ๋จํ์ง๋ง, Backward index scan์์ ์ด์ ํ์ด์ง ์ ๊ธ์ ํ๋ํ๋ ๊ณผ์ ์ ์๋นํ ๋ณต์กํ ๊ณผ์ ์ ๊ฑฐ์ณ์ผ ํ๋ค
3-2. ๋จ๋ฐฉํฅ์ผ๋ก๋ง ์ฐ๊ฒฐ๋ ํ์ด์ง ๋ด์ ์ธ๋ฑ์ค ๋ ์ฝ๋ ๊ตฌ์กฐ
ํด๋ฌ์คํฐ๋ง ์ธ๋ฑ์ค์ ๊ฒฝ์ฐ B-Tree์ ๋ฆฌํ ๋ ธ๋๊ฐ ๋ ์ฝ๋๊ฐ ์๋๋ฉฐ ์ฌ๋ฌ ๋ ์ฝ๋๊ฐ ํ๋๋ก ๊ตฌ์ฑ๋ ํ์ด์ง(Block)๊ฐ ๋ฆฌํ ๋ ธ๋๋ก ๊ตฌ์ฑ๋์ด ์๋ค. ๋ฐ๋ผ์ InnoDB ์์ง์ด ํน์ ๋ ์ฝ๋๋ฅผ ๊ฒ์ํ ๋, ๊ฒ์ ๋์ ๋ ์ฝ๋(์ธ๋ฑ์ค ๋ ์ฝ๋)๊ฐ ์ ์ฅ๋ ํ์ด์ง๊น์ง๋ B-Tree๋ฅผ ์ด์ฉํด์ ๊ฒ์ํ ์ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก 16K ์ฌ์ด์ฆ์ธ ์ธ๋ฑ์ค ํ์ด์ง๋ผ๋ฉด 600์ฌ๊ฐ ์ด์์ ๋ ์ฝ๋๊ฐ ์ ์ฅ๋ ์ ์๋ค. InnoDB๊ฐ 600์ฌ๊ฐ ๋ ์ฝ๋๋ฅผ ํ๋์ฉ ๋ค ์์ฐจ์ ์ผ๋ก ๋น๊ตํ๋ค๋ฉด ๋ ์ฝ๋ ๊ฒ์์ด ์๋นํ ๋๋ฆด ๊ฒ์ด๋ค.
๋ฐ๋ผ์ ํ๋์ ํ์ด์ง ๋ด์์ ์์ฐจ์ ์ผ๋ก ์ ๋ ฌ๋ ๋ ์ฝ๋ 4~8๊ฐ ์ ๋์ฉ ๋ฌถ์ด์ ๋ํ ํค(๊ฐ์ฅ ํฐ ์ธ๋ฑ์ค ์ํธ๋ฆฌ ํค ๊ฐ)๋ฅผ ์ ์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋ํ ํค๋ค๋ง ๋ชจ์์ ๋ณ๋์ ๋ฆฌ์คํธ๋ฅผ ๊ด๋ฆฌํ๋๋ฐ, ์ด๋ฅผ ํ์ด์ง ๋๋ ํ ๋ฆฌ(Page directory)๋ผ๊ณ ํ๋ค. InnoDB๋ ํน์ ํค ๊ฐ์ ๊ฒ์ํ ๋ ์ด์งํ์์ผ๋ก ๋ํ ํค๋ฅผ ๊ฒ์ํ๊ณ , ๋ํ ํค๋ฅผ ์ฐพ์ผ๋ฉด ๊ทธ๋๋ถํฐ ์ธ๋ฑ์ค ํค ๊ฐ ์์๋๋ก ์ฐ๊ฒฐ๋ Linked list๋ฅผ ์ด์ฉํด ๋์ ๋ ์ฝ๋๋ฅผ ๊ฒ์ํ๋ค.
๋ฌธ์ ๋ Double linked list๋ก ์ฐ๊ฒฐ๋ B-Tree ๋ฆฌํ ํ์ด์ง ๊ตฌ์กฐ์๋ ๋ฌ๋ฆฌ, ํ์ด์ง ๋ด๋ถ์ ๋ ์ฝ๋๋ค์ Single linked list ๊ตฌ์กฐ๋ก ๊ตฌ์ฑ๋์ด ์๋ค. ๋ฐ๋ผ์ Ascending index์์ Forward index scan๊ณผ๋ ๋ฌ๋ฆฌ Backward index scan์ ์จ์ ํ ์๋์ ์ด๋์ ๋ณด์ง ๋ชปํ๋ ๊ฒ์ด๋ค.
๋ํ, ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ ์ ๋ created_at ์ ๋ ฌ์ ์ต์ ์(desc)๋ง ์ ์ฉ๋๋ค.
๋ฐ๋ผ์ created_at ์ธ๋ฑ์ค๋ Backward index๋ก ๊ตฌ์ฑํด์ผ ํ๋ค.
create index review_created_at_index on reviewmate.review (created_at desc);
10 rows retrieved starting from 1 in 901 ms (execution: 885 ms, fetching: 16 ms)
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1 | SIMPLE | review0_ | null | index | PRIMARY | review_created_at_index | 9 | null | 10 | 100 | null |
1 | SIMPLE | <subquery2> | null | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 8 | reviewmate.review0_.review_id | 1 | 100 | null |
2 | MATERIALIZED | reviewtags3_ | null | index | review_tag_review_id_property_index | review_tag_review_id_property_index | 775 | null | 2177808 | 10 | Using where; Using index |
๊ด๋ จํด์ ์กฐ์ฌํ๋ ๊ณผ์ ์์ ์๋ก ๋ฐฐ์ฐ๋ ๋ด์ฉ์ด ๋ง์ ์๋ก ์ด ๊ธธ์์ง๋ง, ๊ฐ๋จํ๊ฒ ์ ์ฉํ ์ ์์๋ค. 998 ms -> 901 ms๋ก 10%๊ฐ ๋จ์ถ๋์๋ค.
๋ง๋ฌด๋ฆฌ
origin | review_tag index | review index | Descending index | ๊ฒฐ๊ณผ |
์ฟผ๋ฆฌ ์ฒ๋ฆฌ ์๊ฐ | 1667 ms | 1166 ms | 998 ms | 901 ms |
์ด์ ๋๋น ๋จ์ถ๋ฅ | - | 30% | 15% | 10% |
์ต์ข ๋จ์ถ๋ฅ | 46% ๋จ์ถ |
Datagrip์ ํตํด ์ธ๋ฑ์ค์ ์ฟผ๋ฆฌ๋ค์ ์กฐ์ํ๊ณ MySQL ์คํ ๊ณํ์ผ๋ก ์ ์ฉ๋๋ ์ธ๋ฑ์ค์ ์ต์ ํ ์ต์ ์ ํ์ธํ๊ณ , ๋ณตํฉ ์ธ๋ฑ์ค, ์ปค๋ฒ๋ง ์ธ๋ฑ์ค, ํด๋ฌ์คํฐ๋ง ์ธ๋ฑ์ค๋ฅผ ์ ์ฉํ์ฌ subquery์ where, order by๊ฐ ํฌํจ๋ ์ฟผ๋ฆฌ์ ์๋๋ฅผ ๊ฐ์ ํ๋ค. ์ธ๋ฑ์ค๋ฅผ ์ฒ์ ๋ฐฐ์ฐ๊ณ ์ ์ฉํด๋ณด์๋๋ฐ, ๋จ์ํ SQL์ ์ ์ง๋ ๊ฒ์ ๋์ด MySQL ๋ด๋ถ ์๋ฆฌ๋ฅผ ์ดํดํ๊ณ ์ธ๋ฑ์ค๋ฅผ ํตํด ์ฟผ๋ฆฌ ์คํ ๋์์ ์ต์ ํํด๋๊ฐ๋ฉด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ ๊น์ด ์ดํดํ๊ณ ๋ฐฐ์ธ ์ ์์๋ค.
์ฐธ๊ณ ์๋ฃ
https://jojoldu.tistory.com/474
https://jojoldu.tistory.com/476
https://jojoldu.tistory.com/158
https://tech.kakao.com/2018/06/19/mysql-ascending-index-vs-descending-index/
https://velog.io/@kbpark9898/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B8%EB%8D%B1%EC%8A%A4-B-tree-1