Redis
๐ Redis(Remote Dictionary Server)
"key-value" ๊ตฌ์กฐ์ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ํ ์คํ ์์ค ๊ธฐ๋ฐ์ ๋น๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ฆฌ ์์คํ (DBMS)์ด๋ค.
๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋๊ฐ ์์ฒญ ๋น ๋ฅธ NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์ด๋ค.
๐ NoSQL(Not Only SQL)
key-value ํํ๋ก ์ ์ฅํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค
๋น๊ด๊ณํ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ ์ฐํ๊ฒ ์ ์ฅํ ์ ์์ด ์ฟผ๋ฆฌ ์์ด๋ ์ฝ๊ฒ ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
Redis ์ฅ์
in-memory
Redis๋ in-memory์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค. ๋๋ฌธ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ฑ๋ฅ์ด ๊ต์ฅํ ๋น ๋ฅด๋ค.
MySQL๊ณผ ๊ฐ์ RDBMS์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋๋ถ๋ถ ๋์คํฌ(Disk)์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค.
ํ์ง๋ง Redis๋ ๋ฉ๋ชจ๋ฆฌ(RAM)์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค. ๋์คํฌ(Disk)๋ณด๋ค ๋ฉ๋ชจ๋ฆฌ(RAM)์์์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์๋๊ฐ ์๋ฑํ๊ฒ ๋น ๋ฅด๋ค. ์ด ๋๋ฌธ์ Redis์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋๊ฐ RDBMS์ ๋นํด ํจ์ฌ ๋น ๋ฅด๋ค.
์ฃผ์ ์ฌ์ฉ ์ฌ๋ก
- ์บ์ฑ (Caching)
- ์ธ์ ๊ด๋ฆฌ (Sessino Management)
- ์ค์๊ฐ ๋ถ์ ๋ฐ ํต๊ณ (Real-time Analystics)
- ๋ฉ์์ง ํ (Message Queue)
- ์ง๋ฆฌ๊ณต๊ฐ ์ธ๋ฑ์ฑ (Geospatial Indexing)
- ์๋ ์ ํ (Rate Limiting)
- ์ค์๊ฐ ์ฑํ ๋ฐ ๋ฉ์์ง (Real-time Chat And Messaging)
๋ ๋์ค(Redis)์๋ ๋ด์ฅ๋ ๊ธฐ๋ฅ์ด ๋ค์ํด ์ฌ๋ฌ ์ฉ๋๋ก ์ฌ์ฉ๋๋ค.
์ฌ๊ธฐ์๋ ํ์ ์์ ๋ง์ด ์ฌ์ฉ๋๋ ‘์บ์ฑ(๋ฐ์ดํฐ ์กฐํ ์ฑ๋ฅ ํฅ์)’์ ๋ํด ์ง์ค์ ์ผ๋ก ๊ณต๋ถํด๋ณผ ๊ฒ์ด๋ค.
๋์ฉ๋ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํ์์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ์ด Redis ์บ์ฑ(Caching) ๊ธฐ๋ฅ์ด๋ค.
Redis ๊ธฐ๋ณธ ๋ช ๋ น์ด
๋ฐ์ดํฐ(Key, Value) ์ ์ฅ
# set [key ์ด๋ฆ] [value]
$ set jaeseong:name "jaeseong park" # ๋์์ฐ๊ธฐ ํด์ ์ ์ฅํ๋ ค๋ฉด ์๋ฐ์ดํ๋ก ๋ฌถ์ด์ฃผ๋ฉด ๋จ
$ set jaeseong:hobby soccer
๋ฐ์ดํฐ ์กฐํ
Key๋ก Value ๊ฐ ์กฐํ
# get [key ์ด๋ฆ]
$ get jaeseong:name
$ get jaeseong:hobby
$ get pjs:name # ์๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๊ฒฝ์ฐ (nil)์ด๋ผ๊ณ ์ถ๋ ฅ๋จ
์ ์ฅ๋ ๋ชจ๋ key ์กฐํ
$ keys *
๋ฐ์ดํฐ ์ญ์
Key๋ก ๋ฐ์ดํฐ ์ญ์
# del [key ์ด๋ฆ]
$ del jaeseong:hobby
$ get jaeseong:hobby # ์ญ์ ๋๋ ์ง ํ์ธ
๋ฐ์ดํฐ ์ ์ฅ ์ ๋ง๋ฃ์๊ฐ(TTL) ์ ํ๊ธฐ
# set [key ์ด๋ฆ] [value] ex [๋ง๋ฃ ์๊ฐ(์ด)]
$ set jaeseong:pet dog ex 30 # ๋์๋ s ๋ค.
redis๋ RDBMS์๋ ๋ค๋ฅด๊ฒ ๋ฐ์ดํฐ ์ ์ฅ ์ ๋ง๋ฃ ์๊ฐ์ ์ค์ ํ ์ ์๋ค.
์๊ตฌ ์ ์ฅ์ด ์๋ ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ฐ์ดํฐ๊ฐ ์ญ์ ๋๋๋ก ์ธํ ํ ์ ์๋ค.
๋ ๋์ค์ ํน์ฑ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ด ํ์ ๋์ด ์๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋์ค์ ์ ์ฅํ ์ ์๋ค. ๋ฐ๋ผ์ ๋ง๋ฃ์๊ฐ(TTL)์ ํ์ฉํด ์์ฃผ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ๋ง ๋ ๋์ค์ ์ ์ฅํด๋๊ณ ์ฐ๋ ์์ผ๋ก ํ์ฉํ๋ค.
๋ง๋ฃ์๊ฐ(TTL) ํ์ธ
# ttl [key ์ด๋ฆ]
$ ttl jaeseong:pet
$ ttl jaeseong:name
$ ttl pjs:name
- ๋ง๋ฃ ์๊ฐ์ด ๋ช ์ด ๋จ์๋ ์ง ๋ฐํํ๋ค.
- ํค๊ฐ ์๋ ๊ฒฝ์ฐ -2๋ฅผ ๋ฐํํ๋ค.
- ํค๋ ์กด์ฌํ์ง๋ง ๋ง๋ฃ ์๊ฐ์ด ์ค์ ๋ผ ์์ง ์์ ๊ฒฝ์ฐ์๋ -1์ ๋ฐํํ๋ค.
๋ชจ๋ ๋ฐ์ดํฐ ์ญ์
$ flushall
key ๋ค์ด๋ฐ ์ปจ๋ฒค์
ํ์ ์์ ์์ฃผ ํ์ฉํ๋ ๋ค์ด๋ฐ ์ปจ๋ฒค์ ์ ์ฝ๋ก (:)์ ํ์ฉํด ๊ณ์ธต์ ์ผ๋ก ์๋ฏธ๋ฅผ ๊ตฌ๋ถํ๋ ๋ฐฉ๋ฒ์ด๋ค.
- ์์
- users:100:profile : ์ฌ์ฉ์๋ค(users) ์ค์์ PK๊ฐ 100์ธ ์ฌ์ฉ์(user)์ ํ๋กํ(profile)
- products:123:details : ์ํ๋ค(products) ์ค์์ PK๊ฐ 123์ธ ์ํ(product)์ ์ธ๋ถ์ฌํญ(details)
- ์ฅ์
- ๊ฐ๋ ์ฑ : ๋ฐ์ดํฐ์ ์๋ฏธ์ ์ฉ๋๋ฅผ ์ฝ๊ฒ ํ์
- ์ผ๊ด์ฑ : ์ปจ๋ฒค์ ์ ๋ฐ๋ฆ์ผ๋ก์จ ์ฝ๋์ ์ผ๊ด์ฑ์ด ๋์์ง๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์์ง
- ๊ฒ์ ๋ฐ ํํฐ๋ง ์ฉ์ด์ฑ : ํจํด ๋งค์นญ์ ์ฌ์ฉํด ํน์ ์ ํ์ Key๋ฅผ ์ฝ๊ฒ ์ฐพ์
- ํ์ฅ์ฑ : ์๋ก ๋ค๋ฅธ Key์ ์ด๋ฆ์ด ๊ฒน์ณ ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ ์ค์ฌ์ค
์บ์(Cache)
๐ ์บ์(Cache)
์๋ณธ ์ ์ฅ์๋ณด๋ค ๋น ๋ฅด๊ฒ ๊ฐ์ ธ์ฌ ์ ์๋ ์์ ๋ฐ์ดํฐ ์ ์ฅ์
๐ ์บ์ฑ(Caching)
์บ์์ ์ ๊ทผํด์ ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ๊ฐ์ ธ์ค๋ ๋ฐฉ์
์บ์ฑ ์ ๋ต
๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ์บ์ฑํ๊ณ ์ด๋ป๊ฒ ์กฐํํ ๊ฒ์ธ์ง์ ๋ํ ์ ๋ต์ด ๋ค์ํ๋ค.
Cache Aside(=Look Aside, Lazy Loading) ์ ๋ต
๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ ์ ๋ต์ด๋ค. Look Aside ์ ๋ต ๋๋ Lazy Loading ์ ๋ต์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ ๋ต ๋ฐฉ์
- ์ฒ์ ๊ฒ์ํ ์๋น์ค๋ฅผ ๋ฐฐํฌํ์ ๋, ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ ๋์ค์๋ ์๋ฌด๋ฐ ๋ฐ์ดํฐ๋ ์ ์ฅ์ด ์ ๋์ด ์์
- ์ฌ์ฉ์๊ฐ ๊ฒ์๊ธ ์์ฑ์ ํจ์ผ๋ก์จ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ
- ์ด ๋ฐ์ดํฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋๋ค. ๋ ๋์ค์๋ ์ ์ฅ๋์ง ์๋๋ค.
- ์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์กฐํ ์์ฒญ
- ์ด ๋, ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ๋ฐ๋ก ๋ฐ์ดํฐ ์กฐํํ๊ธฐ ์ ์ ๋ ๋์ค์ ์๋ ์ง ๋จผ์ ํ์ธํ๋ค.
- ๋ ๋์ค์ ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฑธ ํ์ธํ ํ, ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์กฐํํด์ ์๋ต
- ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ์กฐํํ ๋ฐ์ดํฐ๋ฅผ ์๋ตํ ๋ค์ ๋ ๋์ค์๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ
- ๋ค์ ์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ค๊ณ ์์ฒญ
- ๋ ๋์ค์ ์กฐํํ๊ณ ์ ํ๋ ๋ฐ์ดํฐ๊ฐ ์๋ ์ง ํ์ธ ํ ์กด์ฌํ๋ฉด ๋ ๋์ค๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ก ๊ฐ์ ธ์ด
์ด๊ฒ Cache Aside ์ ๋ต์ ์ ์ฉ์ํจ ํํ์ด๋ค.
๐ Cache Hit: ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ์ ๋ ์บ์์ ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฒฝ์ฐ
๐ Cache Miss: ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ์ ๋ ์บ์์ ์๋ ๊ฒฝ์ฐ
Cache Aside์ ๋ต์ ์บ์(Cache)์์ ๋ฐ์ดํฐ๋ฅผ ํ์ธํ๊ณ , ์๋ค๋ฉด DB๋ฅผ ํตํด ์กฐํํด์ค๋ ๋ฐฉ์์ด๋ผ ๋ณผ ์ ์๋ค.
Write Around ์ ๋ต
Cache Aside ์ ๋ต์ด ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ์กฐํํ ์ง์ ๋ํ ์ ๋ต์ด์๋ค๋ฉด, Write Around ์ ๋ต์ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ์ธ ์ง(์ ์ฅ, ์์ , ์ญ์ )์ ๋ํ ์ ๋ต์ด๋ค. Cache Aside ์ ๋ต๊ณผ ๊ฐ์ด ์์ฃผ ํ์ฉ๋๋ ์ ๋ต์ด๋ค.
์ ๋ต ๋ฐฉ์
๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋๋ ๋ ๋์ค์ ์ ์ฅํ์ง ์๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ง ์ ์ฅํ๋ ๋ฐฉ์์ด๋ค. ๊ทธ๋ฌ๋ค ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ๋ ๋์ค์ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์กฐํํด์์ ๋ ๋์ค์ ์ ์ฅ์์ผ์ฃผ๋ ๋ฐฉ์์ด๋ค.
Write Around ์ ๋ต์ ์ฐ๊ธฐ ์์ (์ ์ฅ, ์์ , ์ญ์ )์ ์บ์์๋ ๋ฐ์ํ์ง ์๊ณ , DB์๋ง ๋ฐ์ํ๋ ๋ฐฉ์์ ๋ปํ๋ค.
Cache Aside, Write Around ์ ๋ต ํ๊ณ์
์ผ๊ด์ฑ
์บ์๋ ๋ฐ์ดํฐ์ DB ๋ฐ์ดํฐ์ ์ผ์นํ์ง ์์ ์ ์๋ค.
Cache Asdie์ Write Around ์ ๋ต์ ๊ฐ์ด ์ผ์ ๋์ ํ๊ณ์ ์ค ํ๋๋ ์บ์๋ ๋ฐ์ดํฐ์ DB ๋ฐ์ดํฐ๊ฐ ์ผ์นํ์ง ์์ ์ ์๋ค๋ ์ ์ด๋ค. ์ฆ, ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ๋ณด์ฅํ ์ ์๋ค.
Write Around ์ ๋ต์ ๋ฐ๋ฅด๋ฉด ๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋ DB๋ง ์ ๋ฐ์ดํธ๋ฅผ ์ํค๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด์ ์ ์ฅ๋ ๋ ๋์ค์ ๋ฐ์ดํฐ ๊ฐ๊ณผ DB์ ๋ฐ์ดํฐ ๊ฐ์ ๋ค๋ฅผ ์ ๋ฐ์ ์๋ค.
์บ์์ DB์ ๋ฐ์ดํฐ๋ฅผ ์ผ์น์ํค๊ธฐ ์ํด, ๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋๋ง๋ค ๋์์ ์ ๋ฐ์ดํธ ์ํค๋ฉด ์ฑ๋ฅ์ ์ผ๋ก ๋๋ ค์ง๋ค. ๊ทธ๋ ๋ค๊ณ ์ฑ๋ฅ ํฅ์์ ์ํด DB์ ๋ฐ์ดํฐ๋ง ์ ๋ฐ์ดํธ ์ํค๋ฉด ์บ์์ DB์ ๋ฐ์ดํฐ๊ฐ ์ผ์นํ์ง ์๊ฒ ๋๋ค.
์ด๋ค ์ ํ์ ํ๋ ๊ธฐํ ๋น์ฉ(Trade Off)์ด ๋ฐ์ํ๋ค. ๋ฌด์ธ๊ฐ๋ฅผ ์ป์ผ๋ฉด ๋ฌด์ธ๊ฐ๋ฅผ ํฌ๊ธฐํด์ผ ํ๋ค. ๋๋ถ๋ถ์ ๊ฐ๋ฐ ๊ธฐ์ ๋ค์ด ์ฅ์ ์ด ์์ผ๋ฉด ๋จ์ ์ด ์๋ค. ๋ฐ๋ผ์ ๋ฐ์ดํฐ ์กฐํ ์ฑ๋ฅ ๊ฐ์ ๋ชฉ์ ์ผ๋ก ๋ ๋์ค๋ฅผ ์ฐ๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ํฌ๊ธฐํ๊ณ ์ฑ๋ฅ ํฅ์์ ํํ ๊ฒ์ด๋ค.
โ ์บ์๋ฅผ ์ ์ฉ์ํค๊ธฐ์ ์ ์ ํ ๋ฐ์ดํฐ
- ์์ฃผ ์กฐํ๋๋ ๋ฐ์ดํฐ
- ์ ๋ณํ์ง ์๋ ๋ฐ์ดํฐ
- ์ค์๊ฐ์ผ๋ก ์ ํํ๊ฒ ์ผ์นํ์ง ์์๋ ๋๋ ๋ฐ์ดํฐ
ํ์ง๋ง ์ฅ๊ธฐ๊ฐ ๋ฐ์ดํฐ๊ฐ ์ผ์นํ์ง ์๋ ๊ฑด ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค. ๋ฐ๋ผ์ ์ ์ ํ ์ฃผ๊ธฐ๋ก ๋ฐ์ดํฐ๋ฅผ ๋๊ธฐํ์์ผ์ฃผ์ด์ผ ํ๋ค. ์ด ๋ ํ์ฉํ๋ ๊ธฐ๋ฅ์ด ๋ ๋์ค์ TTL ๊ธฐ๋ฅ(๋ง๋ฃ ์๊ฐ ์ค์ ๊ธฐ๋ฅ)์ด๋ค.
์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ฐ์ดํฐ๊ฐ ์บ์์์ ์ญ์ ๋๋ค. ๊ทธ๋ผ ํน์ ์ฌ์ฉ์๊ฐ ์กฐํ๋ฅผ ํ๋ ์๊ฐ Cache Miss๊ฐ ๋ฐ์ํ๋๋ฐ ๊ทธ๋
DB์ ๋ฐ์ดํฐ๋ฅผ ์๋ก ์กฐํํด์์ ์บ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ฒ ๋๋ค. ์ฆ, ๋ฐ์ดํฐ๊ฐ ์๋กญ๊ฒ ๊ฐฑ์ ๋๋ ํจ๊ณผ๊ฐ ์๊ฒ ๋๋ค.
์์ ๊ณต๊ฐ
DB๋ ๋์คํฌ(Disk)์ ์ ์ฅํด์ ๋ง์ ์์ ์ ์ฅํ๊ธฐ ์ฉ์ดํ์ง๋ง,
์บ์๋ ๋ฉ๋ชจ๋ฆฌ(RAM)์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ DB์ ๋นํด ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์๊ฐ ์๋ค.
TTL ๊ธฐ๋ฅ(๋ง๋ฃ ์๊ฐ ์ค์ ๊ธฐ๋ฅ)์ ํ์ฉํ๋ฉด ์บ์์ ๊ณต๊ฐ์ ํจ์จ์ ์ผ๋ก ์ธ ์ ์๋ค. ์๋๋ฉด ์์ฃผ ์กฐํํ์ง ์๋ ๋ฐ์ดํฐ๋ ๋ง๋ฃ ์๊ฐ์ ์ํด ๋ฐ์ดํฐ๊ฐ ์ญ์ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
Cache Aside, Write Around ์ ๋ต์ ์ฌ์ฉํ ๋ ์ฃผ๋ก TTL์ ๊ฐ์ด ํ์ฉํ๋ค.
Springboot์์ ์ฌ์ฉํ๊ธฐ
# local
spring:
datasource:
url: jdbc:mysql://localhost:3306/rediscache
username: root
password: rootpassword
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
data:
redis:
host: localhost
port: 6379
logging:
level:
org.springframework.cache: trace # Redis ์ฌ์ฉ์ ๋ํ ๋ก๊ทธ๊ฐ ์กฐํ๋๋๋ก ์ค์
@Cacheable์ spring-context์ ํฌํจ๋์ด ์๋ค.
spring-boot-starter-data-jpa
spring-boot-starter-data-redis
spring-boot-starter-web
๋ฑ ๋ชจ๋ ํฌํจ
@Cacheable
import org.springframework.cache.annotation.Cacheable;
...
@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository boardRepository;
/**
* - cacheNames : ์บ์ ์ด๋ฆ์ ์ค์ , ์ฌ์ฉํ ์บ์ ๊ท์น์ ์ง์ ํ๊ธฐ ์ํ ์ด๋ฆ
* - key : Redis์ ์ ์ฅํ Key์ ์ด๋ฆ์ ์ค์
* - cacheManager : ์ฌ์ฉํ cacheManager์ Bean ์ด๋ฆ์ ์ง์
* @param page ํ์ด์ง
* @param size ์ฌ์ด์ฆ
* @return ์กฐํ ๋ฐ์ดํฐ
*/
@Cacheable(cacheNames = "getBoards", key = "'boards:page:' + #page + ':size:' + #size", cacheManager = "boardCacheManager")
public List<Board> getBoards(int page, int size) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<Board> pageOfBoards = boardRepository.findAllByOrderByCreatedAtDesc(pageable);
return pageOfBoards.getContent();
}
}
@CachePut
@CachePut(cacheNames = "itemCache", key = "#result.id")
public ItemDto create(ItemDto dto) {
return ItemDto.fromEntity(itemRepository.save(Item.builder()
.name(dto.getName())
.description(dto.getDescription())
.price(dto.getPrice())
.stock(dto.getStock())
.build()
));
}
@Cacheable์ ๋ฐ์ดํฐ๋ฅผ ์บ์์์ ๋ฐ๊ฒฌํ ๊ฒฝ์ฐ(Hit), ๋ฉ์๋ ์์ฒด๋ฅผ ์คํํ์ง ์๋๋ค.
๋ฐ๋ฉด, @CachePut์ ํญ์ ๋ฉ์๋๋ฅผ ์คํํ๊ณ , ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ๋ค.
์์ฑ, ๋๋ ์์ ์ ๋ํด์ ์ ์ฉํ๋ฉด Write Through ์ ๋ต์ฒ๋ผ ๋์ํ๋ค.
create()์ key์ธ #result.id: ๋ฐํ๋๋ ItemDto.id๋ฅผ ํ์ฉํ๋ค๋ ์๋ฏธ์ธ๋ฐ, ์ด๋ ๊ฒ ๋ง๋ค์ด์ง ๋ฐ์ดํฐ๊ฐ ์บ์์ ์ ์ฅ๋๊ธฐ ๋๋ฌธ์ readOne ๋ฉ์๋๋ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ์ฉํ ์ ์๋ค.
@CacheEvict
update ๋ฉ์๋์ @CachePut๊ณผ @CacheEvict๋ฅผ ์ถ๊ฐ
@CachePut(cacheNames = "itemCache", key = "args[0]")
@CacheEvict(cacheNames = "itemAllCache", allEntries = true) // allEntries = true ๋์ key = "readAll" ์ฒ๋ผ ์ง์ ํ ์ ์๋ค.
public ItemDto update(Long id, ItemDto dto) {
Item item = itemRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
item.setName(dto.getName());
item.setDescription(dto.getDescription());
item.setPrice(dto.getPrice());
item.setStock(dto.getStock());
return ItemDto.fromEntity(itemRepository.save(item));
}
@CacheEvict๋ ์ฃผ์ด์ง ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์ ์ฅ๋ ์บ์๋ฅผ ์ง์์ค๋ค.
key๋ฅผ ํตํด์ ๋ช ํํ ํ๋์ ์บ์๋ฅผ ์ง์ ํ ์๋ ์์ง๋ง, ๋ชจ๋ ์์ดํ ์ ์ ์ฅํ ์บ์์ธ itemAllCache๋ฅผ ๋ชฉํ๋ก ํ๊ณ ์๋ค. ์์ดํ ์ ์ ๋ณด๊ฐ ๋ฐ๋์์ผ๋, ๋ฐ์ดํฐ๋ฅผ ์ ๋ถ ๋๋ ค์ค ๊ฒฐ๊ณผ๊ฐ ๋์ด์ ์ ํจํ์ง ์๋ค๊ณ ์ด์ผ๊ธฐ ํ๋๊ฑฐ๋ผ ์๊ฐํ ์ ์๋ค.
@EnableCaching, CacheConfig
import java.time.Duration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching // Spring Boot์ ์บ์ฑ ์ค์ ์ ํ์ฑํ
public class RedisCacheConfig {
@Bean
// ๊ฒ์๊ธ ์กฐํ์ฉ ์บ์ ๋งค๋์
public CacheManager boardCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
.defaultCacheConfig()
// Redis์ Key๋ฅผ ์ ์ฅํ ๋ String์ผ๋ก ์ง๋ ฌํ(๋ณํ)ํด์ ์ ์ฅ
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()))
// Redis์ Value๋ฅผ ์ ์ฅํ ๋ Json์ผ๋ก ์ง๋ ฌํ(๋ณํ)ํด์ ์ ์ฅ
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new Jackson2JsonRedisSerializer<Object>(Object.class)
// new GenericJackson2JsonRedisSerializer() ์ด๋ ๊ฒ ์์ฑํ๋ฉด ํด๋์ค ์ด๋ฆ๊น์ง ์ ์ฅ๋๋ค.
)
)
// ๋ฐ์ดํฐ์ ๋ง๋ฃ๊ธฐ๊ฐ(TTL) ์ค์
.entryTtl(Duration.ofMinutes(1L));
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}
RedisConfig
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
// Lettuce: Redis์ ์ฐ๊ฒฐ์ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
// Lettuce๋ฅผ ํ์ฉํด์ redis์ ์ฐ๊ฒฐ์ ๊ด๋ฆฌํ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ
// Redis ์๋ฒ์ ๋ํ ์ ๋ณด(host, port)๋ฅผ ์ค์ ํ๋ค.
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
}
}
Hibernate: select b1_0.id,b1_0.content,b1_0.created_at,b1_0.title from boards b1_0 order by b1_0.created_at desc limit ?
Hibernate: select count(b1_0.id) from boards b1_0
2024-09-10T03:30:46.328+09:00 TRACE 19744 --- [nio-8080-exec-1] o.s.cache.interceptor.CacheInterceptor : Creating cache entry for key 'boards:page:1:size:10' in cache(s) [getBoards]
2024-09-10T03:31:31.547+09:00 TRACE 19744 --- [nio-8080-exec-2] o.s.cache.interceptor.CacheInterceptor : Computed cache key 'boards:page:1:size:10' for operation Builder[public java.util.List com.rediscaching.api.service.BoardService.getBoards(int,int)] caches=[getBoards] | key=''boards:page:' + #page + ':size:' + #size' | keyGenerator='' | cacheManager='boardCacheManager' | cacheResolver='' | condition='' | unless='' | sync='false'
2024-09-10T03:31:31.581+09:00 TRACE 19744 --- [nio-8080-exec-2] o.s.cache.interceptor.CacheInterceptor : Cache entry for key 'boards:page:1:size:10' found in cache(s) [getBoards]
Cache entry๋ ์บ์๊ฐ ์์๋ค๋ ์๋ฏธ๋ค.
value๋ ์๋์ ๊ฐ์ด ์ ์ฅ๋๋ค.
[{"id":795007,"title":"Title0795007","content":"Content0795007","createdAt":"2024-09-10T03:33:52"},{"id":435766,"title":"Title0435766","content":"Content0435766","createdAt":"2024-09-10T03:18:06"},{"id":433560,"title":"Title0433560","content":"Content0433560","createdAt":"2024-09-10T03:15:42"},{"id":17098,"title":"Title0017098","content":"Content0017098","createdAt":"2024-09-10T03:10:25"},{"id":790848,"title":"Title0790848","content":"Content0790848","createdAt":"2024-09-10T03:09:18"},{"id":940990,"title":"Title0940990","content":"Content0940990","createdAt":"2024-09-10T03:07:36"},{"id":132154,"title":"Title0132154","content":"Content0132154","createdAt":"2024-09-10T02:35:47"},{"id":120837,"title":"Title0120837","content":"Content0120837","createdAt":"2024-09-10T02:33:11"},{"id":873326,"title":"Title0873326","content":"Content0873326","createdAt":"2024-09-10T02:31:34"},{"id":441443,"title":"Title0441443","content":"Content0441443","createdAt":"2024-09-10T02:28:40"}]
๋๋ต 10๋ฐฐ์ ๋ ํฅ์
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> keys *
1) "getBoards::boards:page:1:size:10"
127.0.0.1:6379> get getBoards::boards:page:1:size:10
"[{\"id\":740344,\"title\":\"Title0740344\",\"content\":\"Content0740344\",\"createdAt\":\"2024-09-09T16:33:26\"},{\"id\":24559,\"title\":\"Title0024559\",\"content\":\"Content0024559\",\"createdAt\":\"2024-09-09T16:26:01\"},{\"id\":8406,\"title\":\"Title0008406\",\"content\":\"Content0008406\",\"createdAt\":\"2024-09-09T16:22:46\"},{\"id\":301583,\"title\":\"Title0301583\",\"content\":\"Content0301583\",\"createdAt\":\"2024-09-09T16:22:43\"},{\"id\":595094,\"title\":\"Title0595094\",\"content\":\"Content0595094\",\"createdAt\":\"2024-09-09T16:21:38\"},{\"id\":244583,\"title\":\"Title0244583\",\"content\":\"Content0244583\",\"createdAt\":\"2024-09-09T16:19:29\"},{\"id\":274110,\"title\":\"Title0274110\",\"content\":\"Content0274110\",\"createdAt\":\"2024-09-09T16:17:33\"},{\"id\":256964,\"title\":\"Title0256964\",\"content\":\"Content0256964\",\"createdAt\":\"2024-09-09T16:13:56\"},{\"id\":395993,\"title\":\"Title0395993\",\"content\":\"Content0395993\",\"createdAt\":\"2024-09-09T16:07:12\"},{\"id\":961766,\"title\":\"Title0961766\",\"content\":\"Content0961766\",\"createdAt\":\"2024-09-09T16:01:57\"}]"
127.0.0.1:6379> ttl getBoards::boards:page:1:size:10
(integer) 29
AWS ์ฌ์ฉํ๊ธฐ
๐ AWS ๋น์ฉ
EC2
- EC2 ์ธ์คํด์ค (t3a.small) : ์๊ฐ๋น 0.026 USD (24์๊ฐ๋น ์ฝ 800์)
- ๋ฐ์ดํฐ ์ ์ก ๋น์ฉ : 1 GB๋น 0.1368 USD (1GB๋น ์ฝ 200์)
(์ค์ต ๊ณผ์ ๋์ 1GB ์ดํ์ ๋ฐ์ดํฐ๋ง ์ ์ก)
- Public IPv4 ๋น์ฉ : ์๊ฐ๋น 0.005 USD (24์๊ฐ๋น ์ฝ 200์)
RDS
- RDS ์ธ์คํด์ค (t3.micro) : ์๊ฐ๋น 0.026 USD (24์๊ฐ๋น ์ฝ 800์)(ํ๋ฆฌํฐ์ด์ผ ๊ฒฝ์ฐ ์ 750์๊ฐ๊น์ง ๋ฌด๋ฃ)
- ์คํ ๋ฆฌ์ง ๋น์ฉ : GB-์๋น 0.131 USD (20GB-24์๊ฐ๋น ์ฝ 200์)(ํ๋ฆฌํฐ์ด์ผ ๊ฒฝ์ฐ 20GB๊น์ง ๋ฌด๋ฃ)
- Public IPv4 ๋น์ฉ : ์๊ฐ๋น 0.005 USD (24์๊ฐ๋น ์ฝ 200์)
ElastiCache
- ์บ์ (cache.t3micro) : ์๊ฐ๋น 0.025 USD (24์๊ฐ๋น ์ฝ 800์)(ํ๋ฆฌํฐ์ด์ผ ๊ฒฝ์ฐ ์ 750์๊ฐ๊น์ง ๋ฌด๋ฃ)
EC2 ์ธํ
RDB ์ธํ
๋ณด์๊ทธ๋ฃน ์ธ๋ฐ์ด๋ ๊ท์น ํ์ฉ ํ์
EC2์ Redis ์ค์น
1. Redis ์ค์น
$ sudo apt update
$ sudo apt install redis
2. ์ค์น ํ์ธ
$ redis-cli
127.0.0.1:6379> ping
PONG
Spring Boot ์ ํ
$ sudo apt install openjdk-17-jdk
$ java -version
application ํ์ผ ์์
# local ํ๊ฒฝ
spring:
profiles:
default: local
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
data:
redis:
host: localhost
port: 6379
logging:
level:
org.springframework.cache: trace
---
# prod ํ๊ฒฝ
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://{rds ์ฃผ์}:3306/mydb
username: admin
password: password
rds ์ฃผ์๋ rds์ ์๋ํฌ์ธํธ
# git clone
$ git clone {Github Repository ์ฃผ์}
$ cd {ํ๋ก์ ํธ ๊ฒฝ๋ก}
# ์๋ฒ ์คํ ์คํ๋ง ํ๋ก์ ํธ ๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ์ ์๋ ๋ช
๋ น์ด ์คํ
$ ./gradlew clean build -x test # ์๋๋ฉด chmod +x gradlew
$ cd build/libs
$ java -jar -Dspring.profiles.active=prod {๋น๋๋ jar ํ์ผ๋ช
} # plain๋ง๊ณ
github password๋ ํ ํฐ ์ฌ์ฉํจ
๋ถํ ํ ์คํธ
์๋น์ค๋ฅผ ๋ฐฐํฌํ๊ธฐ ์ ์ ๋ฐฑ์๋ ์๋ฒ๊ฐ ์ด๋ ์ ๋์ ์์ฒญ์ ๊ฒฌ๋ ์ ์๋ ์ง ๋ถํ ํ ์คํธ๋ฅผ ํด๋ด์ผ ํ๋ค.
๐ Throughput
๋ถํ ํ ์คํธ์์ ์๋น์ค๊ฐ 1์ด๋น ์ฒ๋ฆฌํ ์ ์๋ ์์ ๋
- ๋จ์: TPS(Transaction Per Seconds, 1์ด๋น ์ฒ๋ฆฌํ ํธ๋์ญ์ ์ ์)
์๋น์ค๊ฐ 1์ด์ ์ต๋ 100๊ฐ์ API ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ค๋ฉด, ์ด ์๋น์ค์ Throughput์ 100 TPS
K6
์ฑ๋ฅ ๋น๊ต๋ฅผ ์ํด k6๋ผ๋ ๋ถํํ ์คํธ ํด์ ์ฌ์ฉํ ๊ฒ์ด๋ค.
๋ถํํ ์คํธ ํด์๋ k6 ์ด์ธ์๋ ngrinder, jmeter, ab, locust ๋ฑ ๋ค์ํ ํด์ด ์๋ค.
๊ทธ ์ค ๊ฐ๋จํ๊ณ ๋น ๋ฅด๊ฒ ํ ์คํธ ํด๋ณผ ์ ์๋ ํด์ธ k6๋ฅผ ํ์ฉํ๊ณ ์ ํ๋ค.
https://grafana.com/docs/k6/latest/set-up/install-k6/#fedoracentos
k6 ์ค์น ๋ฐฉ๋ฒ
๊ณต์๋ฌธ์ : https://k6.io/docs/get-started/installation/
- Windows - ์ค์น ๋ฐฉ๋ฒ 1
- ์๋ ์์์ ์ฐธ๊ณ ํด์ ์ค์นํด๋ณด๋๋ก ํ์. k6 ํ๋ก๊ทธ๋จ์ ์ง์ ๋ค์ด๋ฐ์์ ์ค์นํ๋ ๋ฐฉ๋ฒ์ด๋ค.
- https://www.youtube.com/watch?v=eVmcDt5C8io&feature=youtu.be
- Windows - ์ค์น ๋ฐฉ๋ฒ 2
- Package Manager๋ฅผ ํ์ฉํด์ ์ค์นํ๋ ๋ฐฉ๋ฒ์ด๋ค.
- https://yscho03.tistory.com/101#google_vignette
์์ฒญ์ ์ด๋ป๊ฒ ๋ณด๋ผ ๊ฒ์ธ์ง์ ๋ํ ์คํฌ๋ฆฝํธ ์ ์ด์ค์ผ ํ๋ค.
์ด๋ฒ์๋ ๋ฐฑ์คํผ์ค๋ก ์๋ฒ๋ฅผ ์คํ์์ผ ํ ์คํธ ํด๋ณด์. ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ์ ์์ผ์ผ ์ถ๋ ฅ ํ๋ฉด์ ๋์ฐ์ง ์์ ๋ ์ ํํ ์ฑ๋ฅ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
# ์คํ๋ง ํ๋ก์ ํธ ๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ์ ์๋ ๋ช
๋ น์ด ์คํ
$ ./gradlew clean build -x test
# ์ ํํ ํ
์คํธ๋ฅผ ์ํด Spring Boot ์๋ฒ๋ฅผ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ
$ cd build/libs
$ nohup java -jar -Dspring.profiles.active=prod {๋น๋๋ jar ํ์ผ๋ช
} &
# 8080๋ฒ ํฌํธ์ Spring Boot ์๋ฒ๊ฐ ์ ์คํ๋๊ณ ์๋ ์ง ํ์ธ
$ lsof -i:8080
k6 script.js ์์ฑ
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
http.get('http://{EC2 IP ์ฃผ์}:8080/boards');
}
์คํ
# K6์ ์คํฌ๋ฆฝํธ ํ์ผ์ด ์์นํ ๊ฒฝ๋ก์์ ์๋ ๋ช
๋ น์ด ์คํ์ํค๊ธฐ
$ k6 run --vus 30 --duration 10s script.js
- --vus 30 : ๊ฐ์ ์ ์ (Virtual Users)๋ฅผ 30๋ช ์ผ๋ก ์ ํ (API ์์ฒญ์ ๋ณด๋ด๋ ์ฌ์ฉ์๊ฐ 30๋ช ์ธ ๊ฒ์ฒ๋ผ ๋ถํ ์์ฑ)
- --duration 30s : 30์ด ๋์ ํ ์คํธ๋ฅผ ์ ์ง
๊ฒฐ๊ณผ
ํ์ฌ ๊ตฌ์ถํ ์๋น์ค์์ ๊ฒ์๊ธ ์กฐํ API์ throughput์ 1.585228์ด๋ค.
ํ๊ท ์ ์ผ๋ก 1์ด์ ์ฒ๋ฆฌํ ์ ์๋ ์์ฒญ์ 1.585228๊ฐ ์ด๋ค.
redis cache๋ฅผ ์ ์ฉํ ์๋น์ค์์ ๊ฒ์๊ธ ์กฐํ API์ throughput์ 44.355323์ด๋ค.
ํ๊ท ์ ์ผ๋ก 1์ด์ ์ฒ๋ฆฌํ ์ ์๋ ์์ฒญ์ 44.355323๊ฐ ์ด๋ค.
Docker Compose
Dockerfile
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
compose.yml
services:
api-server:
build: . # ๋์ปคํ์ผ์ ๊ธฐ์ค์ผ๋ก ๋น๋๋ฅผ ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ Dockerfile ๊ฒฝ๋ก๋ฅผ ๋ํ๋ด์ค๋ค.
ports:
- 8080:8080
depends_on:
cache-server:
condition: service_healthy
# spring boot ์๋ฒ๊ฐ ์บ์ ์๋ฒ์ ์ฐ๊ฒฐ์ ํด์ผํ๊ธฐ ๋๋ฌธ์
# ์บ์ ์๋ฒ๊ฐ ๋์ด์ ธ์์ง ์๋ค๋ฉด ์๋ฌ๋ฅผ ๋์
cache-server:
image: redis
ports:
- 6379:6379
healthcheck: # redis๊ฐ ์ ์คํ๋์๋์ง ์กฐ๊ฑด
test: [ "CMD", "redis-cli", "ping" ]
interval: 5s
retries: 10
application.yml
# local ํ๊ฒฝ
spring:
profiles:
default: local
datasource:
url: jdbc:mysql://host.docker.internal:3306/mydb # host ์ปดํจํฐ ์ฃผ์
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
data:
redis:
host: cache-server // compose-yml์ ์๋ ์ด๋ฆ๊ณผ ๋์ผํ๊ฒ
port: 6379
logging:
level:
org.springframework.cache: trace
---
# prod ํ๊ฒฝ
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://instagram-db.coseefawhrzc.ap-northeast-2.rds.amazonaws.com:3306/mydb
username: admin
password: password
์คํ๋ง ๋ถํธ๊ฐ ์คํ๋๋๊ฑด ์ปจํ ์ด๋ ์์์์ ์ฃผ์๋ค.
ํด๋น ์ปจํ ์ด๋ ์์๋ MySQL์ด ์์ด localhost๋ผ๊ณ ์ ์ผ๋ฉด ์ฐ๊ฒฐ์ ๋ชปํ๋ค.
host.doccker.internal๋ก ์ ์ด์ ํธ์คํธ ์ปดํจํฐ์ ์ฃผ์๋ฅผ ๋ปํ๋๋ก ํ๋ค.
Docker ์ปจํ ์ด๋๋ก ๋์ฐ๊ธฐ
$ ./gradlew clean build -x test
$ docker compose up --build -d
$ docker ps # ์ ๋์์ก๋ ์ง ํ์ธ
$ docker compose logs -f # ์ค์๊ฐ ๋ก๊ทธ ํ์ธํ๊ธฐ
EC2์ Docker Compose๋ก ๋์ฐ๊ธฐ
Dockerfile-prod
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/app.jar"]
- -Dspring.profiles.active=prod
Compose-prod.yml
services:
api-server:
build:
context: .
dockerfile: ./Dockerfile-prod
ports:
- 8080:8080
depends_on:
cache-server:
condition: service_healthy
cache-server:
image: redis
ports:
- 6379:6379
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 5s
retries: 10
Git Pull
$ cd {ํ๋ก์ ํธ ๊ฒฝ๋ก}
$ git pull origin main
EC2์ Docker ์ค์น
$ sudo apt-get update && \
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common && \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
sudo apt-key fingerprint 0EBFCD88 && \
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \
sudo apt-get update && \
sudo apt-get install -y docker-ce && \
sudo usermod -aG docker ubuntu && \
newgrp docker && \
sudo curl -L "https://github.com/docker/compose/releases/download/2.27.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose && \
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
$ docker -v # Docker ๋ฒ์ ํ์ธ
$ docker compose version # Docker Compose ๋ฒ์ ํ์ธ
๊ธฐ์กด ์คํ ์ค์ธ Redis, Spring Boot ์ข ๋ฃ
# Redis ์ค์ง
$ sudo systemctl stop redis
$ sudo systemctl status redis # ์ ์ข
๋ฃ๋๋ ์ง ํ์ธ
# Spring Boot ์ข
๋ฃ
$ sudo lsof -i:8080 # 8080๋ฒ ํฌํธ ์คํ๋๊ณ ์๋ ํ๋ก์ธ์ค ํ์ธ
$ kill {Spring Boot์ PID} # ํ๋ก์ธ์ค ์ข
๋ฃ
$ sudo lsof -i:8080 # ์ ์ข
๋ฃ๋๋ ์ง ํ์ธ
Docker ์ปจํ ์ด๋๋ก ๋์ฐ๊ธฐ
$ ./gradlew clean build -x test
$ docker compose -f compose-prod.yml up --build -d
$ docker ps # ์ ๋์์ก๋ ์ง ํ์ธ
$ docker compose logs -f # ์ค์๊ฐ ๋ก๊ทธ ํ์ธํ๊ธฐ
'database > redis' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Redis][Spring Boot] Redis Keyspace Notification ๊ธฐ๋ฅ ํ์ฉํ์ฌ redis key ๋ง๋ฃ ๊ฐ์งํ๊ธฐ (1) | 2024.11.22 |
---|---|
[Redis][Spring][Exception] RedisConnectionException (0) | 2024.04.16 |
Redis ์ฌ์ฉํ๊ธฐ (Windows) (1) | 2024.02.11 |