
์ธ๊ธฐ ์์ ์กฐํ ์ฑ๋ฅ์ ๊ฐ์ ํ๊ธฐ ์ํด Redis ์๋ต ์บ์๋ฅผ ์ ์ฉํ๋ค.
์บ์๊ฐ ์กด์ฌํ๋ ๋์์๋ ๋์ผํ ์ธ๊ธฐ ์์ ์์ฒญ์ด ๋ค์ด์๋ Redis์ ์ ์ฅ๋ ์์ฑ ์๋ต์ ๋ฐ๋ก ๋ฐํํ๊ธฐ ๋๋ฌธ์ Redis ๋ญํน ์กฐํ์ MySQL ์กฐํ๋ฅผ ์๋ตํ ์ ์์๋ค.
๊ทธ๋ฐ๋ฐ ์บ์๋ฅผ ์ ์ฉํ๋ค๊ณ ํด์ ๋ชจ๋ ๋ฌธ์ ๊ฐ ๋๋ ๊ฒ์ ์๋์๋ค.
์บ์์ TTL์ด ๋ง๋ฃ๋๋ ์๊ฐ ๋์ผํ ์์ฒญ์ด ๋์์ ๋ค์ด์ค๋ฉด, ๋ชจ๋ ์์ฒญ์ด Cache Miss๋ก ํ๋จํ์ฌ ๊ฐ์ Redis ๋ญํน๊ณผ MySQL ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ณตํด์ ์กฐํํ ์ ์์๋ค.
์ด๋ฒ ๊ธ์์๋ ์ด Cache Stampede ๋ฌธ์ ๋ฅผ ์ง์ ์ฌํํ๊ณ , ์ฌ๋ฌ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋น๊ตํ ๋ค Key ๋จ์ Single Flight๋ฅผ ์ ์ฉํ์ฌ ์๋ณธ ์กฐํ๋ฅผ 10ํ์์ 1ํ๋ก ์ค์ธ ๊ณผ์ ์ ์ ๋ฆฌํ๋ค.
RoomPick์ ์ธ๊ธฐ ์์ ์กฐํ๋ ๋ค์ ์์๋ก ๋์ํ๋ค.
์ฌ์ฉ์ ์์ฒญ
โ ์ธ๊ธฐ ์์ ์๋ต ์บ์ ์กฐํ
โ Cache Hit๋ผ๋ฉด ์์ฑ๋ ์๋ต ๋ฐํ
โ Cache Miss๋ผ๋ฉด Redis ์ธ๊ธฐ ๋ญํน ์กฐํ
โ MySQL์์ ACTIVE ์์ ์ ๋ณด ์ผ๊ด ์กฐํ
โ Redis ๋ญํน ์์ ๋ณต์
โ ๋น๊ณต๊ฐยท์ญ์ ์์ ํํฐ๋ง
โ rank ๊ณ์ฐ ๋ฐ ์๋ต DTO ์์ฑ
โ ์์ฑ๋ ์๋ต์ Redis ์บ์์ ์ ์ฅ
์๋ต ์บ์ ํค์๋ ๋ค์ ๊ฐ์ด ํฌํจ๋๋ค.
์กฐํ ๊ธฐ๊ฐ + ๊ธฐ๊ฐ ๊ธฐ์ค ๋ ์ง + limit
์๋ฅผ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
DAILY + 2026-08-05 + limit 10
WEEKLY + 2026-08-04 + limit 10
DAILY + 2026-08-05 + limit 20
๊ธฐ๊ฐ์ด๋ limit์ด ๋ค๋ฅด๋ฉด ์๋ก ๋ค๋ฅธ ์บ์๋ก ๊ด๋ฆฌ๋๋ค.
ํ์ฌ ์ธ๊ธฐ ์์ ์๋ต ์บ์์ ๊ธฐ๋ณธ TTL์ 60์ด๋ค.
60์ด๋ ์ ๊ณ์์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ๋ ํ์ค๊ฐ์ด ์๋๋ผ, RoomPick์ ๋ฐ์ดํฐ ํน์ฑ๊ณผ ์ฑ๋ฅ ์๊ตฌ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ํ ์ด๊ธฐ ์ ์ถฉ๊ฐ์ด๋ค.
์ธ๊ธฐ ์์ ์์๋ ์์ฝ ๊ฐ๋ฅ ์ฌ๋ถ๋ ๊ฒฐ์ ๊ธ์ก์ฒ๋ผ ๊ฑฐ๋ ์ ํฉ์ฑ์ ๊ฒฐ์ ํ๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ค.
์์ฝ ๊ฐ๋ฅ ์ฌ๋ถ๊ฐ ์ค๋๋ ๊ฒฝ์ฐ
โ ์ด๋ฏธ ์์ฝ๋ ๊ฐ์ค์ ์์ฝํ ์ ์๋ค๊ณ ํ๋จํ ์ ์์
โ ๊ฑฐ๋ ์ ํฉ์ฑ ๋ฌธ์ ๋ฐ์
์ธ๊ธฐ ์์ ์์๊ฐ ์ฝ 60์ด ์ค๋๋ ๊ฒฝ์ฐ
โ ์ต๊ทผ ์กฐํ์๊ฐ ์์์ ์ ์ ๋ฆ๊ฒ ๋ฐ์๋จ
โ ๊ฑฐ๋ ์ ํฉ์ฑ์๋ ์ง์ ์ ์ธ ์ํฅ์ด ์์
๋ฐ๋ผ์ ์ธ๊ธฐ ์์ ์์์๋ ์ฝ๊ฐ์ ์ต์ ์ฑ ์ง์ฐ์ ํ์ฉํ๊ณ , ๊ทธ ๋์ ๋ฐ๋ณต๋๋ Redis ๋ญํน ์กฐํ์ MySQL ์กฐํ๋ฅผ ์ค์ด๋ ๋ฐฉํฅ์ ์ ํํ๋ค.
TTL์ 10์ด๋ 30์ด๋ก ์ค์ ํ๋ฉด ์ต๊ทผ ๋ญํน์ ๋ ๋น ๋ฅด๊ฒ ๋ฐ์ํ ์ ์๋ค.
ํ์ง๋ง ๋ค์ ๋น์ฉ์ด ์ฆ๊ฐํ๋ค.
์บ์ ๋ง๋ฃ ํ์ ์ฆ๊ฐ
โ Cache Miss ์ฆ๊ฐ
โ Redis ๋ญํน ์กฐํ ์ฆ๊ฐ
โ MySQL ์กฐํ ์ฆ๊ฐ
โ DTO ์ฌ์์ฑ ์ฆ๊ฐ
โ Cache Stampede ๋ฐ์ ๊ธฐํ ์ฆ๊ฐ
ํ์ฌ ์ธ๊ธฐ ์์๋ฅผ ์ ์ด ๋จ์๋ก ๊ฐฑ์ ํด์ผ ํ๋ค๋ ์ ํ ์๊ตฌ๋ ์์๋ค.
TTL์ 3๋ถ์ด๋ 5๋ถ์ผ๋ก ์ค์ ํ๋ฉด Cache Hit ๋น์จ์ ๋์์ง ์ ์๋ค.
๊ทธ๋ฌ๋ ์บ์๊ฐ ๋ง๋ค์ด์ง ์งํ ๋ญํน์ด ๋ณ๊ฒฝ๋๋ฉด ์ฌ์ฉ์๋ ์ต๋ 5๋ถ ์ ์ ์ธ๊ธฐ ์์๋ฅผ ๋ณด๊ฒ ๋๋ค.
์ธ๊ธฐ ์์๋ ์ต๊ทผ ์ฌ์ฉ์์ ๊ด์ฌ ํ๋ฆ์ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ด๋ฏ๋ก, ์ด๊ธฐ ์ ์ฑ ์์๋ ์ต๋ ๋ฐ์ ์ง์ฐ์ ์ฝ 1๋ถ์ผ๋ก ์ ํํ๋ค.
60์ด๊ฐ ์ต์ ๊ฐ์ด๋ผ๊ณ ๋จ์ ํ ๊ฒ์ ์๋๋ค.
์ค์ ํธ๋ํฝ์ ๊ธฐ์ค์ผ๋ก 30์ด, 60์ด, 180์ด, 300์ด ๋ฑ์ ๋น๊ตํ ๋ค ์กฐ์ ํ ์ ์๋ ์ด์ ์ค์ ์ผ๋ก ๊ด๋ฆฌํ๋ค.
์บ์๊ฐ ์ ์ง๋๋ ๋์์๋ ๊ฐ์ ์์ฒญ์ด ์ฌ๋ฌ ๋ฒ ๋ค์ด์๋ ๋ฌธ์ ๊ฐ ์๋ค.
์์ฒญ 10๊ฑด
โ ๋ชจ๋ Cache Hit
โ Redis ๋ญํน ์กฐํ 0ํ
โ MySQL ์กฐํ 0ํ
๋ฌธ์ ๋ ์บ์๊ฐ ๋ง๋ฃ๋๋ ์๊ฐ์ด๋ค.
์บ์๊ฐ ์ฌ๋ผ์ง ์งํ ๋์ผํ ์์ฒญ 10๊ฑด์ด ๋์์ ๋ค์ด์ค๋ฉด ๋ชจ๋ ์์ฒญ์ด ๋ค์๊ณผ ๊ฐ์ด ํ๋จํ ์ ์๋ค.
์์ฒญ 1 โ ์บ์๊ฐ ์๋ค
์์ฒญ 2 โ ์บ์๊ฐ ์๋ค
์์ฒญ 3 โ ์บ์๊ฐ ์๋ค
...
์์ฒญ 10 โ ์บ์๊ฐ ์๋ค
๊ฐ ์์ฒญ์ ๋ค๋ฅธ ์์ฒญ์ด ๋์ผํ ์๋ต์ ๋ง๋ค๊ณ ์๋ค๋ ์ฌ์ค์ ์ ์ ์๋ค.
๊ฒฐ๊ตญ ๋ชจ๋ ์์ฒญ์ด ๋์ผํ ์๋ณธ ์กฐํ๋ฅผ ์คํํ๋ค.
์์ฒญ 1 โ Redis ์กฐํ โ MySQL ์กฐํ โ ์๋ต ์์ฑ
์์ฒญ 2 โ Redis ์กฐํ โ MySQL ์กฐํ โ ์๋ต ์์ฑ
์์ฒญ 3 โ Redis ์กฐํ โ MySQL ์กฐํ โ ์๋ต ์์ฑ
...
์์ฒญ 10 โ Redis ์กฐํ โ MySQL ์กฐํ โ ์๋ต ์์ฑ
์ต์ข ๊ฒฐ๊ณผ๋ ๊ฐ์ง๋ง Redis์ DB์๋ ๋ถํ์ํ ์์ฒญ์ด ๋ฐ๋ณต๋๋ค.
์ด์ฒ๋ผ ์บ์๊ฐ ๋ง๋ฃ๋ ์๊ฐ ๋ค์์ ์์ฒญ์ด ๋์์ ์๋ณธ ๋ฐ์ดํฐ๋ก ๋ชฐ๋ฆฌ๋ ํ์์ Cache Stampede, ์ฆ ์บ์ ์๋ ํ์์ด๋ผ๊ณ ํ๋ค.
์๋น์ ์ค๋์ ์ธ๊ธฐ ๋ฉ๋ด๊ฐ ์ ํ ์๋ดํ์ด ์๋ค๊ณ ์๊ฐํด ๋ณด์.
์๋ดํ์ด ์ ํจํ ๋์ ์๋๋ค์ ์๋ดํ๋ง ํ์ธํ๋ฉด ๋๋ค.
์๋ 10๋ช
โ ์๋ดํ ํ์ธ
โ ์ง์์๊ฒ ์ง๋ฌธํ์ง ์์
๊ทธ๋ฐ๋ฐ ์๋ดํ ๊ต์ฒด ์๊ฐ์ด ๋์ด ์๋ดํ์ด ๋น์ด ์์ ๋ ์๋ 10๋ช ์ด ๋์์ ๋ค์ด์ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
๊ธฐ์กด ๋ฐฉ์์์๋ ์๋ 10๋ช ์ด ๋ชจ๋ ์ฃผ๋ฐฉ ์ง์์๊ฒ ๊ฐ์ ์ง๋ฌธ์ ํ๋ค.
์๋ 10๋ช
โ ์ค๋์ ์ธ๊ธฐ ๋ฉ๋ด๊ฐ ๋ฌด์์ธ์ง ๊ฐ๊ฐ ์ง๋ฌธ
โ ์ง์์ด ๊ฐ์ ๋ด์ฉ์ 10๋ฒ ํ์ธ
โ ๊ฐ์ ์๋ดํ์ 10๋ฒ ์์ฑ
์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ๋ค.
์ฒซ ๋ฒ์งธ ์๋
โ ์ฃผ๋ฐฉ ์ง์์๊ฒ ์ธ๊ธฐ ๋ฉ๋ด ์ง๋ฌธ
โ ์ ์๋ดํ ์์ฑ
๋๋จธ์ง ์๋ 9๋ช
โ ์ฒซ ๋ฒ์งธ ์๋์ด ๋ฐ์์ฌ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆผ
โ ์์ฑ๋ ๊ฐ์ ์๋ดํ์ ํจ๊ป ์ฌ์ฉ
์์ฒญ์ ๊ฑฐ๋ถํ๋ ๊ฒ์ด ์๋๋ผ ์ด๋ฏธ ์งํ ์ค์ธ ๋์ผ ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๊ณต์ ํ๋ ๊ฒ์ด๋ค.
์ฒ์๋ถํฐ ๋ฝ์ ์ ์ฉํ์ง ์์๋ค.
์ค์ ๋ก ํ์ฌ ์ฝ๋์์ Cache Stampede๊ฐ ๋ฐ์ํ๋์ง ํตํฉ ํ ์คํธ๋ก ๋จผ์ ํ์ธํ๋ค.
ํ ์คํธ ์กฐ๊ฑด์ ๋ค์๊ณผ ๊ฐ๋ค.
์บ์ ์ํ: Cold cache
์กฐํ ๊ธฐ๊ฐ: DAILY
limit: 1
๋์ ์์ฒญ: 10๊ฑด
Cold cache๋ ํด๋น ์์ฒญ์ ์๋ต์ด ์บ์์ ์กด์ฌํ์ง ์๋ ์ํ๋ฅผ ์๋ฏธํ๋ค.
๋จ์ํ ์ค๋ ๋ 10๊ฐ๋ฅผ ์คํํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ ํํ ์ฌํ์ด ์ด๋ ค์ ๋ค.
์ฒซ ๋ฒ์งธ ์์ฒญ์ด ๋๋ฌด ๋นจ๋ฆฌ ์บ์๋ฅผ ์์ฑํ๋ฉด ๋๋จธ์ง ์์ฒญ์ Cache Hit๊ฐ ๋์ด ๋ฌธ์ ๋ฅผ ์ฌํํ์ง ๋ชปํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ CountDownLatch๋ฅผ ์ฌ์ฉํ๋ค.
ready
โ ๋ชจ๋ ์์ฒญ ์ค๋ ๋๊ฐ ์์ ์ค๋น๋ฅผ ๋ง์ณค๋์ง ํ์ธ
start
โ ์ค๋น๋ ์์ฒญ 10๊ฑด์ ๋์์ ์ถ๋ฐ
rankingLookupEntered
โ ์์ฒญ์ด ์ค์ Redis ๋ญํน ์กฐํ์ ์ง์
ํ๋์ง ํ์ธ
releaseRankingLookup
โ ์ฒซ ์์ฒญ์ด ๋๋ฌด ๋นจ๋ฆฌ ์บ์๋ฅผ ๋ง๋ค์ง ๋ชปํ๋๋ก ์๋ณธ ์กฐํ ๋ฐํ์ ์ ์ ์ฐจ๋จ
ํต์ฌ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
CountDownLatch ready =
new CountDownLatch(THREAD_COUNT);
CountDownLatch start =
new CountDownLatch(1);
CountDownLatch rankingLookupEntered =
new CountDownLatch(THREAD_COUNT);
CountDownLatch releaseRankingLookup =
new CountDownLatch(1);
๋ชจ๋ ์์ฒญ์ด Cache Miss ์ดํ ์๋ณธ ์กฐํ์ ์ง์ ํ ๋๊น์ง Redis ๋ญํน ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ์ง ์๋๋ก ๊ตฌ์ฑํ๋ค.
๋์ผํ ์บ์ Key๋ก ์์ฒญ 10๊ฑด์ ๋์์ ์คํํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์๋ค.
Redis ์ธ๊ธฐ ๋ญํน ์กฐํ: 10ํ
MySQL ACTIVE ์์ ์กฐํ: 10ํ
๋์ผ ์๋ต ์์ฑ: 10ํ
์บ์๊ฐ ์ต์ข ์ ์ผ๋ก ์ ์ฅ๋๊ธฐ๋ ํ์ง๋ง, ์บ์๊ฐ ๋ง๋ค์ด์ง๊ธฐ ์ ๋์ผํ ๊ณ์ฐ์ 10๋ฒ ๋ฐ๋ณตํ๊ณ ์์๋ค.
์ฆ, Cache Stampede๊ฐ ์ด๋ก ์ ์ผ๋ก๋ง ๊ฐ๋ฅํ ๋ฌธ์ ๊ฐ ์๋๋ผ ํ์ฌ RoomPick ์ฝ๋์์ ์ค์ ๋ก ๋ฐ์ํ๋ ๋ฌธ์ ์์ ํ์ธํ๋ค.
๋ฌธ์ ๋ฅผ ์ฌํํ ๋ค ๋ค์ ์ธ ๊ฐ์ง ๋์์ ๋น๊ตํ๋ค.
@Cacheable(sync = true)@Cacheable(sync = true)Spring Cache์๋ ๋์ผํ ์บ์ Key์ ๋์ ์กฐํ๋ฅผ ๋๊ธฐํํ๊ธฐ ์ํ ์ต์ ์ด ์๋ค.
@Cacheable(
cacheNames = "popularAccommodations",
key = "...",
sync = true
)
์ฒ์์๋ ๊ฐ์ฅ ๊ฐ๋จํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก ๋ณด์๋ค.
ํ์ง๋ง RoomPick์ Redis Cache Manager๋ ๊ธฐ๋ณธ Non-locking Redis Cache Writer๋ฅผ ์ฌ์ฉํ๊ณ ์์๋ค.
์ค์ํ ๊ฒ์ ๋ฌธ์๋ง ๋ณด๊ณ ๋์ํ๋ค๊ณ ๊ฐ์ ํ์ง ์๋ ๊ฒ์ด์๋ค.
์ค์ ๋ก sync = true๋ฅผ ์ ์ฉํ ๋ค ๋์ผํ Stampede ํ
์คํธ๋ฅผ ๋ค์ ์คํํ๋ค.
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์๋ค.
๋์ ์์ฒญ 10๊ฑด
โ Redis ๋ญํน ์กฐํ 10ํ
โ MySQL ์กฐํ 10ํ
ํ์ฌ Redis ์บ์ ๊ตฌ์ฑ์์๋ sync = true๋ง ์ถ๊ฐํด๋ ์๋ณธ ์กฐํ๊ฐ ํ๋๋ก ํฉ์ณ์ง์ง ์์๋ค.
ํจ๊ณผ๊ฐ ์๋ ์ค์ ์ด๋ฏ๋ก ๋ค์ ์ ๊ฑฐํ๊ณ ์ปค๋ฐ์๋ ํฌํจํ์ง ์์๋ค.
Redis Cache Writer๋ฅผ Locking ๋ฐฉ์์ผ๋ก ๋ฐ๊พธ๋ ๋ฐฉ๋ฒ๋ ๊ฒํ ํ๋ค.
๊ทธ๋ฌ๋ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์ ๊ธ ๋ฒ์๋ ๋์ผํ ์บ์ Key๋ค.
DAILY:2026-08-05:10
โ ์ด ํค์ ๋์ผํ ์์ฒญ๋ง ๋๊ธฐ
์บ์ ์ด๋ฆ ์ ์ฒด๋ฅผ ์ ๊ทธ๋ ๋ฐฉ์์ ์๋ก ๊ด๊ณ์๋ ์์ฒญ๊น์ง ๊ธฐ๋ค๋ฆฌ๊ฒ ํ ์ ์๋ค.
DAILY + limit 10 ๊ฐฑ์ ์ค
WEEKLY + limit 10
โ ๋ค๋ฅธ ์์ฒญ์ด์ง๋ง ๋๊ธฐํ ๊ฐ๋ฅ์ฑ
DAILY + limit 20
โ ๋ค๋ฅธ ์์ฒญ์ด์ง๋ง ๋๊ธฐํ ๊ฐ๋ฅ์ฑ
Stampede๋ฅผ ๋ง๋ ๋์ ์บ์ ์ ์ฒด๊ฐ ์๋ก์ด ๋ณ๋ชฉ ์ง์ ์ด ๋ ์ ์๋ค.
Redis ๋ถ์ฐ ๋ฝ์ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ ์ฌ์ด์์๋ ์ค๋ณต ๊ฐฑ์ ์ ์ ์ดํ ์ ์๋ค.
ํ์ง๋ง ๋ถ์ฐ ๋ฝ์๋ ๋ค์๊ณผ ๊ฐ์ ์ถ๊ฐ ์ค๊ณ๊ฐ ํ์ํ๋ค.
๋ฝ ํ๋ ์คํจ ์ฒ๋ฆฌ
๋ฝ ๋๊ธฐ ์๊ฐ
๋ฝ ๋ง๋ฃ ์๊ฐ
์ฌ์๋ ์ ์ฑ
๋ฝ ์์ ๊ถ ํ์ธ
์๋ฒ ์ฅ์ ์ ๋ฝ ํด์
Redis ์ฅ์ ์ฒ๋ฆฌ
ํ์ฌ RoomPick์ ๋จ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ธ์คํด์ค๋ฅผ ๊ธฐ์ค์ผ๋ก ์ด์ํ๊ณ ์์๋ค.
์์ง ์กด์ฌํ์ง ์๋ ๋ค์ค ์ธ์คํด์ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ถ์ฐ ๋ฝ์ ๋ณต์ก์ฑ์ ๋ฏธ๋ฆฌ ์ถ๊ฐํ๋ ๊ฒ์ ๊ณผ๋ํ๋ค๊ณ ํ๋จํ๋ค.
ํ์ฌ ๊ตฌ์กฐ์์ ๊ฐ์ฅ ์ ์ ๋ณต์ก๋๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ด๋ถ Key ๋จ์ Single Flight๋ฅผ ์ ํํ๋ค.
Single Flight๋ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ด๋ค.
๋์ผํ ์์ ์ด ์ด๋ฏธ ์งํ ์ค์ด๋ผ๋ฉด ์๋ก์ด ์์ ์ ์คํํ์ง ์๊ณ , ์งํ ์ค์ธ ํ ๋ฒ์ ์์ ๊ฒฐ๊ณผ๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ค.
์๋ก์ด Service๋ฅผ ์ถ๊ฐํ๋ค.
PopularAccommodationSingleFlightService
์ ์ฒด ํธ์ถ ๊ตฌ์กฐ๋ ๋ค์์ฒ๋ผ ๋ณ๊ฒฝ๋๋ค.
Controller
โ AccommodationFacade
โ PopularAccommodationQueryService
โ Redis ๋ญํน ์กฐํ
โ MySQL ์์ ์กฐํ
Controller
โ AccommodationFacade
โ PopularAccommodationSingleFlightService
โ PopularAccommodationQueryService
โ Redis ๋ญํน ์กฐํ
โ MySQL ์์ ์กฐํ
๊ฐ ๊ณ์ธต์ ์ฑ ์์ ๋ค์๊ณผ ๊ฐ๋ค.
AccommodationFacade
โ ์ธ๊ธฐ ์์ ์กฐํ ํ๋ฆ ์กฐ์จ
โ Redis ์ฅ์ fallback ์ฒ๋ฆฌ
PopularAccommodationSingleFlightService
โ ๋์ผํ ์บ์ Key์ ๋์ ์์ฒญ ์ ์ด
PopularAccommodationQueryService
โ ์ธ๊ธฐ ์์ ๋ฐ์ดํฐ ์กฐํฉ
โ @Cacheable์ ํตํ ์๋ต ์บ์ฑ
PopularAccommodationRankingService
โ Redis ์ธ๊ธฐ ๋ญํน ์กฐํ
AccommodationService
โ MySQL ACTIVE ์์ ์กฐํ
๋์์ฑ ์ ์ด ์ฑ ์์ ๋ณ๋ Service๋ก ๋ถ๋ฆฌํ์ฌ QueryService๋ Repository๊ฐ ๊ณผ๋ํ ์ฑ ์์ ๊ฐ์ง์ง ์๋๋ก ํ๋ค.
๊ตฌํ์์๋ ๋ค์ ๋ ๊ฐ์ง๋ฅผ ์ฌ์ฉํ๋ค.
ConcurrentHashMap
CompletableFuture
์งํ ์ค์ธ ์์ ์ ๋ค์ ํํ๋ก ๊ด๋ฆฌํ๋ค.
private final ConcurrentMap<
String,
CompletableFuture<List<PopularAccommodationResponseDto>>
> inFlightRequests = new ConcurrentHashMap<>();
๊ฐ๋ ์ ์ผ๋ก๋ ๋ค์ ์ ๋ณด๊ฐ ๋ค์ด ์๋ค.
Key
โ DAILY:2026-08-05:10
Value
โ ํ์ฌ ์งํ ์ค์ธ ์ธ๊ธฐ ์์ ์กฐํ ์์
๋์ผ ํค๋ก ๋ค์ด์จ ์์ฒญ ์ค ์ต์ด ์์ฒญ๋ง ์ค์ ์กฐํ๋ฅผ ์ํํ๋ค.
CompletableFuture<List<PopularAccommodationResponseDto>>
newRequest = new CompletableFuture<>();
CompletableFuture<List<PopularAccommodationResponseDto>>
existingRequest = inFlightRequests.putIfAbsent(
cacheKey,
newRequest
);
putIfAbsent()๋ ๋ค์๊ณผ ๊ฐ์ด ๋์ํ๋ค.
Map์ ํด๋น Key๊ฐ ์๋ค
โ ๋ด๊ฐ ๋ง๋ Future ๋ฑ๋ก
โ ๋ด๊ฐ ์ต์ด ์์ฒญ
Map์ ํด๋น Key๊ฐ ์ด๋ฏธ ์๋ค
โ ๊ธฐ์กด Future ๋ฐํ
โ ๋ค๋ฅธ ์์ฒญ์ด ์ด๋ฏธ ์์
์ค
์ฌ๋ฌ ์์ฒญ์ด ๋์์ ์ ๊ทผํด๋ ConcurrentHashMap๊ณผ putIfAbsent()๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ ์์ฒญ๋ง ์ต์ด ์์ฒญ์ด ๋๋ค.
์์ฒญ A โ ๋ฑ๋ก ์ฑ๊ณต โ ์ค์ ์กฐํ ์คํ
์์ฒญ B โ ๊ธฐ์กด Future ๋ฐ๊ฒฌ โ A์ ๊ฒฐ๊ณผ ๋๊ธฐ
์์ฒญ C โ ๊ธฐ์กด Future ๋ฐ๊ฒฌ โ A์ ๊ฒฐ๊ณผ ๋๊ธฐ
์ด๋ฏธ ๊ฐ์ Key์ ์์ฒญ์ด ์งํ ์ค์ด๋ผ๋ฉด ์๋ก์ด ์๋ณธ ์กฐํ๋ฅผ ์์ํ์ง ์๋๋ค.
if (existingRequest != null) {
return awaitResult(
existingRequest
);
}
์ต์ด ์์ฒญ์ CompletableFuture๊ฐ ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ ๋ค ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
์ต์ด ์์ฒญ
โ Redis ๋ญํน ์กฐํ
โ MySQL ์กฐํ
โ ์๋ต ์์ฑ
โ Future ์๋ฃ
๋๊ธฐ ์์ฒญ
โ ๊ธฐ์กด Future ๋๊ธฐ
โ ์ต์ด ์์ฒญ์ด ๋ง๋ ์๋ต ๋ฐํ
์ต์ด ์์ฒญ์ด ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋๋ฉด ๊ฒฐ๊ณผ๋ก Future๋ฅผ ์๋ฃํ๋ค.
currentRequest.complete(
result
);
๋๊ธฐ ์ค์ธ ๋ชจ๋ ์์ฒญ์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌ๋ฐ๋๋ค.
์์ฒญ 10๊ฑด
โ ์ค์ ์๋ณธ ์กฐํ 1ํ
โ ๊ฒฐ๊ณผ ๊ฐ์ฒด๋ฅผ 10๊ฐ ์์ฒญ์ด ๊ณต์
์ฑ๊ณต ๊ฒฐ๊ณผ๋ง ๊ณต์ ํด์๋ ์ ๋๋ค.
Redis ์กฐํ์ ์คํจํ์ ๋ ์ต์ด ์์ฒญ๋ง ์์ธ๋ฅผ ๋ฐ๊ณ , ๋๊ธฐ ์์ฒญ๋ค์ด ๋ฉ์ถ๊ฑฐ๋ ๋ค๋ฅธ ๋์์ ํ๋ฉด ์ ๋๋ค.
๋ฐ๋ผ์ ์ต์ด ์์ฒญ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด Future๋ ์์ธ ์ํ๋ก ์๋ฃํ๋ค.
currentRequest.completeExceptionally(
exception
);
๋๊ธฐ ์์ฒญ์ CompletionException ๋ด๋ถ์ ์๋ณธ ์์ธ๋ฅผ ๊บผ๋ด ๋ค์ ์ ๋ฌํ๋ค.
if (cause instanceof RuntimeException runtimeException) {
throw runtimeException;
}
if (cause instanceof Error error) {
throw error;
}
์ด๋ฅผ ํตํด ์ฑ๊ณต๋ฟ๋ง ์๋๋ผ ์คํจ๋ ๊ฐ์ ์์ ์ ๊ฒฐ๊ณผ๋ก ๊ณต์ ํ๋ค.
์ต์ด ์์ฒญ ์ฑ๊ณต
โ ๋ชจ๋ ๋๊ธฐ ์์ฒญ์ด ๊ฐ์ ์ฑ๊ณต ๊ฒฐ๊ณผ ๋ฐํ
์ต์ด ์์ฒญ Redis ์ฅ์
โ ๋ชจ๋ ๋๊ธฐ ์์ฒญ์ด ๊ฐ์ Redis ์ฅ์ ์์ธ ์ ๋ฌ
Single Flight Map์ ํ์ฌ ์งํ ์ค์ธ ์์ฒญ๋ง ๊ด๋ฆฌํด์ผ ํ๋ค.
์๋ฃ๋ Future๊ฐ ๊ณ์ ๋จ์ ์์ผ๋ฉด ์ดํ ์์ฒญ์ด ์ค๋๋ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ ์ฌ์ฌ์ฉํ๊ฑฐ๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ฆ๊ฐํ ์ ์๋ค.
๋ฐ๋ผ์ ์ฑ๊ณต๊ณผ ์คํจ ์ฌ๋ถ์ ์๊ด์์ด finally์์ ์ ๊ฑฐํ๋ค.
finally {
inFlightRequests.remove(
cacheKey,
currentRequest
);
}
๋จ์ํ ๋ค์์ฒ๋ผ ์ ๊ฑฐํ์ง ์์๋ค.
inFlightRequests.remove(cacheKey);
๋์ ๋ค์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
inFlightRequests.remove(
cacheKey,
currentRequest
);
์ด๋ Map์ ์ ์ฅ๋ ๊ฐ์ด ํ์ฌ ์์ฒญ์ Future์ ๊ฐ์ ๋๋ง ์ ๊ฑฐํ๋ค๋ ์๋ฏธ๋ค.
ํน์ ์๋ก์ด ์์ ์ด ๊ฐ์ Key์ ๋ฑ๋ก๋ ์ํ๋ผ๋ฉด ๊ทธ ์์ ์ ์๋ชป ์ ๊ฑฐํ์ง ์๋๋ก ๋ฐฉ์ดํ ์ ์๋ค.
Single Flight ์๋ณ Key๋ ๊ธฐ์กด ์๋ต ์บ์ Key์ ๋์ผํ ์กฐ๊ฑด์ผ๋ก ์์ฑํ๋ค.
return popularAccommodationKeyGenerator
.generateCurrentKey(period)
+ ":"
+ limit;
๋ฐ๋ผ์ ์๋ ์์ฒญ๋ค์ ๊ฐ์ ์์ ์ ๊ณต์ ํ๋ค.
DAILY + 2026-08-05 + limit 10
DAILY + 2026-08-05 + limit 10
๋ฐ๋ฉด ๋ค์ ์์ฒญ๋ค์ ์๋ก ๋ ๋ฆฝ์ ์ผ๋ก ์คํ๋๋ค.
DAILY + limit 10
DAILY + limit 20
WEEKLY + limit 10
์ฆ, ์บ์ ์ ์ฒด์ ๋ฝ์ ๊ฑฐ๋ ๊ฒ์ด ์๋๋ผ ๋์ผํ Key์ ์์ฒญ๋ง ๋ฌถ๋๋ค.
RoomPick์๋ Redis ์ธ๊ธฐ ๋ญํน์ ์ฌ์ฉํ ์ ์์ ๋ API ์ ์ฒด ์ฅ์ ๋ฅผ ๋ง๊ธฐ ์ํ fallback์ด ์กด์ฌํ๋ค.
Redis ์ธ๊ธฐ ๋ญํน ์กฐํ ์คํจ
โ PopularAccommodationRankingUnavailableException ๋ฐ์
โ AccommodationFacade์์ ์์ธ ์ฒ๋ฆฌ
โ ์ต์ ACTIVE ์์๋ฅผ MySQL์์ ์กฐํ
โ ์์ rank ๋ถ์ฌ
โ fallback ์๋ต ๋ฐํ
Single Flight๊ฐ ์์ธ๋ฅผ ๋ด๋ถ์์ ์ผ์ผ๋ฒ๋ฆฌ๋ฉด ๊ธฐ์กด fallback ํ๋ฆ์ด ๊นจ์ง๋ค.
๊ทธ๋์ Single Flight๋ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์๋๋ผ ๋๊ธฐ ์์ฒญ์๋ ๋์ผํ ์์ธ๋ฅผ ์ ๋ฌํ๋ ์ญํ ๋ง ํ๋ค.
Single Flight
โ ๋์ผ ์์ฒญ์ ์ฑ๊ณต ๋๋ ์คํจ ๊ฒฐ๊ณผ ๊ณต์
Facade
โ Redis ์ฅ์ ์ผ ๋ DB fallback ์คํ
์ฑ ์์ ๋ถ๋ฆฌํ์ฌ ๊ธฐ์กด ์ฅ์ ๋์ ํ๋ฆ์ ์ ์งํ๋ค.
Redis ๋ญํน ์กฐํ ์คํจ
โ ์์ธ ๊ณต์
โ Facade์์ ์ต์ ACTIVE ์์ fallback
MySQL ์กฐํ ์คํจ
โ Redis ์ฅ์ ๊ฐ ์๋๋ฏ๋ก fallbackํ์ง ์์
โ DB ์์ธ ๊ทธ๋๋ก ์ ๋ฌ
์๋ชป๋ limit ๋ฑ ๋น์ฆ๋์ค ์์ธ
โ fallback์ผ๋ก ์จ๊ธฐ์ง ์์
โ ์์ธ ๊ทธ๋๋ก ์ ๋ฌ
RoomPick์์๋ ์บ์ ์ ์ฉ ์ ํ์ ์ฑ๋ฅ์ ๋น๊ตํ๊ธฐ ์ํด ์บ์๋ฅผ ๋นํ์ฑํํ ์ ์๋ค.
roompick.cache.popular-accommodations-enabled=false
์บ์๋ง ๋นํ์ฑํํ๊ณ Single Flight๋ ์ ์งํ๋ฉด ๋์ ์์ฒญ์ด ํ๋๋ก ํฉ์ณ์ง๋ค.
๊ทธ๋ฌ๋ฉด ์บ์ ๋ฏธ์ ์ฉ ์ํ์ ์๋ ๋น์ฉ์ ์ ํํ๊ฒ ์ธก์ ํ ์ ์๋ค.
๋ฐ๋ผ์ ์บ์๊ฐ ๋นํ์ฑํ๋ ๊ฒฝ์ฐ Single Flight๋ ์ฐํํ๋ค.
if (!popularAccommodationCacheCondition.isEnabled()) {
return popularAccommodationQueryService
.getPopularAccommodations(
period,
limit
);
}
์ ์ฑ ์ ๋ค์๊ณผ ๊ฐ๋ค.
์บ์ ํ์ฑํ
โ Single Flight ์ ์ฉ
โ ๋์ผ Key์ ์๋ณธ ์กฐํ๋ฅผ ํ๋๋ก ๋ฌถ์
์บ์ ๋นํ์ฑํ
โ Single Flight ์ฐํ
โ ๋ชจ๋ ์์ฒญ์ด ๊ธฐ์กด ์กฐํ ํ๋ฆ ์คํ
์ฑ๋ฅ ํ ์คํธ์ ๊ธฐ์ค ์กฐ๊ฑด์ ์ ์งํ๊ธฐ ์ํ ๊ฒฐ์ ์ด๋ค.
๊ธฐ์กด ์ฌํ ํ ์คํธ๋ฅผ Single Flight ๊ฐ์ ๊ฒ์ฆ ํ ์คํธ๋ก ๋ณ๊ฒฝํ๋ค.
๋์ผํ ์กฐ๊ฑด์ผ๋ก ๋ค์ ์คํํ๋ค.
Cold cache
๋์ผ ์บ์ Key
๋์ ์์ฒญ 10๊ฑด
์ต์ด ์์ฒญ์ด ์๋ณธ ์กฐํ์ ์ง์ ํ๋ฉด Redis ๋ญํน ์๋ต์ ์ ์ ๋ง์ ๋๋จธ์ง ์์ฒญ๋ค์ด ๋์ผํ ์งํ ์ค ์์ ์ ๋ฐ๊ฒฌํ ์ ์๋๋ก ํ๋ค.
CountDownLatch rankingLookupEntered =
new CountDownLatch(1);
CountDownLatch releaseRankingLookup =
new CountDownLatch(1);
๋ชจ๋ ์์ฒญ์ ๋ค์ Service๋ฅผ ํธ์ถํ๋ค.
popularAccommodationSingleFlightService
.getPopularAccommodations(
DAILY,
LIMIT
);
์ต์ข ๊ฒ์ฆ ์กฐ๊ฑด์ ๋ค์๊ณผ ๊ฐ๋ค.
then(popularAccommodationRankingService)
.should(times(1))
.findRankedAccommodationIds(
DAILY,
LIMIT,
0L,
4L
);
then(accommodationService)
.should(times(1))
.findAllActiveSummaryByIds(
rankedAccommodationIds
);
| ํญ๋ชฉ | ๊ฐ์ ์ | ๊ฐ์ ํ |
|---|---|---|
| ๋์ ์์ฒญ ์ | 10๊ฑด | 10๊ฑด |
| Redis ๋ญํน ์กฐํ | 10ํ | 1ํ |
| MySQL ์์ ์กฐํ | 10ํ | 1ํ |
| ์๋ต DTO ์์ฑ | 10ํ | 1ํ |
| ์ฌ์ฉ์ ์์ฒญ ๊ฒฐ๊ณผ | ๋ชจ๋ ์ฑ๊ณต | ๋ชจ๋ ์ฑ๊ณต |
| ์ต์ข Redis ์บ์ ์ ์ฅ | ์ฑ๊ณต | ์ฑ๊ณต |
์ฌ์ฉ์๊ฐ ๋ฐ๋ ์๋ต์ ๋์ผํ์ง๋ง ๋ด๋ถ์์ ๋ฐ์ํ๋ ์ค๋ณต ๋น์ฉ์ ํฌ๊ฒ ๊ฐ์ํ๋ค.
๊ฐ์ ์
๋์ผ ์์ฒญ 10๊ฑด
โ Redis ์กฐํ 10ํ
โ MySQL ์กฐํ 10ํ
โ ๊ฐ์ ์๋ต 10๋ฒ ์์ฑ
๊ฐ์ ํ
๋์ผ ์์ฒญ 10๊ฑด
โ Redis ์กฐํ 1ํ
โ MySQL ์กฐํ 1ํ
โ ์ต์ด ์์ฒญ ๊ฒฐ๊ณผ๋ฅผ 10๊ฑด์ด ๊ณต์
ํนํ MySQL ์กฐํ๊ฐ 10ํ์์ 1ํ๋ก ์ค์ด DB Connection๊ณผ ์ฟผ๋ฆฌ ์คํ ๋น์ฉ์ ์ค์ผ ์ ์์๋ค.
Single Flight ์ ์ฉ ํ ๊ธฐ์กด ๊ธฐ๋ฅ์ด ๊นจ์ง์ง ์์๋์ง ๋ค์ ํ ์คํธ๋ฅผ ์คํํ๋ค.
./gradlew integrationTest \
--tests "*PopularAccommodation*"
./gradlew integrationTest \
--tests "*PopularAccommodationCacheStampedeIntegrationTest"
./gradlew clean test
Facade๊ฐ ๊ธฐ์กด PopularAccommodationQueryService ๋์ PopularAccommodationSingleFlightService๋ฅผ ํธ์ถํ๋๋ก ๋ณ๊ฒฝ๋๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด Facade ๋จ์ ํ
์คํธ์ Mock ๋์๋ ํจ๊ป ์์ ํ๋ค.
๋ชจ๋ ํ ์คํธ๊ฐ ์ต์ข ์ ์ผ๋ก ์ฑ๊ณตํ๋ค.
Controller
โ
AccommodationFacade
โ
PopularAccommodationSingleFlightService
โ
PopularAccommodationQueryService
โ
PopularAccommodationRankingService
โ
Redis Repository
๊ทธ๋ฆฌ๊ณ ์์ ๊ณต๊ฐ ์ ๋ณด๋ ๋ค์ ํ๋ฆ์ผ๋ก ์กฐํ๋๋ค.
PopularAccommodationQueryService
โ
AccommodationService
โ
AccommodationRepository
โ
MySQL
๊ฐ ๊ณ์ธต์ ๋ค์ ์ฑ ์์ ๊ฐ์ง๋ค.
| ๊ณ์ธต | ์ฑ ์ |
|---|---|
| Controller | ์์ฒญ ์์ ๋ฐ ์๋ต ๋ฐํ |
| Facade | ์ ์ฒด ๋น์ฆ๋์ค ํ๋ฆ ์กฐ์จ, Redis ์ฅ์ fallback |
| SingleFlightService | ๋์ผ ์บ์ Key์ ๋์ ์์ฒญ ์ ์ด |
| QueryService | ๋ญํน๊ณผ ์์ ๋ฐ์ดํฐ๋ฅผ ์กฐํฉํ๊ณ ์๋ต ์บ์ฑ |
| RankingService | Redis ๋ญํน ์กฐํ |
| AccommodationService | ACTIVE ์์ DB ์กฐํ |
| Repository | Redis ๋ฐ MySQL ๋ช ๋ น ์คํ |
์ด๋ฒ Single Flight๋ ์ ํ๋ฆฌ์ผ์ด์ ์ธ์คํด์ค ๋ด๋ถ์์๋ง ๋์ํ๋ค.
์๋ฒ๊ฐ ํ ๋๋ผ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
์๋ฒ A์ ๋์ผ ์์ฒญ 10๊ฑด
โ ์๋ฒ A ๋ด๋ถ์์ ์๋ณธ ์กฐํ 1ํ
์๋ฒ๊ฐ ๋ ๋๋ผ๋ฉด ๊ฐ ์๋ฒ๊ฐ ์์ ์ Map์ ๊ฐ์ง๊ณ ์๋ค.
์๋ฒ A์ ์์ฒญ 5๊ฑด
โ ์๋ฒ A์์ ์๋ณธ ์กฐํ 1ํ
์๋ฒ B์ ์์ฒญ 5๊ฑด
โ ์๋ฒ B์์ ์๋ณธ ์กฐํ 1ํ
์ ์ฒด์ ์ผ๋ก๋ ์๋ณธ ์กฐํ๊ฐ 2ํ ๋ฐ์ํ ์ ์๋ค.
ํ์ง๋ง ํ์ฌ RoomPick์ ๋จ์ผ ์ธ์คํด์ค ๊ตฌ์กฐ์ด๋ฏ๋ก ์ ํ๋ฆฌ์ผ์ด์ ๋ด๋ถ Single Flight๊ฐ ํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๊ฐ์ฅ ๋จ์ํ๊ณ ์ ์ ํ ๋ฐฉ์์ด๋ผ๊ณ ํ๋จํ๋ค.
ํฅํ ๋ค์ค ์ธ์คํด์ค ํ๊ฒฝ์์ ์ธ์คํด์ค ๊ฐ ์ค๋ณต ๊ฐฑ์ ์ด ์ค์ ๋ฌธ์ ๋ก ํ์ธ๋๋ค๋ฉด ๋ค์ ๋ฐฉ๋ฒ์ ๊ฒํ ํ ์ ์๋ค.
Redis ๋ถ์ฐ ๋ฝ
๋ถ์ฐ Single Flight
Soft TTL
์บ์ ์ ์ ๊ฐฑ์
๋ฐฑ๊ทธ๋ผ์ด๋ Refresh
์ค์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ ์ ์ ๋ถ์ฐ ๋ฝ์ ๋ณต์ก์ฑ์ ๋ฏธ๋ฆฌ ์ถ๊ฐํ์ง ์๋ ๊ฒ๋ ์ค์ํ ์ค๊ณ ๊ฒฐ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์บ์๋ ํ์์ ์๋ณธ ์กฐํ๋ฅผ ์ค์ฌ์ฃผ์ง๋ง ์บ์ ๋ง๋ฃ ์์ ์๋ ์์ฒญ์ด ์๋ณธ ๋ฐ์ดํฐ๋ก ํ๊บผ๋ฒ์ ๋ชฐ๋ฆด ์ ์๋ค.
Cache Hit ์ฑ๋ฅ๋ฟ ์๋๋ผ ๋ค์ ์ํฉ๋ ํจ๊ป ๊ฒ์ฆํด์ผ ํ๋ค.
Cold cache
TTL ๋ง๋ฃ ์งํ
๋์ ์์ฒญ
Redis ์ฅ์
์บ์ ๋นํ์ฑํ
โ์๋ง ์ค๋ณต ์กฐํ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋คโ๋ผ๋ ์ถ์ธก๋ง์ผ๋ก ๋ณต์กํ ๋ฝ์ ์ถ๊ฐํ์ง ์์๋ค.
CountDownLatch์ ๋ค์ค ์ค๋ ๋ ํ
์คํธ๋ฅผ ํตํด ์ค์ ๋ก Redis์ MySQL ์กฐํ๊ฐ ๊ฐ๊ฐ 10ํ ๋ฐ์ํ๋ค๋ ๊ฒ์ ํ์ธํ ๋ค ๊ฐ์ ํ๋ค.
Redis ๋ถ์ฐ ๋ฝ์ ๊ฐ๋ ฅํ์ง๋ง ํ์ฌ ๋จ์ผ ์ธ์คํด์ค ๊ตฌ์กฐ์์๋ ๊ณผ๋ํ ๋ณต์ก์ฑ์ด ๋ ์ ์๋ค.
ํ์ฌ ๋ฌธ์ ์ ๋ฒ์์ ๋ง์ถฐ Key ๋จ์ ์ ํ๋ฆฌ์ผ์ด์ Single Flight๋ฅผ ์ ํํ๋ค.
๋์ผ ์์ ์ ํ๋๋ก ํฉ์น ๋ ์ฑ๊ณต ๊ฒฐ๊ณผ๋ง ๊ณต์ ํ๋ฉด ์ ๋๋ค.
์ต์ด ์์ฒญ์์ ๋ฐ์ํ ์์ธ๋ ๋๊ธฐ ์์ฒญ์ ์ ๋ฌํด์ผ ๊ธฐ์กด ์์ธ ์ฒ๋ฆฌ์ fallback ํ๋ฆ์ ์ ์งํ ์ ์๋ค.
Redis ์ค๋ณต ์กฐํ๋ฅผ ์ค์ด๋ ๊ฒ๋งํผ ๊ธฐ์กด DB fallback๊ณผ ๋น์ฆ๋์ค ์์ธ ์ ์ฑ ์ ์ ์งํ๋ ๊ฒ๋ ์ค์ํ๋ค.
์ต์ ํ ๋๋ฌธ์ ์์ธ ์๋ฏธ๊ฐ ๋ฌ๋ผ์ง๊ฑฐ๋ ์๋ชป๋ fallback์ด ์คํ๋์ง ์๋๋ก ์ฑ ์์ ๋ถ๋ฆฌํ๋ค.
TTL 60์ด๋ผ๋ ์ซ์๋ง ๋จ์ ์์ผ๋ฉด ์ดํ ํ์์ด ๋ณ๊ฒฝ ์ฌ๋ถ๋ฅผ ํ๋จํ๊ธฐ ์ด๋ ต๋ค.
๋ค์์ ๋ฌธ์ํํ๋ค.
์ 60์ด์ธ๊ฐ
์ ๋ ์งง์ง ์์๊ฐ
์ ๋ ๊ธธ์ง ์์๊ฐ
์ด๋ค ์ต์ ์ฑ ์ง์ฐ์ ํ์ฉํ๋๊ฐ
์ด๋ค ๋ณ๊ฒฝ์ ์ฆ์ ์บ์๋ฅผ ๋ฌดํจํํด์ผ ํ๋๊ฐ
ํ์ฌ ๊ฒฐ์ ์ ํ๊ณ๋ ๋ฌด์์ธ๊ฐ
ํฅํ ์ด๋ค ๋น๊ต ์ธก์ ์ด ํ์ํ๊ฐ
์ซ์๋ณด๋ค ์ค์ํ ๊ฒ์ ์ซ์๋ฅผ ์ ํํ ๊ทผ๊ฑฐ๋ผ๊ณ ๋๊ผ๋ค.
์ด๋ฒ ์์ ์์๋ ์ธ๊ธฐ ์์ ์๋ต ์บ์๊ฐ ๋ง๋ฃ๋ ์งํ ๋์ผ ์์ฒญ์ด ๋์์ ์ ์ ๋๋ฉด Redis ๋ญํน๊ณผ MySQL ์์ ์ ๋ณด๋ฅผ ์ค๋ณต ์กฐํํ๋ Cache Stampede ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๋ค.
๋จผ์ 10๊ฐ์ ๋์ ์์ฒญ์ ์ฌ์ฉํ ํตํฉ ํ ์คํธ๋ก ์ค์ ๋ฌธ์ ๋ฅผ ์ฌํํ๋ค.
๊ฐ์ ์
โ Redis ๋ญํน ์กฐํ 10ํ
โ MySQL ์กฐํ 10ํ
์ดํ @Cacheable(sync = true), Redis Locking Cache Writer, Redis ๋ถ์ฐ ๋ฝ์ ๋น๊ตํ๋ค.
ํ์ฌ RoomPick์ ๋จ์ผ ์ธ์คํด์ค ๊ตฌ์กฐ์์๋ ๋์ผ ์บ์ Key๋ง ์ ์ดํ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ด๋ถ Single Flight๊ฐ ๊ฐ์ฅ ์ ์ ๋ณต์ก๋๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค๊ณ ํ๋จํ๋ค.
ConcurrentHashMap๊ณผ CompletableFuture๋ฅผ ์ฌ์ฉํด ์ต์ด ์์ฒญ์ ์ฑ๊ณต ๊ฒฐ๊ณผ ๋๋ ์์ธ๋ฅผ ๊ณต์ ํ๋๋ก ๊ตฌํํ๊ณ , ๋์ผํ ํ
์คํธ ์กฐ๊ฑด์์ ๋ค์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ค.
๊ฐ์ ํ
โ Redis ๋ญํน ์กฐํ 1ํ
โ MySQL ์กฐํ 1ํ
โ ๋์ ์์ฒญ 10๊ฑด ๋ชจ๋ ๋์ผํ ๊ฒฐ๊ณผ ๋ฐํ
๋ํ ์บ์ ๋นํ์ฑํ ์ฑ๋ฅ ์ธก์ ์กฐ๊ฑด๊ณผ Redis ์ฅ์ ์ DB fallback ํ๋ฆ๋ ๊ทธ๋๋ก ์ ์งํ๋ค.
์ด๋ฒ ๊ฒฝํ์ ํตํด ๋จ์ํ ์บ์๋ฅผ ์ ์ฉํ๋ ๊ฒ์ ๋์ด, ์บ์๊ฐ ๋น์ด ์๊ฑฐ๋ ๋ง๋ฃ๋๋ ์๊ฐ์ ์์คํ ์ด ์ด๋ป๊ฒ ๋์ํ๋์ง๊น์ง ๊ฒ์ฆํด์ผ ํ๋ค๋ ๊ฒ์ ๋ฐฐ์ธ ์ ์์๋ค.
Spring Boot Redis Cache Cache Stampede Single Flight CompletableFuture ConcurrentHashMap ๋์์ฑ ์ฑ๋ฅ ์ต์ ํ MySQL Testcontainers Spring Cache