๐Ÿ‘จ‍๐Ÿ‘ฉ‍๐Ÿ‘ง‍๐Ÿ‘ฆ Project/๐Ÿ“บ KIOSEK

์˜ˆ์•ฝ๋‚ด์—ญ ํŽ˜์ด์ง• ์กฐํšŒ ๊ธฐ๋Šฅ ์„ฑ๋Šฅ ์ตœ์ ํ™” (totalCount in Redis)

DevPoong 2023. 8. 22. 00:02

1. ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ณ„๊ธฐ

ํ˜„์žฌ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์—์„œ ์˜ˆ์•ฝ ๋‚ด์—ญ์„ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌํ•˜์—ฌ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.
limit๋Š” 20์œผ๋กœ ํ•œ ํŽ˜์ด์ง€๋‹น 20๊ฐœ์˜ ์˜ˆ์•ฝ ์ปจํ…์ธ ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค.

์ „์ฒด ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋Š” 354,200๊ฐœ์ธ ์ƒํ™ฉ์ด๊ณ  ํ•ด๋‹น API์˜ ์„ฑ๋Šฅ์ด ํ‰๊ท ์ ์œผ๋กœ 1.74์ดˆ์˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ๊ณ  ์ตœ๋Œ€ 1.99์ดˆ๊นŒ์ง€ ๊ฑธ๋ฆผ์œผ๋กœ์จ ์„ฑ๋Šฅ์ด ์ข‹์ง€ ๋ชปํ–ˆ๋‹ค. ํ•ด๋‹น ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ๋ชจ์ƒ‰ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. 

 

๋ฏธ๋ฆฌ ๋งํ•˜์ž๋ฉด, ํ‰๊ท  56.2%์˜ ์„ฑ๋Šฅํ–ฅ์ƒ์„ ๋ง›๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค!

2. ๋ฐ˜๋ณต๋˜๋Š” Count Query๋ฅผ ๊ฐœ์„ ํ•ด๋ณด์ž!

ํ•ด๋‹น ๋ถ€๋ถ„์—์„œ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์œผ๋กœ ๊ฐ€์žฅ ๋ˆˆ์— ๋ˆ๊ฒƒ์€ ๋งค๋ฒˆ ๋ฐ˜๋ณต๋˜๋Š” ์ „์ฒด ๋ฐ์ดํ„ฐ์˜ ์ˆ˜๋ฅผ ์ธก์ •ํ•˜๋Š” Count Query์˜€๋‹ค.
"๊ตณ์ด Count Query๊ฐ€ ํŽ˜์ด์ง€๋งˆ๋‹ค ๋งค๋ฒˆ ๋ฐ˜๋ณต๋  ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ?" ์ƒ๊ฐํ–ˆ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ์ •๋ฆฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

  • ํ•œ๋ฒˆ ์กฐํšŒ ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ totalCount๋ฅผ Redis ์บ์‹œ์— ์ €์žฅํ•ด๋‘์—ˆ๋‹ค๊ฐ€, ์˜ˆ์•ฝ์ด ์ƒˆ๋กœ ๋“ค์–ด์˜ค๊ธฐ์ „์—๋Š” ์บ์‹œ์—์„œ totalCount๋ฅผ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.
  • ๋งŒ์•ฝ, ์ƒˆ๋กœ์šด ์˜ˆ์•ฝ์ด ๋ฐœ์ƒํ•˜๋ฉด ์บ์‹œ์— ์žˆ๋Š” totalCount์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜์—ฌ ์ •ํ•ฉ์„ฑ์ด ๋งž์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์šฐ์„  ์ฝ”๋“œ๋ฅผ ๋ณด์ž!

