DB/RDBMS

์กฐํšŒ ์†๋„ ๊ฐœ์„  (์ฟผ๋ฆฌ์ตœ์ ํ™”)

dddzr 2025. 4. 6. 21:30

๐ŸŽฏ ๋ฌธ์ œ

์ชฝ์ง€ํ•จ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ํ›„ ์ชฝ์ง€ ๋ชฉ๋ก์„ ์กฐํšŒ ํ•  ๋•Œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ ์„ ๋• ๋ฌธ์ œ๋ฅผ ๋А๋ผ์ง€ ๋ชป ํ–ˆ๋Š”๋ฐ, 10๋งŒ๊ฑด ์ด์ƒ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ํ›„ ํ…Œ์ŠคํŠธ ํ–ˆ์„ ๋•Œ ์กฐํšŒ ์†๋„๊ฐ€ ๋А๋ ค์กŒ๋‹ค. ์กฐํšŒ ์†๋„๋ฅผ ๊ฐœ์„ ํ•ด๋ณด์ž!! ๐Ÿ˜Š

์ „์ฒด ์กฐํšŒ ๊ฐœ์„ , ๊ฒ€์ƒ‰ ๊ฐœ์„  -> ํŽ˜์ด์ง• ๊ฐœ์„  ์œผ๋กœ ์ง„ํ–‰ํ•จ.

๊ธ‰ํ•˜๋ฉด ๊ฒฐ๋ก ๋งŒ ๋ณด๊ธฐ!!

๐Ÿ“Œ ํ™”๋ฉด (๋ณด๋‚ธ ์ชฝ์ง€ ๋ชฉ๋ก)

โœ” ํŽ˜์ด์ง€ ์ง„์ž… ์‹œ ์ตœ์‹ ์ˆœ์œผ๋กœ ์กฐํšŒ.

โœ” ๊ฒ€์ƒ‰(ํ•„ํ„ฐ)์กฐ๊ฑด์— ๋”ฐ๋ผ ์กฐํšŒ.

โœ” ํŽ˜์ด์ง€ ์ด๋™ ์‹œ ํ•œ ํŽ˜์ด์ง€ ๊ฐœ์ˆ˜ ์„ค์ •๋งŒํผ ๊ฐ€์ ธ์˜ด.

 

๐Ÿ“Œ ์†๋„ ์ธก์ • ๋ฐฉ๋ฒ•

โœ… ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ๋„คํŠธ์›Œํฌ ์‘๋‹ต์„ ์ธก์ •

์กฐํšŒ ๊ฐœ์ˆ˜, ๊ฒ€์ƒ‰ ์กฐ๊ฑด์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์กฐํšŒ ๊ฐœ์ˆ˜ 10๊ฐœ ๊ณ ์ •ํ•˜๊ณ  ์ „์ฒด ์กฐํšŒ์™€ ์ˆ˜์‹ ์ž(๋ฐ›์€ ์‚ฌ๋žŒ) ๊ฒ€์ƒ‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ–ˆ๋‹ค.

  • ๋‚ด๊ฐ€ ๋ณด๋‚ธ ์ชฝ์ง€ 10๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ x
  • ๋‚ด๊ฐ€ ๋ณด๋‚ธ ์ชฝ์ง€ 20๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ 10๋งŒ๊ฑด

 

โœ” ์ฐธ๊ณ 

  • ๋‹ค๋ฅธ ์ชฝ์ง€: ๋‹ค๋ฅธ ์œ ์ €๊ฐ€ ๋ณด๋‚ธ ์ชฝ์ง€๋กœ ์กฐํšŒํ™”๋ฉด์— ๋‚˜์˜ค์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ (๋ฐœ์‹ ์ž ID๋กœ ์กฐํšŒ)
  • ์œ„ ์กฐ๊ฑด์—์„œ ๊ฐœ์ˆ˜๋Š” ์ •ํ™•ํžˆ 10๋งŒ๊ฑด x, 100๊ฐœ ๋ฏธ๋งŒ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋” ์žˆ์—ˆ์ง€๋งŒ ๋ฌด์‹œํ–ˆ๋‹ค!! (ํ…Œ์ŠคํŠธ๋ฅผ ํ˜ผ์ž๋งŒ ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์ด ์•„๋‹ˆ๋ผ์„œ..)
  • ์†๋„๋Š” 2๊ฐ€์ง€ ๊ฒฝ์šฐ์—์„œ ์ธก์ •ํ•˜์ง€ ๋ชป ํ•˜๊ฑฐ๋‚˜ ์บก์ณ๋ฅผ ๋ชป ํ•œ ๊ฒƒ๋„ ์žˆ๋‹ค. ๐Ÿ˜œ

