웹개발 - Back 관련/Spring-DB

[Kotlin Spring] Connection, Connection Pool, DataSource 개념과 작동원리

정상호소인 2024. 8. 14. 12:26
Topic =  Connection, Connection Pool 개념 및 작동원리와 DataSource

 


 

Connection 개념 및 생명주기

 

Connection?

  • Connection 이란 특정 DB와의 연결된 세션이다.
    Connection은 SQL 문을 연결된 컨텍스트 내에서 실행하고 결과를 반환하느 세션으로 DB와 상호작용을 가능하게 한다.

 

Connection이 생성되는 과정 ( Connection Pool - X )

  1. 연결정보 조회
    - 애플리케이션 로직이 DB 드라이버를 통해 연결정보를 조회한다.
    - ( 각 DB 드라이버 유형마다 자체적인 URL 을 갖는다. 전달된 URL형식과 일치하는 유형의 DB 드라이버가 매핑되고, 해당 DB 시스템의 URL 형식에 맞는지 확인하는 작업 등이 진행된다. )

  2. 네트워크 연결 시도 및 정보 전달
    - URL에 포함된 호스트와 포트 정보를 이용하여 DB 서버에 네트워크 연결을 시도한다.
    - 네트워크 연결이 되면, DB에 ID,PW와 기타 부가정보를 DB에 전달한다.

  3. 인증 및 세션 생성 ( DB 내부에서 진행됨 )
    - 전달된 연결 정보를 통해 DB내부에서 인증이 진행된다.
    - 인증이 완료되면 DB 내부에 DB 세션을 생성한다.

  4. 연결정보 응답 및 Connection 객체 생성
    - 인증 작업이 끝나고 세션이 생성되면, DB 응답 정보를 반환한다.
    - Connection 객체가 생성되어 반환된다.

 

Connection 종료

  • 클라이언트의 DB 관련 요청이 종료되면, close() 메서드를 통해서 Connection을 종료하면 DB의 해당 Connection 세션이 종료되며 커넥션이 종료되게 된다.
  • Connection을 close() 하지 않을 경우, 클라이언트 각각의 Connection이 무한정으로 생성되게 되고 Connection 객체 각각은 네트워크 연결이 된 객체기 때문에 리소스 누수( Connection Leak )가 발생하게 된다.

 

 

🚨Connection 관리 문제

  1. 자원 관리 - Connection 리소스 누수 문제
    ➡️클라이언트가 DB 관련 요청을 보낼 때 마다 새로운 Connection을 생성하게 되므로, 서비스 로직의 실행되는 시간이 늦춰지는 문제가 발생하게 된다. 이러한 문제로 인해 Connection Pool을 사용하여 리소스 누수를 방지하는 등 성능을 개선하는 방법을 사용하게 된다.

  2. 동시성 처리 - 데이터 무결성 보장
    ➡️여러 클라이언트가 동시에 데이터베이스에 접근할 때, 각 클라이언트는 독립적인 Connection을 유지해야 한다. 이 문제를 해결하기 위해 DB 트랜잭션 개념이 도입되었다.

 

 

 

Connection Pool

 

Connection Pool 이란?

Connection Pool 은 데이터베이스와의 연결을 통해 미리 여러 Connection 객체를 생성하여 Connection 저장소인 풀에 저장해두고, 애플리케이션이 필요할 때 생성된 Connection을 가져와 사용하다가 요청 작업이 끝나면 다시 풀에 반환되어 생성된 Connection을 재사용하는 방식으로 작동하는 것을 얘기한다.

 

Connection Pool의 생성

🟥 Spring Boot 는 Connection 정보를 설정하고 관리할 뿐, Connection Pool의 생성주체는 애플리케이션 로직이 아니라, Connection Pool 라이브러리 ( Hikari CP 등 ) 이다.

 

♻️작동원리

  1. 초기화
    애플리케이션이 시작될 때, 설정된 수 만큼의 연결정보가 DB와 연결되어 Connection 객체들이 생성되고 Connection Pool에 저장된다.

  2. Connection 요청 및 사용
    애플리케이션이 DB에 접근하려고 할 때, DB 드라이버로 접근하여 새로운 커넥션을 획득하는 것이 아닌, Connection Pool에 접근하여 생성되어 있는 커넥션 객체를 참조하여 SQL 쿼리를 실행하고 결과를 수신한다. 이때 각 Connection은 DB와 네트워크 연결이 되어 있는 상태이다.

  3. 연결 반환
    작업이 완료된 후, 사용한 연결을 Connection Pool에 반환한다. 해당 Connection은 다른 요청에 재사용 된다.

  4. 유지 관리
    주기적으로 업데이트 되지 않은 유휴 상태의 Connection을 업데이트 하는 등의 Connection 최신화 작업 등이 포함된다.
    - Connection 만료 및 재생성
    - 쿼리 실행 중 오류 발생 시 Connection 재생성
    - 백그라운드 스레드를 통한 주기적으로 Connection의 유효성 검사
    - DB 스키마 변경 감지

 

 

 

