<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Datasource on kastori</title><link>http://blog.kastori.dev/tags/datasource/</link><description>Recent content in Datasource on kastori</description><generator>Hugo -- gohugo.io</generator><language>ko-kr</language><lastBuildDate>Sat, 30 May 2026 00:00:00 +0900</lastBuildDate><atom:link href="http://blog.kastori.dev/tags/datasource/index.xml" rel="self" type="application/rss+xml"/><item><title>[DB 완전 정복 #5] HikariCP와 커넥션 풀 — 설정 하나가 서비스를 멈춘다</title><link>http://blog.kastori.dev/tech/2026-05-30-db-05-connection-pool/</link><pubDate>Sat, 30 May 2026 00:00:00 +0900</pubDate><guid>http://blog.kastori.dev/tech/2026-05-30-db-05-connection-pool/</guid><description>&lt;h2 id="요청-1000개에-커넥션-1000개를-만들면"&gt;&lt;a href="#%ec%9a%94%ec%b2%ad-1000%ea%b0%9c%ec%97%90-%ec%bb%a4%eb%84%a5%ec%85%98-1000%ea%b0%9c%eb%a5%bc-%eb%a7%8c%eb%93%a4%eb%a9%b4" class="header-anchor"&gt;&lt;/a&gt;요청 1000개에 커넥션 1000개를 만들면?
&lt;/h2&gt;&lt;p&gt;트래픽이 급증하는 상황을 상상해보자. 매 요청마다 DB 커넥션을 새로 만들면 어떻게 될까.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DB 커넥션 생성 과정:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1. TCP 소켓 연결 (3-way handshake)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 2. DB 서버 인증 (사용자/비밀번호 검증)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 3. 세션 초기화 (인코딩, 타임존, 옵션 설정)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 수십 ~ 수백 ms 소요
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 초당 1000 요청이면 커넥션 생성만으로 서버 과부하
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;커넥션 풀은 이 문제를 미리 만들어 두고 재사용하는 방식으로 해결한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hikaricp--spring-boot의-기본-커넥션-풀"&gt;&lt;a href="#hikaricp--spring-boot%ec%9d%98-%ea%b8%b0%eb%b3%b8-%ec%bb%a4%eb%84%a5%ec%85%98-%ed%92%80" class="header-anchor"&gt;&lt;/a&gt;HikariCP — Spring Boot의 기본 커넥션 풀
&lt;/h2&gt;&lt;p&gt;Spring Boot 2.0부터 HikariCP가 기본이다. &amp;ldquo;세상에서 가장 빠른 커넥션 풀&amp;quot;이라는 별명이 있다. 별도 설정 없이도 동작하지만, 기본값이 모든 상황에 맞지 않다는 게 함정이다.&lt;/p&gt;
&lt;h3 id="동작-원리"&gt;&lt;a href="#%eb%8f%99%ec%9e%91-%ec%9b%90%eb%a6%ac" class="header-anchor"&gt;&lt;/a&gt;동작 원리
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;애플리케이션 시작
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 미리 커넥션 N개 생성해서 풀에 보관
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP 요청 처리
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 풀에서 유휴 커넥션 꺼내기 (borrow)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → DB 작업 수행
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 커넥션 반납 (close가 아님, return)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;풀이 비어있을 때
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → connectionTimeout 동안 대기
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 초과 → SQLTimeoutException
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;핵심은 커넥션을 &lt;strong&gt;닫지 않고 반납&lt;/strong&gt;한다는 것이다. 코드에서 &lt;code&gt;connection.close()&lt;/code&gt;를 호출해도 실제로 닫히지 않고 풀로 돌아간다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="풀-사이즈-공식--너무-크면-오히려-느리다"&gt;&lt;a href="#%ed%92%80-%ec%82%ac%ec%9d%b4%ec%a6%88-%ea%b3%b5%ec%8b%9d--%eb%84%88%eb%ac%b4-%ed%81%ac%eb%a9%b4-%ec%98%a4%ed%9e%88%eb%a0%a4-%eb%8a%90%eb%a6%ac%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;풀 사이즈 공식 — 너무 크면 오히려 느리다
&lt;/h2&gt;&lt;p&gt;직관적으로는 커넥션이 많을수록 좋을 것 같다. 하지만 커넥션이 너무 많으면 DB 서버 자체가 부하를 받는다.&lt;/p&gt;
&lt;p&gt;HikariCP 공식 문서가 제안하는 공식:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;maximumPoolSize = (CPU 코어 수 × 2) + 유효 디스크 수
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;예: CPU 4코어 + SSD 1개 = &lt;strong&gt;9개&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;놀랍도록 작은 숫자처럼 보인다. 하지만 실제로 DB 연산은 CPU와 I/O를 번갈아 쓰므로, 코어당 2개가 적절하다. 그 이상은 Context Switch 오버헤드만 늘어난다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# application.yml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hikari&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;maximum-pool-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 최대 커넥션 수&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;minimum-idle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 유지할 최소 유휴 커넥션 수&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;connection-timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 커넥션 대기 최대 시간 (ms)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;idle-timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;600000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 유휴 커넥션 유지 시간 (ms)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max-lifetime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1800000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 커넥션 최대 수명 (ms)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="max-lifetime-주의사항"&gt;&lt;a href="#max-lifetime-%ec%a3%bc%ec%9d%98%ec%82%ac%ed%95%ad" class="header-anchor"&gt;&lt;/a&gt;max-lifetime 주의사항
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;max-lifetime&lt;/code&gt;은 DB 서버의 &lt;code&gt;wait_timeout&lt;/code&gt;보다 반드시 짧게 설정해야 한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DB 서버 wait_timeout: 8시간 (기본값)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;max-lifetime: 30분 (권장)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ DB가 연결을 끊기 전에 풀에서 먼저 커넥션을 교체
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;max-lifetime&lt;/code&gt;이 &lt;code&gt;wait_timeout&lt;/code&gt;보다 길면, DB가 이미 닫은 커넥션을 풀이 계속 들고 있다가 사용 시점에 &lt;code&gt;Connection is closed&lt;/code&gt; 에러가 난다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="커넥션-고갈--가장-흔한-장애-원인"&gt;&lt;a href="#%ec%bb%a4%eb%84%a5%ec%85%98-%ea%b3%a0%ea%b0%88--%ea%b0%80%ec%9e%a5-%ed%9d%94%ed%95%9c-%ec%9e%a5%ec%95%a0-%ec%9b%90%ec%9d%b8" class="header-anchor"&gt;&lt;/a&gt;커넥션 고갈 — 가장 흔한 장애 원인
&lt;/h2&gt;&lt;p&gt;풀의 모든 커넥션이 사용 중이면, 새 요청은 &lt;code&gt;connectionTimeout&lt;/code&gt;까지 대기한다. 시간이 초과되면 에러다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HikariPool-1 - Connection is not available, request timed out after 30000ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="커넥션-고갈의-주요-원인"&gt;&lt;a href="#%ec%bb%a4%eb%84%a5%ec%85%98-%ea%b3%a0%ea%b0%88%ec%9d%98-%ec%a3%bc%ec%9a%94-%ec%9b%90%ec%9d%b8" class="header-anchor"&gt;&lt;/a&gt;커넥션 고갈의 주요 원인
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 트랜잭션 안에서 외부 I/O&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 커넥션 획득&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 이메일 전송 (3~5초)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 이메일 전송하는 동안 커넥션을 계속 들고 있음&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 여기서 커넥션 반납&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이메일 전송이 5초 걸리는 동안 커넥션 1개가 묶여 있다. 동시 요청이 많으면 풀이 금방 고갈된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;해결&lt;/strong&gt;: 트랜잭션 밖으로 느린 I/O를 분리한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 짧은 트랜잭션&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 트랜잭션 밖&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;completeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 짧은 트랜잭션&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. 풀 사이즈가 너무 작음&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;요청 동시성 대비 풀이 작으면 대기 큐가 쌓인다. 모니터링으로 실제 사용량을 확인하고 조정해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 긴 쿼리&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;인덱스 없는 쿼리가 10초씩 걸리면 그동안 커넥션이 묶인다. 쿼리 최적화로 해결한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="모니터링--actuator로-풀-상태-보기"&gt;&lt;a href="#%eb%aa%a8%eb%8b%88%ed%84%b0%eb%a7%81--actuator%eb%a1%9c-%ed%92%80-%ec%83%81%ed%83%9c-%eb%b3%b4%ea%b8%b0" class="header-anchor"&gt;&lt;/a&gt;모니터링 — Actuator로 풀 상태 보기
&lt;/h2&gt;&lt;p&gt;Spring Boot Actuator를 쓰면 커넥션 풀 상태를 실시간으로 볼 수 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# application.yml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;management&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;web&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;exposure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;health, metrics&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET /actuator/metrics/hikaricp.connections.active
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET /actuator/metrics/hikaricp.connections.pending
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET /actuator/metrics/hikaricp.connections.timeout
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pending&lt;/code&gt;(대기 중)이 0이 아니거나 &lt;code&gt;timeout&lt;/code&gt;이 발생하기 시작하면 풀 사이즈 조정 또는 쿼리 최적화를 검토해야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="마치며"&gt;&lt;a href="#%eb%a7%88%ec%b9%98%eb%a9%b0" class="header-anchor"&gt;&lt;/a&gt;마치며
&lt;/h2&gt;&lt;p&gt;커넥션 풀은 조용히 잘 동작하다가 특정 임계점을 넘으면 갑자기 서비스 전체가 멈춘다. 평소에 &lt;code&gt;maximum-pool-size&lt;/code&gt;, &lt;code&gt;max-lifetime&lt;/code&gt; 설정을 확인하고, 트랜잭션 안에 느린 I/O가 없는지 리뷰하는 것만으로도 많은 장애를 예방할 수 있다.&lt;/p&gt;
&lt;p&gt;다음 편부터는 심화 시리즈로, MySQL과 PostgreSQL의 내부 구조 차이를 다룬다.&lt;/p&gt;</description></item></channel></rss>