[HikariCP] set autocommit 개선을 통한 성능 최적화를 해보자

Hocaron·2024년 3월 20일
2

Spring

목록 보기
44/44

회사 기술 공유 시간에 다른 팀 개발자님께서 set autocommit 쿼리 호출을 개선하여 API 응답시간을 1/2 로 줄여버렸다는 발표를 하셨다.

어떻게 API 응답시간을 1/2 로 줄였을까?

spring:
   datasource:
     hikari:
       auto-commit: false
#   jpa:
#     properties:
#       hibernate.connection.provider_disables_autocommit: true  

엄청 간단하다. yml 파일에 이 설정을 추가해주기만 하면 된다.

HikariCP를 사용하고 있기 때문에 HikariCP 설정에서 Auto-Commit을 False로 설정하고, provider_disables_autocommit 설정은 DBCP(커넥션 풀) 설정의 autoCommit을 믿고 사용하는 설정을 True로 두는 설정을 적용한 부분이다.

hibernate.connection.provider_disables_autocommit 설정은 따로 해주지 않아도 true 로 설정이 되는데, 이유는 뒤에서 알아보자.

왜 API 응답시간이 1/2 로 줄었을까?


하나의 로직에서 set autocommit = ? 쿼리가 반복적으로 호출되고 있다.

위 설정으로 set autocommit = ? 쿼리가 날라가지 않도록 한건데, set autocommit = ? 이 700 ns 가 소요된다면, 4번의 쿼리를 줄여 2.8ms 줄인 것이다. 👏👏👏

set autocommit

autocommit 은 뭘까?

사용자가 commit 명령을 따로 하지 않아도 자동으로 모든 명령을 commit 되어 즉시 반영되는 명령어이다. autocommit을 활성화 해놓은 후 쿼리를 날리면 실수를 하더라도 DB 에 곧바로 즉시 반영이 되므로 주의해서 사용해야한다.

Transaction으로 묶어서 작업을 수행해도 True로 되어 있으면 즉시 반영 된다.
따라서, Hibernate에서는 트랜잭션 전 후로 setAutoCommit(false) → 쿼리 1 수행 → 쿼리 2 수행 → setAutoCommit(true) → Commit 또는 Rollback 를 수행하게된다.

어디서 왜 set autocommit 쿼리가 날라가는 걸까?

코드로 알아보자.

LogicalConnectionManagedImpl.begin() & AbstractLogicalConnectionImplementor.begin()

auto-commit: false 이기때문에 providerDisablesAutoCommit 이 true 가 되고,

doConnectionsFromProviderHaveAutoCommitDisabled 값이 true 가 되어 setAutoCommit(false)을 수행하지 않게되는 것이다.

AbstractLogicalConnectionImplementor.resetConnection()

commit 후에 initiallyAutoCommit 이 true 라면, setAutoCommit(true)를 수행하게 되는데,

auto-commit: false 로 인해 doConnectionsFromProviderHaveAutoCommitDisabled 값이 true 가 되고 initiallyAutoCommit 은 false가 되어 setAutoCommit(true)를 수행하지 않게되는 것이다.

auto-commit: false 설정이 되어 있지 않다면

역시, 서비스의 쿼리량을 정렬을 하니 set autocommit 이 제일 많았다.

로직과 날아가는 쿼리

    public void create() {

        member.findByMemberId(memberId);

        member.save(admin);
        passwordRepository.save(password);
    }

findByMemberId, save JPA 쿼리 메서드에서는 각각 @Trasactional, @Trasactional(readOnly = true) 걸려있고, 앞뒤로 set autocommit 쿼리가 날라가는 것을 알 수 있다.

auto-commit: false 설정이 되어 있다면

hibernate.connection.provider_disables_autocommit: true 설정은 필요없을까?

HibernateJpaConfiguration.configureProviderDisablesAutocommit()

auto-commit: false 설정 시에 poolMetadata.getDefaultAutoCommit() 가 FALSE 가 되어 우리가 따로 설정해주지 않아도 hibernate.connection.provider_disables_autocommit은 true 로 설정이 되어버린다.

@Transactional 로 묶여있지 않은 쿼리는 실행이 될까?

JPA 쿼리 메서드의 경우, 우리가 따로 설정을 해주지 않아도 SimpleJpaRepository 를 통해 @Transactional 이 붙어있지만, @Transactional 을 따로 붙여주지 않은 queryDSL 메서드는 실행이 될까?

정답은 명시적 커밋이 일어나지 않아 롤백이 일어난다. 물론, insert 쿼리는 모두 JPA 통해서 하고 있던 프로젝트라서 따로 이슈는 없었지만 JDBC 를 사용했다면 명시적으로 커밋을 해줬어야 롤백이 일어나지 않았을 것이다.

HikariPool-1 - Executed rollback on connection org.mariadb.jdbc.MariaDbConnection@35630ee8 due to dirty commit state on close().

위의 예시는 queryDSL 로 실행되고, 매 쿼리마다 롤백이 일어나는 로그이다. 명시적 커밋이 없어, isCommitStateDirty 이 true 로 유지되고 ProxyConnection.java 에서 롤백 시키고 로그를 찍는 것을 알 수 있다.

queryDSL 를 호출하는 서비스 상단에 @Transactional 만 걸어주면 해결이 된다.

@Transactional 이 걸려있는 메서드 앞뒤로 begin(), commit() 을 해주기 때문에 따로 설정해주지 않아도 우리가 직접 명시적 커밋을 하지 않아도 되는 것이다!

결론

  • JPA 를 사용하고 있는 프로젝트라면, auto-commit: false 를 고려해봐도 좋을 것 같다.

References

profile
기록을 통한 성장을

2개의 댓글

comment-user-thumbnail
2024년 4월 26일

선생님 질문이 있습니다. 왜 API 응답시간이 1/2 로 줄었을까? 부분의 사진에서 어떤 모니터링을 이용하시고있는건가요?

1개의 답글