
๐ฆ 1ํธ. Spring Boot ์ฑ๋ฅ ํ๋ — ์ด์ ์๋ฒ์์ ์ค์ ๋ก ํจ๊ณผ ๋ณธ ๋ฐฉ๋ฒ
์ด์ ์๋น์ค์์ ์ฑ๋ฅ ๋ฌธ์ ๋ ๋ณดํต ์๊ณ ์์ด ์ฐพ์์จ๋ค.
์ด๋ ๋ , ํน์ ์๊ฐ๋์ API ์๋ต์ด ๊ฐ์๊ธฐ ํ๊ท 100ms → 2~3์ด๊น์ง ์น์๋ ์ด์๊ฐ ๋ฐ์ํ๋ค.
๋จ์ ํธ๋ํฝ ์ฆ๊ฐ๋ก ๋ณด๊ธฐ์ ์์น๊ฐ ๋๋ฌด ์ปธ๋ค. ์ด์ ์งํ๋ฅผ ํ์ธํ๊ธฐ ์ํด APM์ ์ด์ด๋ณด๋, ๊ฐ์ฅ ๋จผ์ ๋์ ๋ ๊ฒ์ DB Connection Pool ๋๊ธฐ ์๊ฐ ์ฆ๊ฐ์๋ค. HikariCP ๋ชจ๋ํฐ๋ง ๊ทธ๋ํ๊ฐ ์ ํํ ํผํฌ ์๊ฐ๋์ ๊ธ๊ฒฉํ ์์นํ๊ณ ์์๊ณ , ๊ทธ ์๊ฐ ์ ์ฒด API์ ์๋ต ์๊ฐ์ด ํจ๊ป ๋์ด๋ฌ๋ค.
์ด๋ฐ ๋ฌธ์ ๋ ๊ฐ๋ฐ·ํ ์คํธ ํ๊ฒฝ์์๋ ์ ๋๋ฌ๋์ง ์๊ธฐ ๋๋ฌธ์ ์ด์ ํ๊ฒฝ์์๋ง ์ฌํ๋๋ ์ผ์ด์ค๊ฐ ๋ง๋ค. ์ค๋์ ์ค์ ์ด์์์ ๊ฒช์ ์ด ์ด์๋ฅผ ๊ธฐ์ค์ผ๋ก Spring Boot ์ฑ๋ฅ ํ๋ ๊ณผ์ ์ ์์ธํ ์ ๋ฆฌํด๋ณธ๋ค.
โ ๋ฌธ์ 1: HikariCP ์ปค๋ฅ์ ํ ๊ณ ๊ฐ
โ ์ด๋ค ํ์์ด ๋ฐ์ํ๋?
- ์ฐ๊ฒฐ์ ๋น๋ ค์ค๋ ๋ฐ๋ง 500ms ์ด์ ๊ฑธ๋ฆผ
- maxPoolSize ๋ ์ถฉ๋ถํด ๋ณด์ด๋๋ฐ๋ ๊ณ์ ํ ๋ถ์กฑ์ด ๋ฐ์
- ํน์ ์๊ฐ๋์๋ง ๋ฐ๋ณต์ ์ผ๋ก ๋ฐ์
HikariCP ๋ก๊ทธ๋ฅผ DEBUG ์์ค์ผ๋ก ์ด์ด๋ณด๋, ํธ๋ํฝ์ด ๋ชฐ๋ฆฌ๋ ์๊ฐ๋ง๋ค ๋ค์ ๋ฉ์์ง๊ฐ ๋ฐ๋ณต์ ์ผ๋ก ์ถ๋ ฅ๋๊ณ ์์๋ค.
HikariPool-1 - Pool stats (total=40, active=40, idle=0)
HikariPool-1 - Timeout failure waiting for a connection.
์ฆ ๋ชจ๋ ์ปค๋ฅ์ ์ด ์ฌ์ฉ ์ค(active=40) ์ด๊ณ , ๋ฐํ์ด ๋ฆ์ด์ง๋ฉด์ ๋๊ธฐ์ด์ด ์์ธ ๊ฒ์ด๋ค.
โ ์์ธ ๋ถ์
1) DB ์ค์ ๊ณผ HikariCP maxLifetime ๋ถ์ผ์น
์ด์ DB(MySQL, MariaDB ๋ฑ)๋ ๋ณดํต wait_timeout, interactive_timeout ์ค์ ๊ฐ์ด ์๋ค.
์ด ๊ฐ๋ณด๋ค ๋ ์ค๋ ์ด์ ์๋ ์ปค๋ฅ์
์ DB๊ฐ ๋จผ์ ๋์ด๋ฒ๋ฆฐ๋ค.
๊ทธ๋ฐ๋ฐ HikariCP๋ DB๊ฐ ์ปค๋ฅ์
์ ๋์ ์ค ๋ชจ๋ฅด๊ณ ๊ณ์ ์ฌ์ฉํ๋ ค๋ค Dead Connection ์ ๋ฐ๊ฒ ๋๊ณ , ์ด๋ก ์ธํด ์ฌ์๋ ๋ฐ ๋๊ธฐ๊ฐ ๋ฐ์ํ๋ค.
ํ ์คํธ ๊ฒฐ๊ณผ:
- DB wait_timeout: 1800์ด(30๋ถ)
- Hikari maxLifetime: 1800000ms(30๋ถ) (๊ธฐ๋ณธ๊ฐ)
์ฆ ๋์ด ์ ํํ ๊ฐ์ ๊ฐ์ด๋ผ DB๊ฐ ๋์ด๋ฒ๋ฆฌ๋ ํ์ด๋ฐ๊ณผ ๊ฑฐ์ ๋์ผํ๋ค.
2) ํธ๋ํฝ ํญ์ฃผ ๊ตฌ๊ฐ์์ ์ธ์ ์ด ์ค๋ ์ ์ง๋จ
ํน์ API๋ ๋ค์ ๋ฌด๊ฑฐ์ด ์กฐํ ์ฟผ๋ฆฌ๋ฅผ ์ํํ๊ณ ์์๊ณ , ์ด API๊ฐ ๋์ ๋ค๋ฐ์ ์ผ๋ก ํธ์ถ๋๋ฉด์ ์ปค๋ฅ์ ์ ์ ์๊ฐ์ด ๊ธธ์ด์ก๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก:
- ํ์์์ ๋ฌธ์ ์์
- ํผํฌ ํ์์๋ง ์ ์ ๋๊ธฐ ์ฆ๊ฐ → ์ ์ฒด API ์ง์ฐ
โ ํด๊ฒฐ ๋ฐฉ๋ฒ
DB ์ค์ ์ ๋จผ์ ํ์ธํ๊ณ , ์ด๋ฅผ ๊ธฐ์ค์ผ๋ก HikariCP ์ค์ ์ ์กฐ์ ํ๋ค.
spring:
datasource:
hikari:
maximum-pool-size: 40
minimum-idle: 10
max-lifetime: 1700000 # DB wait_timeout ์ ์ฝ 90%๋ก ๋ง์ถค
connection-timeout: 3000
โ ์ค์ ์ ์ฉ ํ ํจ๊ณผ
- ๋ถ๊ท์นํ๋ dead connection ์ด ์ฌ๋ผ์ง
- active pool ๊ทธ๋ํ๊ฐ ์์ ์ ์ผ๋ก ์ ์ง๋จ
- ํธ๋ํฝ ํผํฌ ์๊ฐ๋ API ์๋ต 2~3์ด → 110~140ms๋ก ํ๋ณต
- APM DB ๋๊ธฐ ๊ตฌ๊ฐ ์ง์ฐ์ด ๊ฑฐ์ 0์ ๊ฐ๊น๊ฒ ๊ฐ์
์ฆ HikariCP๊ฐ ์ปค๋ฅ์ ์ ๋จผ์ ์ ๋ฆฌํ๊ณ ์ฌ์์ฑํ๋๋ก ๋ง๋๋ ๊ฒ๋ง์ผ๋ก๋ ํฐ ํจ๊ณผ๋ฅผ ๋ณด์๋ค.
โ ์ค์ ํ 3๊ฐ (์ด์ ํ๊ฒฝ ๊ธฐ์ค)
1. maxLifetime ์ ๋ฐ๋์ DB wait_timeout ์ 90%๋ก ์ค์ ํ๋ค
- DB๊ฐ ๋จผ์ ์ฐ๊ฒฐ์ ๋์ด๋ฒ๋ฆฌ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฅ์ ๋ฅผ ๋ง๋๋ค.
- ์ปค๋ฅ์ ์ “DB๊ฐ ๋๊ธฐ๊ธฐ ์ ์” HikariCP๊ฐ ๋จผ์ ์ ๊ฑฐํด์ผ ํ๋ค.
2. idleTimeout ์ maxLifetime ๋ณด๋ค ํญ์ ์์์ผ ํ๋ค
- idle ์ํ์ ์ปค๋ฅ์ ๋ ์ ์ ํ ์ฌ์์ฑ๋๋๋ก ํด์ผ ํ๋ค.
- ์ผ๋ฐ์ ์ผ๋ก idleTimeout ≈ maxLifetime ์ 50~60% ์์ค์ด ์์ ์ ์ด๋ค.
3. ํ ๋ถ์กฑ์ด๋ผ๊ณ ๋ฌด์กฐ๊ฑด maximum-pool-size ๋ฅผ ๋๋ฆฌ์ง ๋ง๋ผ
๋๋ถ๋ถ์ ๊ฒฝ์ฐ root cause ๋ ๋ค์ ์ค ํ๋๋ค:
- ๋๋ฆฐ SQL
- ๋๋ JOIN
- ๋ถํ์ํ ์ฟผ๋ฆฌ ํธ์ถ
- ํธ๋์ญ์ ๋ฒ์ ๊ณผ๋ํ๊ฒ ํผ
์ฆ ํ์ ๋๋ฆฌ๋ ๊ฒ์ ์ตํ์ ์๋จ์ด๊ณ , SQL ํ๋์ด ์คํ๋ ค ๋ ํฐ ํจ๊ณผ๋ฅผ ๋ง๋ ๋ค.
๐งฉ ๋ง๋ฌด๋ฆฌ
Spring Boot ์ฑ๋ฅ ํ๋์ ๋จ์ํ ์ค์ ๊ฐ์ ๋ฐ๊พธ๋ ๊ฒ์ด ์๋๋ผ
ํ๊ฒฝ ์ ์ฒด(DB ์ค์ → Hikari ์ค์ → SQL → API ์ค๊ณ) ๋ฅผ ํจ๊ป ๊ณ ๋ คํด์ผ ํ๋ค.
์ด๋ฒ ๊ธ์ ๊ทธ์ค์์๋ ๊ฐ์ฅ ๋ง๊ณ ์ค์ ์ด์์์ ์์ฃผ ๋ฐ์ํ๋ “HikariCP ์ปค๋ฅ์ ํ ๊ณ ๊ฐ” ๋ฌธ์ ๋ฅผ ๋ค๋ค๋ค. ๋ค์ ๊ธ์์๋ Jackson ์ง๋ ฌํ ํ๋, ThreadPool ์กฐ์ ๋ฑ ๋ ๋ค๋ฅธ ์ค์ ๊ฒฝํ์ ๊ธฐ๋ฐ์ผ๋ก ๋ ๊น์ ๋ด์ฉ์ ๋ค๋ฃฐ ์์ ์ด๋ค.
๐ TAGS
Java, SpringBoot, PerformanceTuning, HikariCP, DatabaseOptimization, ProductionIssue, BackendEngineering, RealWorldTips
- 1ํธ — ์ด์ ์๋ฒ์์ ์ค์ ๋ก ํจ๊ณผ ๋ณธ ๋ฐฉ๋ฒ (ํ์ฌ ๊ธ)
- 2ํธ — Jackson ์ง๋ ฌํ ํ๋: ์จ์ CPU ๋ณ๋ชฉ ํด๊ฒฐ
- 3ํธ — ThreadPool & Tomcat ํ๋: ํธ๋ํฝ ํญ์ฃผ ๋์
- 4ํธ — DB ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง๊ณผ SQL ํ๋ ์ฌ๋ก
- 5ํธ — ์ด์ ์ฅ์ ์ฌ๋ก 10์ + ์ฅ์ ๋ณต๊ตฌ ํ๋ ์ด๋ถ