โœ… app์— ๋ฐ˜์˜ ์ „ DBeaver ํˆด์—์„œ ์ฟผ๋ฆฌ ์†๋„ ๋น„๊ต

SET profiling = 1;

SHOW PROFILES;

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํŽ˜์ด์ง€ ๋กœ๋“œ, ๊ฒ€์ƒ‰ ์‹œ

์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜ + ์‹ค์ œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š” ์ฟผ๋ฆฌ 2๊ฐœ๊ฐ€ ์‹คํ–‰๋˜๋Š”๋ฐ

๋””ํ”ผ ํˆด์—์„œ ์ฟผ๋ฆฌ 2๊ฐœ ๊ฐ๊ฐ ์‹คํ–‰์‹œ๊ฐ„ ์ธก์ •ํ•ด์„œ ๋”ํ•œ ๊ฒƒ = ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์‘๋‹ต ์‹œ๊ฐ„ ์–ผ์ถ” ์ผ์น˜ํ–ˆ๋‹ค.

  • ์ธก์ • ์˜ˆ์‹œ (๋ฐ์ดํ„ฐ: ๋ณด๋‚ธ ์ชฝ์ง€ 20๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ 10๋งŒ๊ฑด)

โœ… 1. Exists ์‚ฌ์šฉ

โœ… 1-1. ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰ ๊ฐœ์ˆ˜ -  3.3s

SELECT COUNT(DISTINCT n.note_id)

	FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

    WHERE n.regt_id = 'user1'

    AND EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE '%์šฉ%'

	                )

 

โœ… 1.2 ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰ - 7.13s

 

SELECT

    	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

    	, ntm.trans_id, ntm.trans_type, ntm.read_yn, COUNT(nam.attach_id) as attach_cnt

    	, GROUP_CONCAT(DISTINCT u2.user_nm_kr) AS recv_nms

    FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

    LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = 'user1'

    AND EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE '%์šฉ%'

	                )

    GROUP BY n.note_id

    ORDER BY n.note_id desc

 

โœ… 2. Exists ๋ฏธ์‚ฌ์šฉ

โœ… 2-1. ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰  - 5.1s

SELECT COUNT(DISTINCT n.note_id)

	FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = 'user1'	

    AND u2.user_nm_kr LIKE '%์šฉ%'

 

โœ… 2.2 ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰ - 9.2s

SELECT

    	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

    	, ntm.trans_id, ntm.trans_type, ntm.read_yn, COUNT(nam.attach_id) as attach_cnt

    	, GROUP_CONCAT(DISTINCT u2.user_nm_kr) AS recv_nms

    FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

    LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = 'user1'

    AND u2.user_nm_kr LIKE '%์šฉ%'

    GROUP BY n.note_id

    ORDER BY n.note_id desc

๐Ÿ“Œ ์กฐํšŒ ์†๋„ ๊ฐœ์„  ๊ณผ์ •

โœ…1. ์ดˆ๊ธฐ ์ฟผ๋ฆฌ (GROUP_CONCAT, LEFT JOIN, ์„œ๋ธŒ์ฟผ๋ฆฌ ์‚ฌ์šฉ)

๐Ÿ”น ์กฐํšŒ ์†๋„

  • (๋ณด๋‚ธ ์ชฝ์ง€ 10๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ x) ์ „์ฒด: 17.71s
  • (๋ณด๋‚ธ ์ชฝ์ง€ 20๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ 10๋งŒ๊ฑด) ์ „์ฒด**: 38.83s**

 

๐Ÿ”น ๋ฌธ์ œ์ 

GROUP_CONCAT, LEFT JOIN๊ณผ ์„œ๋ธŒ์ฟผ๋ฆฌ ๋‹ค์ค‘ ์‚ฌ์šฉ์œผ๋กœ ์ธํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ์„ ํ˜•์ ์œผ๋กœ ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฐœ์ƒ

๐Ÿ”น ์ดˆ๊ธฐ ์ฟผ๋ฆฌ

