본문 바로가기
카테고리 없음

Spring Boot 4.1 Lazy JDBC connection Fetching

by igooo 2026. 8. 21.
728x90

개요

지난 포스팅(https://blog.igooo.org/191)에 이어 Spring Boot 4.1.0에 포함된 Lazy JDBC connection feting을 사용하여 JDBC를 사용하는 프로젝트에서 성능을 향상하는 방법을 알아보자.

 

spring.datasource.connection-fetch 동작 방식

JDBC를 사용하는 프로젝트에서는 Transaction을 사용하여 중요한 비즈니스 로직을 처리하는 경우가 많다. Transaction을 사용하는 경우 보통은 Service 클래스에 @Transactional 어노테이션을 사용하여 트렌젝션의 범위를 지정하여 사용한다. 이때 @Transactional 어노테이션이 지정된 메서드에 시작부터 끝까지 BEGIN, COMMIT이라는 명령 사이에서 메서드의 로직이 동작하게 된다. Transaction 중에는 하나의 DB Connecion을 Transaction이 종료될 때까지 사용하게 된다. 사용자가 작성한 로직이 오래 걸리는 경우(외부 서버와 연동하거나 계산 로직이 복잡한 경우...) Transaction이 시작되고 끝날 때까지 DB Connection 한 개가 Transaction을 위해 사용되어야 하는 단점이 있다.

Spring Boot 4.1.0에 추가된 spring.datasource.connection-fetch 옵션은 기본적으로는 eager(default) 방식으로 동작하고, lazy 옵션으로 변경하면 @Transactional이 지정된 메서드에서도 실제 DB Connection이 필요한 시점에 DB Connection을 가져오게 된다.

 

 

Getting Started

spring.datasource.connection-fetch 옵션에 따라 실제 DB Connection의 사용시간을 비교해 보는 간단한 프로젝트를 생성하여 동작 방식을 알아보도록 하자.

프로젝트 생성

JDBC를 사용하는 Spring Boot Web 프로젝트를 생성하고, 성능 확인을 위한 actuator도 같이 추가한다.

plugins {
    id 'java'
    id 'org.springframework.boot' version '4.1.0'
    id 'io.spring.dependency-management' version '1.1.7'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webmvc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-acturator'
    runtimeOnly 'com.mysql:mysql-connector-j'    
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

 

 

application properties

DB 접속 정보를 설정하고, Connection 사용시간을 확인하기 위해 metrics 정보도 확인 가능하도록 설정한다.

spring.datasource.url=
spring.datasource.username=
spring.datasource.passowrd=

management.endpoints.web.exposure.include=metrics

 

 

사용자 로직 작성

사용자를 저장하는 간단한 API를 작성하여 기능을 테스트한다.

사용자 정보를 저장하기 전에 외부 서버와 통신을 하는 예제 코드를 작성한다. 이때 외부 서버는 임의로 2초 정도에 응답시간을 가지도록 설정한다.

@Service
class UserService {
    private final UserRepository userRepository;
	
    UserService(UserRepository userRepository) {
        this.UserRepository = userRepository;
    }
    
    @Transactional
    User create(String name) {
    	// slow remote call 2s
        remoteCall();
        
        return userRepository.save(new User(null, name, Instant.now());
    }
    
    private void remoteCall() {
    	// 외부 서버와 연동
    }
}

@RestController
class UserController {
	private final UserService userService;
    
    UserController(UserService userService) {
    	this.userService = userService;
    }
    
    @GetMapping("/users")
    User create(String name) {
    	return this.userService.create(name);
    }

}

record User(@Id Long id, String name, Instant createdAt) {
}

interface UserRepository extends ListCrudRepository<User, Long> {
}

 

eager로 실행

UserController에 API를 호출해서 사용자를 저장하고, metrics 정보를 확인한다.

(URL: http://localhost:8080/acturator/metrics/hikaricp.connections.usage)

{
	......
    "description": "Connection usage time",
    "measurements": [
    	{
            "statistic": "COUNT",
            "value": 3
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 2.088
        },
        {
            "statistic": "MAX",
            "value": 2.083
        }        
    ],
    "name": "hikaricp.connections.usage"
}

 TOTAL_TIME이 2초로 나오는 것을 볼 수 있다.

 

Lazy 설정

spring.datasource.connection-fetch의 값을 lazy로 설정하고 서버를 다시 실행하고, 동일하게 API를 호출 후 다시 metric을 확인해 본다.

application.properties 파일에 lazy로 설정한다.

# application.properties
spring.datasource.connection-fetch=lazy

 

code로 DataSource를 Bean으로 선언하는 경우는 아래와 같이 작성한다.

DataSource 객체를 LazyConnectionDataSourceProxy 클래스를 사용해서 제공한다.

@Bean
DataSource dataSource(DataSourceProperties properties) {
    var target = properties.initializeDataSourceBuilder.type(HikariDataSource.class).build();
    return new LazyConnectionDataSourceProxy(target);
}

 

 

동일하게 API를 호출하고 metrics을 확이해 보면 아래와 같다.

{
	......
    "description": "Connection usage time",
    "measurements": [
    	{
            "statistic": "COUNT",
            "value": 3
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 0.039
        },
        {
            "statistic": "MAX",
            "value": 0.038
        }        
    ],
    "name": "hikaricp.connections.usage"
}

이전 테스트와는 다르게 TOTAL_TIME 값이 0.039로 이전 테스트와 다르게 매우 작은 값을 볼 수 있다.

 

마치며

spring.datasource.connection-fetch 옵션은 위에 예제처럼 Transaction 코드에서 DB 접근전에 로직이 많은 경우 유용하게 사용될 수 있다. spring.datasource.connection-fetch은 모든 상황에서 더 좋은 성능을 보여주지 않을 수 있기 때문에 위에 예제와 비슷한 경우라면 Spring Boot 4.1.0으로 적용 후 성능을 테스트해보는 것이 좋겠다.

 

참고

https://spring.io/blog/2026/06/21/spring-boot-41-and-spring-batch#lazy-datasource-connections

728x90