사전 학습
DB connection?
DB와 소프트웨어가 통신할 수 있도록 해주는 데이터 중심 프로그래밍의 핵심 개념
명령을 보내고 응답을 받으려면 연결은 필수!
DB와 애플리케이션 간 통신을 할 수 있는 수단
사용자 인증 자격 증명을 주소 지정하는 방법인 연결 문자열을 제공
⇒ [Server=sql_box; Database=Common; User ID=uid; Pwd=password]
자세한 내용을 알고 싶다면
구조
2Tier
클라이언트로서의 자바 프로그램(JSP)이 직접 데이터베이스 서버로 접근하여 데이터를 액세스하는 구조
⇒ WS + DB
3Tier
자바 프로그램과 데이터베이스 서버 중간에 미들웨어 층(WAS)을 두어, 그 미들웨어 층에게 비즈니스 로직 구현부터 트랜잭션 처리, 리소스 관리 등을 전부 맡기는 구조이다.
⇒ WS + WAS + DB
단어를 모르는 분들을 위해~
WS
Web Server, 웹 서버
GUI, 인터페이스, 웹화면 등 제공하는 서버
HTTP 프로토콜 기반 클라이언트의 요청에 따라 정적인 컨텐츠를 제공!!
동적인 컨텐츠 제공을 위해 WAS에 요청을 보내서 WAS에서 처리한 결과를 클라이언트에게 전달 기능도 수행!!
WAS
Web Application Server, WAS 서버
DB 조회 및 다양한 로직 처리 요구시 동적인 컨텐츠를 제공하기 위해 만들어진 애플리케이션 서버입니다.
HTTP를 통해 컴퓨터나 장치에 애플리케이션을 수행해주는 미들웨어(소프트웨어 엔진)입니다.
동적인 컨텐츠 제공을 위해 WAS에 요청을 보내서 WAS에서 처리한 결과를 클라이언트에게 전달 기능도 수행!!
자세한 내용을 알고 싶다면
JDBC
Java에서 사용하는 DB connection 및 SQL문 수행을 하고자 할 때 사용하는 표준 SQL 인터페이스 API
Java Database Connectivity
원래 각 DB마다 연결 방식과 통신 규격이 따로 있기 때문에 프로그램을 DB와 연결한다면, 해당 DB와 관련된 기술적 내용을 배우고 DB가 변경될 시 많은 변경 사항이 존재한다.
각 DBMS에 맞는 JDBC를 받아주게 되면 쉽게 DBMS를 변경
DBMS 종류에 상관 없이 하나의 JDBC API를 사용해서 데이터베이스 작업을 처리할 수 있다.
JDBC Driver
자바 프로그램의 요청을 DBMS가 이해할 수 있는 프로토콜로 변환해 주는 클라이언트 사이드 어댑터이다.
각각의 DBMS는 자신에게 알맞은 JDBC 드라이버를 제공하고 있다.
1️⃣ DB에 맞는 JDBC Driver 로드
2️⃣ DB 서버의 IP, ID, PW 등을 이용하여 DriverManager 클래스의 getConnection() 메소드를 사용하여 Connection 객체 생성
3️⃣ sql문을 작성 및 Connection에서 PreparedStatement객체를 받음
4️⃣ executeQuery를 수행하고 ResultSet 객체를 받아 데이터를 처리
5️⃣ 사용하였던 ResultSet, PreparedStatement, Connection을 close()
실제 사용해본 코드
@Override
public List<ArticleDto> BoardFindByAll() throws SQLException {
List<ArticleDto> result = new ArrayList<>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//1. 드라이버 연결을 위해서 DB 커넥션 객체를 얻음
conn = dbUtil.getConnection();
//2. 쿼리 수행을 위한 SQL문 및 PreparedStatement 객체 생성
StringBuilder sql = new StringBuilder();
sql.append("select article_no, title, content, article_category, email, hit, registtime, heart \n");
sql.append("from article");
pstmt = conn.prepareStatement(sql.toString());
rs = pstmt.executeQuery();
//3. 쿼리 실행 후 결과가 담긴 ResultSet 객체를 Dto형태의 리스트로 변경
while(rs.next()) {
ArticleDto articleDto = new ArticleDto();
articleDto.setArticleNo(rs.getInt("article_no"));
articleDto.setTitle(rs.getString("title"));
articleDto.setContent(rs.getString("content"));
articleDto.setArticleCategory(rs.getString("article_category"));
articleDto.setEmail(rs.getString("email"));
articleDto.setHit(rs.getInt("hit"));
articleDto.setRegistTime(rs.getDate("registtime"));
articleDto.setHeart(rs.getInt("heart"));
result.add(articleDto);
}
} finally {
dbUtil.close(rs, pstmt, conn);
}
return result;
}
DB connection Pool
왜 사용할까?
❓ 위 코드처럼 JDBC를 통해서 DB에 직접 연결해서 처리하는 경우 매번 사용자가 요청을 할 때마다 드라이버를 로드하고 커넥션 객체를 생성하여 연결하고 종료하기 때문에 매우 비효율적이다.
이런 문제를 해결하기 위해서 커넥션 풀을 사용한다.
개념
📢 웹 컨테이너(WAS)가 실행되면서 일정량의 Connection 객체를 미리 만들어서 pool에 저장했다가, 클라이언트 요청이 오면 Connection 객체를 빌려주고 해당 객체의 임무가 완료되면 다시 Connection 객체를 반납 받아서 pool에 저장하는 프로그래밍 기법이다.
Container 구동 시 일정 수의 Connection 객체를 생성하게 되며 클라이언트의 요청에 의해 애플리케이션이 DBMS 작업을 수행해야 하면, Connection Pool에서 Connection 객체를 받아와 작업을 진행한다. 이후 작업이 끝나면 Connetion Pool에 Connection 객체를 반납한다.
동작원리(HikarCP)
HikariCP는 가벼운 용량과 빠른 속도를 가지는JDBC의 커넥션 풀 프레임워크이다.
SpringBoot를 사용해 본 사람이라면 한 번쯤은 이름을 들어본 적이 있을 것이다.
SpringBoot는 커넥션 풀 관리를 위해 HikariCP를 사용한다.
스프링 같은 경우 스프링부트2.0부터 기본적으로 HikariCP를 사용한다.
Thread가 커넥션을 요청했을 때 유휴 커넥션이 존재한다면 해당 커넥션을 반환해준다.
유휴 커넥션이 존재하지 않는다면, HandOffQueue를 Polling 하면서 다른 Thread가 커넥션을 반납하기를 기다린다.
(지정한 TimeOut 시간까지 대기하다가 시간이 만료되면 예외를 던진다.)
다른 Thread가 커넥션 풀에 커넥션을 반납하면 Connection Pool이 Connection 사용 내역을 기록하고, HandOffQueue에 반납된 Connection을 삽입
HandOffQueue를 Polling 하던 Thread는 커넥션을 획득
장점
🚩 DB 접속 설정 객체를 미리 만들어 연결하여 메모리 상에 등록해 놓기 때문에 불필요한 작업(커넥션 생성, 삭제)이 사라지므로 클라이언트가 빠르게 DB에 접속이 가능하다.
DB Connection 수를 제한할 수 있어서 과도한 접속으로 인한 서버 자원 고갈 방지가 가능하다.
DB 접속 모듈을 공통화하여 DB 서버의 환경이 바뀔 경우 쉬운 유지 보수가 가능하다.
연결이 끝난 Connection을 재사용함으로써 새로 객체를 만드는 비용을 줄일 수 있다.
유의사항
동시 접속자가 많을 경우
커넥션은 한정되어 쓸 수 있는 커넥션이 반납될 때까지 대기….
그렇다고? 너무 많은 커넥션을 생성하면
⇒ 커넥션은 객체이므로 많은 메모리를 차지하게 되고, 프로그램의 성능을 떨어뜨리는 원인
따라서 사용량에 따라 적정량의 커넥션 객체를 생성
Connection Pool이 커지면 성능은 무조건 좋아질까?
대답은 NO!!!
⇒Connection의 주체는 Thread이므로 Thread와 함께 고려해야 한다.
Why?
Thread Pool 크기 < Connection Pool 크기
⇒Thread Pool에서 트랜잭션을 처리하는 Thread가 사용하는 Connection 외에 남는Connection은 실질적으로 메모리 공간만 차지하게 된다.
Thread Pool 크기와 Connection Pool 모두 크기 증가
⇒Thread 증가로 인해 더 많은 Context Switching이 발생한다.
Context Switching
하나의 프로세스가 CPU를 사용 중인 상태에서 다른 프로세스가 CPU를 사용하도록 하기 위해, 이전의 프로세스의 상태(문맥)를 보관하고 새로운 프로세스의 상태를 적재하는 작업
⇒Disk 경합 측면에서 성능 한계가 발생한다.
▶ 데이터베이스는 하드 디스크 하나 당 하나의 I/O를 처리하므로 블로킹이 발생한다.
▶ 즉, 특정 시점부터는 성능적인 증가가 Disk 병목으로 인해 미비해진다.
블로킹(Blocking)
블록(Block) : 디스크와 메모리 사이의 데이터 전송 단위(물리적 레코드)
메인 메모리와 I/O효율을 위해 몇 개의 논리적 레코드를 하나의 물리적 레코드에 저장시키는 것
이점 : 갭으로 인한 기억 공간의 낭비 감소, I/O시간 감소
단점 :
버퍼 크기만큼 주기억장치 내의 공간 감소(메모리 효율성 저하)
블록의 일부를 처리하기 위해서는 블록 전체를 전송(불필요한 블록까지 전송)
자세히 공부하고 싶으면 아래 링크 참조
Connection Pool의 크기는 얼마나 적절할까?
Hikari CP의 공식 문서에 의하면
1connections = ((coreCount) * 2) + effectiveSpindleCount)
coreCount
⇒ 서버 환경 CPU 개수
⇒core_count * 2를 하는 이유는 Context Switching 및 Disk I/O와 관련이 있다.
Why ?
▶Context Switching으로 인한 오버헤드를 고려해도 Disk I/O(혹은 DRAM이 처리하는 속도)보다 CPU 속도가 월등히 빠르다.
▶Thread가 Disk와 같은 작업에서 블로킹되는 시간에 다른 Thread의 작업을 처리할 수 있는 여유가 생기고, 여유 정도에 따라 멀티 스레드 작업을 수행이 가능하다
▶Hikari CP가 제시한 공식에서는 계수를 2로 선정하여 Thread 개수를 지정
effective_spindle_count
⇒ 기본적으로 DB 서버가 관리할 수 있는 동시 I/O 요청 수
⇒ 디스크가 16개? 16개의 I/O 요청 처리 가능!
종류
1️⃣ commons-dbcp
⇒아파치에서 제공하는 대표적인 커넥션 풀 라이브러리이다.
maxActive ≥ initialSize
⇒ 최대 커넥션 개수는 초기에 생성할 커넥션 개수와 같거나 크게 설정해야 한다.
maxActive = maxIdle
⇒ maxActive 값과 maxIdle 값은 같은 것이 바람직하다.
Ex. maxActive = 10, maxIdle = 5
커넥션을 동시에 5개는 사용하고 있는 상황 1개의 커넥션이 추가로 요청
⇒ maxActive = 10이므로 1개의 추가 커넥션을 데이터베이스에 연결한 후 pool은 비즈니스 로직으로 커넥션을 전달
pool에 커넥션 반납할 때
⇒ maxIdle = 5에 영향을 받아 커넥션을 실제로 닫아버리므로 일부 커넥션을 매번 생성했다 닫는 비용이 발생
maxActive 값은 DBMS의 설정과 애플리케이션 서버의 개수, Apache, Tomcat에서 동시에 처리할 수 있는 사용자 수 등을 고려해서 설정
커넥션을 많이 사용할 때는 maxActive 값이 충분히 크지 않다면 병목 지점이 될 수 있다.
사용 중인 커넥션이 많지 않은 시스템에서는 maxActive 값을 지나치게 작게 설정하지 않는 한 성능에 큰 영향이 없다.
DBMS에 로그인을 시도하고 있는 상태에서 무한으로 대기
⇒ 모든 커넥션이 사용 중인 상태가 되어서 새로운 요청을 처리할 수 없게 된다.
이런 상황을 모두 방지할 수 없기 때문에 장애 확산을 최소화를 하기 위해서
JDBC 드라이버에서 설정하는 loginTimeOut 속성같은 JDBC 드라이버별 타임아웃 속성을 설정하는 것이 좋다.
2️⃣ tomcat-jdbc-pool
⇒tomcat에 내장되어 사용되고 있다.
⇒Apache Commons DBCP 라이브러리를 바탕으로 만들어져 있다.
⇒Spring boot 2.0.0 하위 버전에서 사용하는 기본 DBCP이다.
3️⃣ HikariCP
⇒스프링 부트 2.0부터 default JDBC connection pool
⇒zero-overhead 소개할 정도로 가볍고 빠르다
- overhead: 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 및 메모리
⇒HikariCP의 연결 정보 외에 원하는 Connection Pool의 크기, 시간과 관련된 설정 등을 작성할 수 있다.
(application.properties에서 간단하게 설정 가능하다.)
자세한 예시와 함께 공부하고 싶다면
정리
💡 JDBC는 자바 애플리케이션이 데이터베이스에 접근할 수 있도록 만든 JAVA에서 제공하는 API이다.
하나의 JDBC로 어떤 DBMS든 각 벤더마다 제공되는 JDBC 드라이버를 통해 연결 할 수 있다.
커넥션 풀?
JDBC 실행 과정 중에서 생성되어야 할 Connection 객체를 미리 만들어서 pool 이란 곳에서 저장을 해두는 기법이다.
장점
불필요한 과정(Connection객체를 생성,삭제)을 줄여서 성능을 높일 수 있다.
WAS에서 커넥션 풀
▶크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기 시간이 감소한다.
▶적게 설정하면 그 만큼 대기 시간이 길어진다.
결과적으로 사용량에 따라 적정량의 커넥션(Connection)객체를 생성해두어야 한다.
[출처]
https://steady-coding.tistory.com/564
https://linked2ev.github.io/spring/2019/08/14/Spring-3-커넥션-풀이란/
https://code-lab1.tistory.com/209
https://velog.io/@mooh2jj/커넥션-풀Connection-pool은-왜-쓰는가
https://zzang9ha.tistory.com/376
https://brunch.co.kr/@jehovah/25
※ 잘못된 정보가 있다면 댓글을 남겨주시면 감사하겠습니다.!!!!!
'CS' 카테고리의 다른 글
Blocking, Non-blocking & Synchronous, Asynchronous (0) | 2023.04.03 |
---|---|
HTTP & HTTPS (0) | 2023.03.30 |
대칭키(비밀키) & 비대칭키(공개키) (0) | 2023.03.23 |
TCP 3 way handshake & 4 way handshake (0) | 2023.03.21 |
댓글