<!-- ๋ณด๋‚ธ ์ชฝ์ง€ ๊ฐœ์ˆ˜ -->

<select id="selectNoteByRegtIdTotalCnt">

	SELECT COUNT(DISTINCT n.note_id)

	FROM notes n

 LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id

	LEFT JOIN (

	 SELECT

	 n2.note_id,

	 GROUP_CONCAT(ntm2.user_id) AS recv_ids,

	 GROUP_CONCAT(u2.user_nm_kr) AS recv_nms

	 FROM

	 notes n2

	 LEFT JOIN

	 note_trans_mgmt ntm2 ON ntm2.note_id = n2.note_id AND ntm2.trans_type = '2'

		LEFT JOIN users u2

			ON ntm2.user_id = u2.user_id

	 GROUP BY

	 n2.note_id

	) AS recvs ON n.note_id = recvs.note_id

 WHERE n.regt_id = #{regtId} AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

	<if test="keyword != null and keyword != ''">

	 <choose>

	 <when test="searchOption == 'recv'">

	 AND LOWER(recvs.recv_nms) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 </when>

	 <when test="searchOption == 'titl'">

	 AND LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 </when>

	 <when test="searchOption == 'cont'">

	 AND LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 </when>

	 <otherwise>

	 AND (

	 LOWER(recvs.recv_nms) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 OR LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 OR LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 )

	 </otherwise>

	 </choose>

	</if>

</select>

<!-- ๋ณด๋‚ธ ์ชฝ์ง€ -->

<select id="selectNoteListByRegtId">

 SELECT

 	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

 	, tu.user_nm_kr as userNm, ntm.trans_id, ntm.trans_type, ntm.read_yn

	, COUNT(nam.attach_id) as attach_cnt

 	, recvs.recv_ids, recvs.recv_nms

 FROM notes n

 LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id

 LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN users tu ON n.regt_id = tu.user_id

	LEFT JOIN (

	 SELECT

	 n2.note_id,

	 GROUP_CONCAT(ntm2.user_id) AS recv_ids,

	 GROUP_CONCAT(u2.user_nm_kr) AS recv_nms

	 FROM

	 notes n2

	 LEFT JOIN

	 note_trans_mgmt ntm2 ON ntm2.note_id = n2.note_id AND ntm2.trans_type = '2'

		LEFT JOIN users u2

			ON ntm2.user_id = u2.user_id

WHERE n2.regt_id = #{regtId}

	 GROUP BY

	 n2.note_id

	) AS recvs ON n.note_id = recvs.note_id

 WHERE n.regt_id = #{regtId} AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

	<if test="keyword != null and keyword != ''">

	 <choose>

	 <when test="searchOption == 'recv'">

	 AND LOWER(recvs.recv_nms) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 </when>

	 <when test="searchOption == 'titl'">

	 AND LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 </when>

	 <when test="searchOption == 'cont'">

	 AND LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 </when>

	 <otherwise>

	 AND (

	 LOWER(recvs.recv_nms) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 OR LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 OR LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	 )

	 </otherwise>

	 </choose>

	</if>

 GROUP BY n.note_id

 ORDER BY n.note_id DESC

 LIMIT #{pageSize} OFFSET #{startIndex}

</select>

โœ…2. ์„œ๋ธŒ์ฟผ๋ฆฌ ์ œ๊ฑฐ

๐Ÿ”น ๊ฐœ์„ 

  • ์„œ๋ธŒ์ฟผ๋ฆฌ (recvs) ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , tbn_noterecvmgmt ์„ ๋ฉ”์ธ ์ฟผ๋ฆฌ์— ์ง์ ‘ JOIN ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝ. -> ๋ถˆํ•„์š”ํ•œ ์ž„์‹œ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์ œ๊ฑฐ
  • ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง์ด ๋จผ์ € ์‹คํ–‰๋จ (WHERE n.regt_id = ?)
  • JOIN ํ›„ ๋ฐ”๋กœ GROUP_CONCAT ์ ์šฉ๋ถˆํ•„์š”ํ•œ ์ค‘๋ณต ์—ฐ์‚ฐ ๊ฐ์†Œ

๐Ÿ”น ์ˆ˜์ •๋œ ์ฟผ๋ฆฌ

LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

//…WHERE

