회사 기술 공유 시간에 다른 팀 개발자님께서 set autocommit 쿼리 호출을 개선하여 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 로 설정이 되는데, 이유는 뒤에서 알아보자.
하나의 로직에서 set autocommit = ?
쿼리가 반복적으로 호출되고 있다.
위 설정으로 set autocommit = ?
쿼리가 날라가지 않도록 한건데, set autocommit = ?
이 700 ns 가 소요된다면, 4번의 쿼리를 줄여 2.8ms 줄인 것이다. 👏👏👏
사용자가 commit 명령을 따로 하지 않아도 자동으로 모든 명령을 commit 되어 즉시 반영되는 명령어이다. autocommit을 활성화 해놓은 후 쿼리를 날리면 실수를 하더라도 DB 에 곧바로 즉시 반영이 되므로 주의해서 사용해야한다.
Transaction으로 묶어서 작업을 수행해도 True로 되어 있으면 즉시 반영 된다.
따라서, Hibernate에서는 트랜잭션 전 후로 setAutoCommit(false) → 쿼리 1 수행 → 쿼리 2 수행 → setAutoCommit(true) → Commit 또는 Rollback 를 수행하게된다.
코드로 알아보자.
auto-commit: false 이기때문에 providerDisablesAutoCommit 이 true 가 되고,
doConnectionsFromProviderHaveAutoCommitDisabled 값이 true 가 되어 setAutoCommit(false)을 수행하지 않게되는 것이다.
commit 후에 initiallyAutoCommit 이 true 라면, setAutoCommit(true)를 수행하게 되는데,
auto-commit: false 로 인해 doConnectionsFromProviderHaveAutoCommitDisabled 값이 true 가 되고 initiallyAutoCommit 은 false가 되어 setAutoCommit(true)를 수행하지 않게되는 것이다.
역시, 서비스의 쿼리량을 정렬을 하니 set autocommit 이 제일 많았다.
public void create() {
member.findByMemberId(memberId);
member.save(admin);
passwordRepository.save(password);
}
save() 쿼리 메서드에서는 각각 @Trasactional
걸려있고, 앞뒤로 set autocommit 쿼리가 날라가는 것을 알 수 있다.
사용자 쿼리 메서드인 findByMemberId()는 @Trasactional
이 걸려있지 않아 set autocommit 쿼리가 발생하지 않는다.
auto-commit: false 설정 시에 poolMetadata.getDefaultAutoCommit() 가 FALSE 가 되어 우리가 따로 설정해주지 않아도 hibernate.connection.provider_disables_autocommit은 true 로 설정이 되어버린다.
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() 을 해주기 때문에 따로 설정해주지 않아도 우리가 직접 명시적 커밋을 하지 않아도 되는 것이다!
선생님 질문이 있습니다. 왜 API 응답시간이 1/2 로 줄었을까? 부분의 사진에서 어떤 모니터링을 이용하시고있는건가요?