public Page<SearchReservationByPagingRes> findAllByConditionAndPageable(ReservationSearchCondition condition, Pageable pageable) {
        List<SearchReservationByPagingRes> content = queryFactory
                .select(xxx)
                .from(reservation)
		... join
                ... where
                .orderBy(reservation.startAt.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        JPAQuery<Long> countQuery = queryFactory
                .select(reservation.count())
                .from(reservation)
              	... join
                ... where 

        return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
    }

๊ธฐ์กด์—๋Š” findAllByConditionAndPageable() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฌด์กฐ๊ฑด ๊ฒ€์ƒ‰์กฐ๊ฑด์— ๋งž๋Š” ๐Ÿ‘Ž๐Ÿปcontents๋ฅผ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ์™€ ์ „์ฒด ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜๋ฅผ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์„ธํŠธ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

 

3. ํ•ด๊ฒฐ ๊ณผ์ •

(1) ์šฐ์„ , ์•„๋ž˜์™€ ๊ฐ™์ด countQuery๋ฅผ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜์˜€๋‹ค.

 

(2) ๋‹ค์Œ์œผ๋กœ Look-Aside ์ „๋žต์„ ์ด์šฉํ•˜์—ฌ ์˜ˆ์•ฝ ๋‚ด์—ญ ํŽ˜์ด์ง• ์กฐํšŒ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด Redis ์บ์‹œ์— totalCount ๊ฐ’์ด ์žˆ์œผ๋ฉด ์บ์‹œ์—์„œ ์ฝ์–ด์˜ค๊ณ , ๋งŒ์•ฝ totalCount๊ฐ€ ์—†์–ด์„œ Cache Miss๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์œ„์˜ countQuery๋ฅผ ์‹คํ–‰ํ•˜์—ฌ RDB์—์„œ ๊ฐ’์„ ์ฝ์–ด์™€์„œ ์บ์‹œ์—๋„ ์ €์žฅํ•ด์ฃผ๋Š” ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค.
(์—ฌ๊ธฐ์„œ๋Š” ๋™์  ๊ฒ€์ƒ‰ ์กฐ๊ฑด์ด ํฌํ•จ๋œ ๊ธฐ๋Šฅ์ด์–ด์„œ ํ‚ค ๊ฐ’์ด ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค!)

 

(3) ์ด์ œ Repository์˜ ํŽ˜์ด์ง• ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ totalCount๋ฅผ ๋„˜๊ฒจ์ฃผ์–ด์„œ Page ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด์ธ PageImpl์„ ์ƒ์„ฑํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.

public Page<SearchReservationByPagingRes> findAllByConditionAndPageable(ReservationSearchCondition condition, Pageable pageable, Long count) {
	.... ์ปจํ…์ธ  ์กฐํšŒ ์ฟผ๋ฆฌ
		
        return new PageImpl<>(content, pageable, count);
    }

 

(4) ์˜ˆ์•ฝ์ด ๋ฐœ์ƒํ•˜๋ฉด ์บ์‹œ์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•ด์ฃผ๋Š” ์ž‘์—…๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋์ด๋‹ค.

 

4. ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ด๋ณด์ž

์šฐ์„  ํ•œ๋ฒˆ ์‹คํ–‰ํ•ด๋ดค๋Š”๋ฐ ์œ„์™€ ๊ฐ™์ด ์‹คํ–‰ ์‹œ๊ฐ„์ด 1173ms๋กœ ๋Œ€ํญ ๊ฐ์†Œํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํ•œ๋ฒˆ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๋‹ค. ์—ฌ๋Ÿฌ๋ฒˆ ๋” ์ธก์ •ํ•ด๋ณด๊ณ  ๋น„๊ตํ•ด๋ณด์•˜๋‹ค.

์ „์ฒด ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋Š” 354,200๊ฐœ, limit๋Š” 20์ด๋‹ค.

๋นจ๊ฐ„์„ ์€ totalCount๋ฅผ ์บ์‹ฑํ•˜์ง€ ์•Š์•˜์„ ๋•Œ, ํŒŒ๋ž€์„ ์€ totalCount๋ฅผ ์บ์‹ฑํ•œ ๊ฒฝ์šฐ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

๊ฐ 10ํšŒ์”ฉ ๋ฐ˜๋ณตํ•ด๋ณด์•˜์„๋•Œ,

  • ํ‰๊ท  ๋น„๊ต : 56.2% ์„ฑ๋Šฅํ–ฅ์ƒ
  • Redis O์˜ ์ตœ์†Ÿ๊ฐ’๊ณผ Redis X์˜ ์ตœ๋Œ“๊ฐ’ ๋น„๊ต : 105% ์„ฑ๋Šฅํ–ฅ์ƒ
  • ์„œ๋กœ ์ตœ๋Œ€๊ฐ’๋ผ๋ฆฌ ๋น„๊ต : 64.5% ์„ฑ๋Šฅํ–ฅ์ƒ

์ด๋ ‡๊ฒŒ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋œ ๊ฒƒ์„ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

5. ๋™์  ์ฟผ๋ฆฌ๋กœ ํ•„ํ„ฐ๋ง ์กฐ๊ฑด์„ ์ถ”๊ฐ€

ํ•˜์ง€๋งŒ, ๊ทธ๋ž˜๋„ ํ‰๊ท ์ ์œผ๋กœ 1113.6ms ๊ฐ€ ์†Œ๋ชจ ๋œ๋‹ค.
๊ฒฐ์ฝ” ์งง์€ ์‹œ๊ฐ„์ด ์•„๋‹ˆ๊ธฐ์— ๊ด€๋ฆฌ์ž๊ฐ€ ๋Š๋ผ๋Š” ๋”œ๋ ˆ์ด๋ฅผ ๋” ์ค„์ด๊ธฐ ์œ„ํ•ด ํŽธ๋ฒ•?์€ ์•„๋‹ˆ์ง€๋งŒ ํ•„ํ„ฐ๋ง ์กฐ๊ฑด๋“ค์„ ์—ฌ๋Ÿฌ๊ฐœ ๋” ์ถ”๊ฐ€ํ•˜์˜€๋‹ค. 

์œ„์™€ ๊ฐ™์ด ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์—์„œ๋Š” ๊ธฐ๊ฐ„๋ณ„๋กœ, ํšŒ์›์˜ ์ด๋ฆ„, ์•„์ด๋””๋ณ„๋กœ, ํšŒ์˜์‹ค ์ด๋ฆ„๋ณ„๋กœ, ์˜ˆ์•ฝ ์ƒํƒœ๋ณ„๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.
Querydsl์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์กฐ๊ฑด์„ ๋™์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์กฐ๊ฑด์— ๋Œ€ํ•ด์„œ ์ธ๋ฑ์Šค๋ฅผ ๊ฑธ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์นด๋””๋„๋ฆฌํ‹ฐ, ์„ ํƒ๋„, ํ™œ์šฉ๋„, ์ˆ˜์ •๋นˆ๋„๋ฅผ ๊ณ ๋ คํ•ด์„œ
์ถ”๊ฐ€ํ•˜๋ฉด ๋” ์ข‹์€ ์„ฑ๋Šฅ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

์‹ค์ œ๋กœ, Member ํ…Œ์ด๋ธ”์˜ name(ํšŒ์›์ด๋ฆ„) ์ปฌ๋Ÿผ์— ์ธ๋ฑ์‹ฑํ•˜์—ฌ
ํšŒ์›์ด๋ฆ„์„ ์กฐ๊ฑด์œผ๋กœ ๊ด€๋ฆฌ์ž์˜ ์˜ˆ์•ฝ ๋‚ด์—ญ ์กฐํšŒ ๊ธฐ๋Šฅ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์€ 563ms -> 78ms๋กœ ์ค„์–ด๋“ค๊ฒŒ ๋˜์—ˆ๋‹ค.