AND LOWER(u2.user_nm_kr) LIKE CONCAT('%', LOWER(#{keyword}), '%')

๐Ÿ”น ์กฐํšŒ ์†๋„

  • (๋ณด๋‚ธ ์ชฝ์ง€ 10๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ x) ์ „์ฒด: 3.06s, ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰: 15.93s
  • (๋ณด๋‚ธ ์ชฝ์ง€ 20๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ 10๋งŒ๊ฑด) ์ „์ฒด: 9.76s, ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰: 25.93s

 

 

์œ„๋ถ€ํ„ฐ ์ „์ฒด ์กฐํšŒ, ์ „์ฒด ๊ฒ€์ƒ‰, ์ œ๋ชฉ ๊ฒ€์ƒ‰, ๋‚ด์šฉ ๊ฒ€์ƒ‰, ๋ฐ›๋Š” ์‚ฌ๋žŒ ๊ฒ€์ƒ‰

๐Ÿ”น ๋ฌธ์ œ์ 

  • ์„œ๋ธŒ์ฟผ๋ฆฌ๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด์„œ ์กฐํšŒ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋จ
  • ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ฐ›๋Š” ์‚ฌ๋žŒ(recv) ๊ฒ€์ƒ‰์€ LIKE๋กœ ์ธํ•ด Full Table Scan ๋ฐœ์ƒ ๊ฐ€๋Šฅ, ๋ฐ์ดํ„ฐ ๋Š˜์–ด๋‚˜๋ฉด ์กฐํšŒ ์†๋„ ๋ฐฐ๋กœ ์ฆ๊ฐ€

โญ ์ „์ฒด ๊ฒ€์ƒ‰์ด ๋ฐ›๋Š” ์‚ฌ๋žŒ ๊ฒ€์ƒ‰๋ณด๋‹ค ๋น ๋ฅด๋‹ค?

โžก๏ธ OR ์กฐ๊ฑด ๋•Œ๋ฌธ!!

  • ์ œ๋ชฉ(title)์ด๋‚˜ ๋‚ด์šฉ(content)์—์„œ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ฐพ์œผ๋ฉด, ๋’ค์— ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ   TRUE๊ฐ€ ๋˜์–ด ๊ฒฐ๊ณผ์— ํฌํ•จ๋จ
  • ์˜ตํ‹ฐ๋งˆ์ด์ €๊ฐ€ ์ž๋™์œผ๋กœ ์ˆœ์„œ ์ตœ์ ํ™” ํ•ด์ค€๋‹ค๊ณ  ํ•˜๋Š”๋ฐ, ํ˜น์‹œ ๋ชฐ๋ผ์„œ ์˜ค๋ž˜๊ฑธ๋ฆฌ๋Š” u2.user_nm_kr์กฐ๊ฑด์„ ๋’ค์ชฝ์— ๋ฐฐ์น˜ํ–ˆ๋‹ค.

โœ… 3. EXISTS ์‚ฌ์šฉ (u2 ์กฐ์ธ์ œ๊ฑฐ)

๐Ÿ”น๊ฐœ์„ 

  • ๊ธฐ์กด JOIN์—์„œ LIKE๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด JOIN ์ดํ›„ ์ „์ฒด ๋ฐ์ดํ„ฐ์—์„œ ๋ฌธ์ž์—ด ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋น„ํšจ์œจ์ 
  • EXISTS๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š”ํ•œ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋งŒ ํ™•์ธํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์กฐํšŒ ๋ฐฉ์ง€
  • JOIN์„ ์‚ฌ์šฉํ•˜๋ฉด ์ „์ฒด u2.user_nm_kr ํ…Œ์ด๋ธ”์„ ์Šค์บ”ํ•œ ํ›„ LIKE๋ฅผ ์ ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ, EXISTS๋Š” ๋งค์นญ๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์Šค์บ”์„ ์ค„์ผ ์ˆ˜ ์žˆ์Œ.

๐Ÿ”น ์ˆ˜์ •๋œ ์ฟผ๋ฆฌ

AND EXISTS (

SELECT 1 FROM users u_sub

WHERE u_sub.user_id = ntm2.user_id

AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

)

๐Ÿ”น ์กฐํšŒ ์†๋„

(๋ณด๋‚ธ ์ชฝ์ง€ 20๋งŒ๊ฑด + ๋‹ค๋ฅธ ์ชฝ์ง€ 10๋งŒ๊ฑด) ์ „์ฒด: 3.03s, ์ˆ˜์‹ ์ž ๊ฒ€์ƒ‰: 13.47s

 

โœ…4. ์ตœ์ข…: Where์กฐ๊ฑด ์œ„์น˜ ์ˆ˜์ •, lowerํ•จ์ˆ˜ ์ œ๊ฑฐ, ๋ถˆํ•„์š” ์ปฌ๋Ÿผ ์ œ๊ฑฐ(userNm)

๐Ÿ”น ๊ฐœ์„ 

  • WHERE ์กฐ๊ฑด์„ Join-on์œผ๋กœ ์˜ฎ๊น€ → ํ•„ํ„ฐ๋ง์„ ๋จผ์ € ์ˆ˜ํ–‰ํ•˜๊ณ , ์ดํ›„ ์—ฐ์‚ฐ์„ ์ง„ํ–‰
  • LOWER() ํ•จ์ˆ˜ ์ œ๊ฑฐ → ๋ถˆํ•„์š” ์—ฐ์‚ฐ ์ œ๊ฑฐ(ํ•œ๊ตญ์–ด๋งŒ ๋“ค์–ด๊ฐ€๋Š” ์ปฌ๋Ÿผ์ด๋‹ค!!)
  • ๋ถˆํ•„์š”ํ•œ ์ปฌ๋Ÿผ(userNm - ๋ณด๋‚ธ์‚ฌ๋žŒ์ด๋ฆ„) ์ œ๊ฑฐ → ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ๋Ÿ‰ ๊ฐ์†Œ

๐Ÿ”น์ˆ˜์ •๋œ ์ฟผ๋ฆฌ

LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

๐Ÿ”น ์กฐํšŒ ์†๋„

(20๋งŒ๊ฑด+๋‹ค๋ฅธ ์œ ์ €10๋งŒ๊ฑด ๊ธฐ์ค€) ์ „์ฒด ์กฐํšŒ: 3.17s, ๋ณด๋‚ธ ์‚ฌ๋žŒ ๊ฒ€์ƒ‰: 11.09s

 

โญ ์ฐธ๊ณ : ๋ฏธ์ ์šฉ๋œ ๋ฐฉ๋ฒ•

โœ… ์ธ๋ฑ์Šค ์ถ”๊ฐ€

์ธ๋ฑ์Šค๋Š” user_nm_kr์— ์ถ”๊ฐ€ํ•˜๊ณ  %text% -> text%๋กœ ๋ฐ”๊ฟ” ๋ณด์•˜์ง€๋งŒ ์‚ฌ์šฉ๋˜์ง€x, force index ์‚ฌ์šฉ์‹œ ์˜คํžˆ๋ ค ์„ฑ๋Šฅ ๊ฐ์†Œ. → ์ธ๋ฑ์Šค๋ฅผ ๊ฐ•์ œํ•  ๊ฒฝ์šฐ ๋ฒ”์œ„ ๊ฒ€์ƒ‰(range scan)์ด ๋ฐœ์ƒํ•˜๋ฉด์„œ ๋ถˆํ•„์š”ํ•œ ๊ฒ€์ƒ‰์ด ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ

like ๊ฒ€์ƒ‰ ํŽธ์˜์„ฑ์„ ์œ„ํ•ด ์ œ์™ธํ•จ.

๊ธฐ์กด

1	SIMPLE	u2	ref	PRIMARY,idx_user_name	PRIMARY	162	skku.ntm2.user_id	1	Using where

LEFT JOIN users u2 FORCE INDEX (idx_user_name)ON ntm2.user_id = u2.user_id ์ ์šฉ ์‹œ

1	SIMPLE	u2	range	idx_user_name	idx_user_name	323		1463	Using where; Using index; Using join buffer (flat, BNL join)

โœ… VARCHAR → CHAR

ํ•œ๊ตญ ์‚ฌ๋žŒ ์ด๋ฆ„ ๊ธ€์ž ์ˆ˜ ์ œํ•œ ์ •์ฑ… ์—†์–ด์„œ ๋ฐ˜์˜ํ•˜์ง€ ์•Š์Œ.

ํŠน์ง• CHAR(n) VARCHAR(n)

๊ธธ์ด ๊ณ ์ • ๊ธธ์ด (n ๋ฐ”์ดํŠธ) ๊ฐ€๋ณ€ ๊ธธ์ด (๋ฐ์ดํ„ฐ ๊ธธ์ด + 1~2๋ฐ”์ดํŠธ)
๊ณต๋ฐฑ ์ฒ˜๋ฆฌ ์งง์œผ๋ฉด ๊ณต๋ฐฑ( )์œผ๋กœ ํŒจ๋”ฉ๋จ ์ €์žฅ ์‹œ ๊ณต๋ฐฑ ์ œ๊ฑฐ๋จ
์†๋„ ๊ณ ์ • ๊ธธ์ด๋ผ ์ •๋ ฌ ๋ฐ ๊ฒ€์ƒ‰์ด ๋น ๋ฅผ ์ˆ˜ ์žˆ์Œ ๊ธธ์ด ํ™•์ธ ๊ณผ์ •์ด ์žˆ์–ด ์•ฝ๊ฐ„์˜ ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
๊ณต๊ฐ„ ํšจ์œจ์„ฑ ๊ธธ์ด๊ฐ€ ์งง์„์ˆ˜๋ก ๊ณต๊ฐ„ ๋‚ญ๋น„ ๋ฐœ์ƒ ๊ฐ€๋Šฅ ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์ €์žฅํ•˜์—ฌ ๊ณต๊ฐ„ ํšจ์œจ์ 
์ ํ•ฉํ•œ ๊ฒฝ์šฐ ๊ธธ์ด๊ฐ€ ์ผ์ •ํ•œ ๋ฐ์ดํ„ฐ (์˜ˆ: ๊ตญ๊ฐ€ ์ฝ”๋“œ, ์„ฑ๋ณ„ ๋“ฑ) ๊ธธ์ด๊ฐ€ ๋‹ค์–‘ํ•œ ๋ฌธ์ž์—ด ์ €์žฅ ์‹œ ์œ ๋ฆฌ

โœ… FULLTEXT,GRAM TABLE

FULLTEXT,GRAM TABLE ๋“ฑ์€ ํ•œ ๊ธ€์ž, %text% ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•ด์„œ ๋ฐ˜์˜x.

โœ… @Transactional(readOnly = true)

์†๋„ ์ธก์ •์—์„œ ์ฐจ์ดx.

// ๋ณด๋‚ธ ์ชฝ์ง€ ๋ชฉ๋ก ์ „์ฒด ์นด์šดํŠธ

@Transactional(readOnly = true)

public int getSendNotesTotalCnt(Criteria cri) {

return noteMapper.selectNoteByRegtIdTotalCnt(cri);

}

// ๋ณด๋‚ธ ์ชฝ์ง€ ๋ชฉ๋ก ๋ฆฌ์ŠคํŠธ (ํŽ˜์ด์ง•)

@Transactional(readOnly = true)

public List<NoteResponseDTO> getSendNotes(Criteria cri) {

return noteMapper.selectNoteListByRegtId(cri);

}

โœ… ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

์‹ค์‹œ๊ฐ„ ์ตœ์‹ ์„ฑ์ด ์ค‘์š”ํ•œ ์„œ๋น„์Šค ํŠน์„ฑ์„ ๊ณ ๋ คํ•ด ์ œ์™ธ.

๐Ÿš€ ๊ฒฐ๋ก 

๐Ÿ”น์ตœ์ข… ์ฟผ๋ฆฌ

โœ” ์„œ๋ธŒ์ฟผ๋ฆฌ ์ œ๊ฑฐ → JOIN์„ ์ด์šฉ

โœ” EXISTS ์‚ฌ์šฉ → ๋ฌด์กฐ๊ฑด ๋” ๋น ๋ฅด์ง€ x, ์ฟผ๋ฆฌ ์†๋„๋ฅผ ๋น„๊ตํ•ด๋ณด๊ณ  ์ ์šฉํ•  ๊ฒƒ!!

โœ” ๋ถˆํ•„์š” ํ•จ์ˆ˜, ์ปฌ๋Ÿผ ์ œ๊ฑฐ

<!-- ๋ณด๋‚ธ ์ชฝ์ง€ ๊ฐœ์ˆ˜ -->

<select id="selectNoteByRegtIdTotalCnt">

	SELECT COUNT(DISTINCT n.note_id)

	FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

    WHERE n.regt_id = #{regtId}

	<if test="keyword != null and keyword != ''">

	    <choose>

	        <when test="searchOption == 'recv'">

	            AND EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

	                )

	        </when>

	        <when test="searchOption == 'titl'">

	            AND LOWER(n.title) LIKE CONCAT('%', #{keyword}, '%')

	        </when>

	        <when test="searchOption == 'cont'">

	            AND LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	        </when>

	        <otherwise>

	            AND (		

	                LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	                OR LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	                OR EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

	                )

	            )

	        </otherwise>

	    </choose>

	</if>

</select>

<!-- ๋ณด๋‚ธ ์ชฝ์ง€ -->

<select id="selectNoteListByRegtId">

    SELECT

    	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

    	, ntm.trans_id, ntm.trans_type, ntm.read_yn, COUNT(nam.attach_id) as attach_cnt

    	, GROUP_CONCAT(DISTINCT u2.user_nm_kr) AS recv_nms

    FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

    LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = #{regtId}

	<if test="keyword != null and keyword != ''">

	    <choose>

	        <when test="searchOption == 'recv'">

	            AND EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

	                )

	        </when>

	        <when test="searchOption == 'titl'">

	            AND LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	        </when>

	        <when test="searchOption == 'cont'">

	            AND LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	        </when>

	        <otherwise>

	            AND (		

	                LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	                OR LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	                OR EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

	                )

	            )

	        </otherwise>

	    </choose>

	</if>

    GROUP BY n.note_id

    ORDER BY n.note_id DESC

    LIMIT #{pageSize} OFFSET #{startIndex}