📚Hikari CP의 경우, Connection 요청 시 실제 데이터베이스 Connection을 감싼 hikariProxyDatabase객체가 Connection Pool에서 반환되어 사용된다.
- 추후 proxy 패턴도 학습해보고 Hikari CP 작동 원리 학습해보기.

 

✅ Spring Boot 2.0 부터 작성일 까지는 Spring Boot에서 Hikari CP를 기본적으로 제공하고 있다. Hikari CP는 tomcat-JDBC Pool, commons-dbcp2 등 다른 Connection Pool에 비해 더 효과적인 성능을 가지고 있다고 한다.

 

GitHub - brettwooldridge/HikariCP-benchmark: JHM benchmarks for JDBC Connection Pools

JHM benchmarks for JDBC Connection Pools. Contribute to brettwooldridge/HikariCP-benchmark development by creating an account on GitHub.

github.com

 

🕹️DB 드라이버가 아닌 Connection Pool에서 커넥션 가져오기

  • DataSource ( 추상화 인터페이스 )
  • HikariDataSource ( DataSource 구현체 - Connection Pool )

 

DataSource

 

DataSource 란?

  • Connection을 획득하는 방법을 추상화 하는 인터페이스이다. 해당 인터페이스를 통해 dataSource를 주입 받아 애플리케이션에서 커넥션을 얻어서 사용하는 부분과 Connection 객체 속성이나 설정에 부분을 명확하게 분리하여 사용할 수 있다.

 

Spring Boot DataSource 자동 Resource ( Bean ) 등록

// applicaton.yml or application.properties
spring:
  datasource:
    url:
    username:
    password:
    driver-class-name:
  • 위와 같이 application.yml 에 정의된 dataSource 속성들은 Spring Boot의 DataSourceAutoConfiguration 에 의해 DataSource가 생성되는데 기본적으로 Hikari CP가 적용된 dataSource가 빈에 등록된다.

 

 

 

 


 

[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]

A. JDBC

 

B. JDBC 개념 정리

 

B. DB Transaction

 


 

[오류,에러 등등]

1. late init var con : Connection 으로 Connection을 사용할 때 발생한 오류

  • 원인 - Repository의 각 메서드마다 지역 변수로 connection 객체를 생성하지 않고 close()만 잘해줘도 정상작동될까?? 해서 전역 변수로 선언해서 사용해본 것이 문제.
  • 문제 - CRUD 메서드 각각을 테스트 해보면 문제가 없으나 통합 테스트 시 6개 메서드 중 3 - 4개의 메서드만 통과.
    • Hikari CP 의 Connection Pool의 1번 Connection만 사용하고 있고 Time Out이 발생됨.
  • 해결 - try-with-resource 구문의 AutoCloseable 인터페이스의 .use {} 메서드를 통해 지역변수로 선언하지도 않으면서, finally의 close() 메서드도 지울 수 있게 됨.

 

- 문제 코드-

class RepositoryImpl( private val dataSource: DataSource ): Repository {
    private lateinit var con : Connection
    private lateinit var ps : PreparedStatement
    private lateinit var rs : ResultSet
    
    override fun create(){
    	val sql = "SQL"
        try {
            con = dataSource.connection()
            ps = con.prepareStatement(sql).apply {
            	// sql쿼리문 작성
            }
            ps.excute()
        } catch(){
        
        } finally {
            allClose()
        }
        
    }
    
    private fun allClose(){
    	if(this::rs.isInitialized) {
            JDBCUtils.closeResultSet(rs)
        }
        if(this::ps.isInitialized) {
            JDBCUtils.closeStatement(ps)
        }
        
        if(this::con.isInitialized) {
            JDBCUtils.closeConnection(con)
        }
    }
}

 

-수정된 코드-

class RepositoryImpl( private val dataSource: DataSource ): Repository {
    private lateinit var con : Connection
    private lateinit var ps : PreparedStatement
    private lateinit var rs : ResultSet
    
    override fun create(){
    	val sql = "SQL"
        try {
            dataSource.connection.use { con -> 
                con.prepareStatement(sql).use { ps ->
                    ps.apply {
                        setString...
                        setInt...
                    }
                    ps.executeUpdate()
                }
            }
        } catch(){
            //예외 처리 로직
        }
    }
}

 


 

[Comment]

1. 

 

2.

 

3.

 

 


[Reference]

 

 

스프링 DB 1편 - 데이터 접근 핵심 원리 강의 | 김영한 - 인프런

김영한 | 백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니

www.inflearn.com