</select>

๐Ÿ“Œ ํŽ˜์ด์ง• ์†๋„ ๊ฐœ์„ 

๐Ÿ”น ๋ฌธ์ œ์ 

์ฒซ ํŽ˜์ด์ง€ ์กฐํšŒ ์†๋„๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ์ง€๋งŒ, ๋’ท ํŽ˜์ด์ง€๋กœ ์ด๋™ ์‹œ ์˜ค๋ž˜ ๊ฑธ๋ฆผ

โœ… LIMIT๊ณผ OFFSET ์ž‘๋™ ๋ฐฉ์‹

LIMIT #{pageSize} OFFSET #{startIndex}

โœ” LIMIT : ๊ฐ€์ ธ์˜ฌ ํ–‰(row)์˜ ๊ฐœ์ˆ˜๋ฅผ ์ง€์ •

โœ” OFFSET : ๊ฑด๋„ˆ๋›ธ ํ–‰(row)์˜ ๊ฐœ์ˆ˜๋ฅผ ์ง€์ •

๐Ÿ”ฅ ์™œ ๋А๋ ค์งˆ๊นŒ?

1๏ธโƒฃ DB๋Š” OFFSET๋งŒํผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ์ฝ๊ณ  ๋ฒ„๋ ค์•ผ ํ•จ

2๏ธโƒฃ OFFSET์ด ํด์ˆ˜๋ก ๋ฒ„๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์ง€๊ณ , ๋””์Šคํฌ I/O ์ฆ๊ฐ€

์ฆ‰, OFFSET์€ ์•ž์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ์ฝ๊ณ  ๊ฑด๋„ˆ๋›ด ํ›„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋œ๋‹ค. ๐Ÿ˜ข

๐Ÿ”น ๊ฐœ์„ 

  • OFFSET์„ ์ œ๊ฑฐ

โœ… ID ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง•

SELECT * FROM ํ…Œ์ด๋ธ”

WHERE note_id < #{๋งˆ์ง€๋ง‰_note_id}

ORDER BY note_id DESC

LIMIT 10;

โœ” ์ด์ „ ํŽ˜์ด์ง€์˜ ๋งˆ์ง€๋ง‰ ID(note_id)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์†๋„๊ฐ€ ๋น ๋ฆ„!

โœ” INDEX๋ฅผ ํ™œ์šฉํ•˜๋ฏ€๋กœ OFFSET๋ณด๋‹ค ํ›จ์”ฌ ํšจ์œจ์ !

 

โญ ์ฐธ๊ณ : ๋ฏธ์ ์šฉ ๋ฐฉ๋ฒ•

โœ… ์„œ๋ธŒ์ฟผ๋ฆฌ๋กœ OFFSET ์ตœ์ ํ™”

SELECT * FROM ํ…Œ์ด๋ธ”

WHERE note_id IN (

SELECT note_id FROM ํ…Œ์ด๋ธ” ORDER BY note_id DESC LIMIT 10 OFFSET 1000

);

โœ” OFFSET์ด ํฐ ๊ฒฝ์šฐ, ๋จผ์ € ID๋งŒ ์ฐพ๊ณ  ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Œ

โœ… ํŽ˜์ด์ง•์„ ์บ์‹ฑํ•˜๊ธฐ

โœ”  ๊ฐ™์€ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐ˜๋ณต๋˜๋ฉด Redis ๊ฐ™์€ ์บ์‹œ ํ™œ์šฉ

๐Ÿš€ ๊ฒฐ๋ก 

โœ” OFFSET๋Œ€์‹  ๋งˆ์ง€๋ง‰ ์กฐํšŒ ๋ฐ์ดํ„ฐ ID๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์กฐํšŒ

โœ” ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์š”์†Œ์— ๋ฐ์ดํ„ฐ ID๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ณด๋‚ด์ฃผ๋Š” ์ฒ˜๋ฆฌ ํ•„์š”ํ•จ!!

๐Ÿ”น ์กฐํšŒ ์†๋„

์ฒซ ํŽ˜์ด์ง€ ์กฐํšŒ ์†๋„, ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€ ์กฐํšŒ ์†๋„

 

๐Ÿ”น์ตœ์ข… ์ฟผ๋ฆฌ

<select id="selectNoteListByRegtId">

    SELECT

    	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

    	, ntm.trans_id, ntm.trans_type, ntm.read_yn, COUNT(nam.attach_id) as attach_cnt

    	, GROUP_CONCAT(DISTINCT u2.user_nm_kr) AS recv_nms

    FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

    LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = #{regtId}

    <if test="lastSno != 0">

    	AND n.note_id <![CDATA[<]]> #{lastSno}

    </if>	

	<if test="keyword != null and keyword != ''">

	    <choose>

	        <when test="searchOption == 'recv'">

	            AND EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

	                )

	        </when>

	        <when test="searchOption == 'titl'">

	            AND LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	        </when>

	        <when test="searchOption == 'cont'">

	            AND LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	        </when>

	        <otherwise>

	            AND (		

	                LOWER(n.title) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	                OR LOWER(n.content) LIKE CONCAT('%', LOWER(#{keyword}), '%')

	                OR EXISTS (

	                    SELECT 1 FROM users u_sub

	                    WHERE u_sub.user_id = ntm2.user_id

	                    AND u_sub.user_nm_kr LIKE CONCAT('%', #{keyword}, '%')

	                )

	            )

	        </otherwise>

	    </choose>

	</if>

    GROUP BY n.note_id

    ORDER BY n.note_id DESC

    LIMIT #{pageSize}

</select>

  • ์†๋„ ๋น„๊ต
# offset ์ด์šฉ - 5.694s
SELECT

    	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

    	, ntm.trans_id, ntm.trans_type, ntm.read_yn, COUNT(nam.attach_id) as attach_cnt

    	, GROUP_CONCAT(DISTINCT u2.user_nm_kr) AS recv_nms

    FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

    LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = 'user1'

    GROUP BY n.note_id

    ORDER BY n.note_id desc

    limit 10 offset 100000

# ID๊ธฐ๋ฐ˜ ์กฐํšŒ - 0.479s

SELECT

    	n.note_id, n.title, n.content, n.regt_id, n.regt_dt

    	, ntm.trans_id, ntm.trans_type, ntm.read_yn, COUNT(nam.attach_id) as attach_cnt

    	, GROUP_CONCAT(DISTINCT u2.user_nm_kr) AS recv_nms

    FROM notes n

    LEFT JOIN note_trans_mgmt ntm ON n.note_id = ntm.note_id AND ntm.trans_type = "1" AND ntm.use_yn = "Y"

    LEFT JOIN note_attach_mgmt nam ON n.note_id = nam.attach_id

	LEFT JOIN note_trans_mgmt ntm2 ON n.note_id = ntm2.note_id AND ntm2.trans_type = '2'

	LEFT JOIN users u2 ON ntm2.user_id = u2.user_id

    WHERE n.regt_id = 'user1' and n.note_id < '291276'

    GROUP BY n.note_id

    ORDER BY n.note_id desc

    limit 10