김찬진의 개발 블로그
[23/08/03] 트랜잭션 관련 용어 정리 본문
트랜잭션
- DB에서 한 번에 여러 쿼리나 DB 작업을 묶어서 하나의 논리적 작업 단위로 처리하는 개념
트랜잭션의 특징, ACID
- Atomicity : 트랜잭션 내에서 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공하거나 모두 실패해야 한다.
- Consistency : 모든 트랜잭션은 일관성있는 DB 상태를 유지해야 한다.
- Isolation : 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야 한다.
- Durability : 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 한다. 그래야 문제발생 시 DB로그 등을 사용해서 성공했던 트랜잭션을 복구할 수 있다.
트랜잭션(커넥션) 동기화
- 애플리케이션에서 DB 트랜잭션을 사용하려면 트랜잭션을 사용하는 동안 같은 커넥션을 유지해야한다. 그래야 같은 세션을 사용할 수 있다.
- 데이터베이스 트랜잭션이란 데이터베이스에서 한 번에 여러 쿼리 또는 작업을 묶어서 하나의 논리적 작업 단위로 처리하는 것을 의미한다. 이러한 트랜잭션은 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)을 보장한다.
- 트랜잭션을 사용할 때 같은 커넥션을 유지하는 이유와 그 과정은 아래와 같다.
- 이유1. 트랜잭션 유지와 커넥션: 데이터베이스 트랜잭션을 사용할 때, 여러 쿼리 또는 데이터베이스 작업을 하나의 트랜잭션으로 묶어서 처리한다. 이 트랜잭션을 수행하는 동안 일관성과 격리성을 유지하기 위해서는 동일한 커넥션을 사용해야 한다. 각각의 커넥션은 독립된 작업 단위로 간주되므로, 다른 커넥션으로 트랜잭션을 처리하면 여러 문제가 발생할 수 있다.
- 이유2. 세션 유지: 데이터베이스 커넥션을 열면 해당 커넥션은 데이터베이스 서버와의 세션을 나타낸다. 세션은 클라이언트(애플리케이션)과 데이터베이스 서버 간의 연결을 유지하는 상태이다. 트랜잭션이 여러 쿼리를 실행하면서 세션을 유지하는 동안 데이터베이스는 트랜잭션 동안 발생하는 작업들을 한 논리적 단위로 처리하게 된다.
- 이유3. 트랜잭션 일관성 보장: 같은 커넥션을 사용하면 데이터베이스는 하나의 세션에서 트랜잭션을 처리하므로 데이터 일관성을 보장할 수 있다. 다른 커넥션을 사용하면 트랜잭션 간의 충돌이나 데이터 불일치가 발생할 가능성이 높아진다.
JDBC (JAVA 기술)
- 자바에서 DB에 접속할 수 있도록 하는 자바 API
JDBC 표준 인터페이스
- 만약 JDBC 표준 인터페이스가 없었다면 각 DB 드라이버(H2 드라이버, MySQL 드라이버, Oracle 드라이버 등)는 자기 회사 마음대로 연결하는 법, SQL 를 보내는 법, SQL 의 응답을 받는 법이 각기 달랐을 것이다.
- JDBC 표준 인터페이스는 각 DB 드라이버가 추상화에 의존하는 것을 가능케 해준다.
- Connection: 연결
- Statement: SQL 내용
- ResultSet: SQL 요청 응답
DriverManager (JDBC 기술)
- 개발자가 라이브러리에 다운로드해놓은 DB 드라이버(H2 드라이버, MySQL 드라이버, Oracle 드라이버 등)를 관리하고 커넥션을 획득하는 기능을 제공하는 클래스
- Connection con = DriverManager.getConnection(...) 식으로 쓴다.
- JDBC 는 인자값을 보고 DriverManager를 어떤 DB 드라이버로 설정할지를 결정한다.
- DriverManager 는 커넥션풀이 아니기 때문에 DriverManager.getConnection() 를 호출할 때마다 매번 새로운 커넥션을 생성한다.
JDBC 드라이버 (JDBC 표준 인터페이스를 상속한 구현체 클래스)
각 DB 드라이버(H2 드라이버, MySQL 드라이버, Oracle 드라이버 등)들은 JDBC 표준 인터페이스(Connection, Statement, ResultSet)를 상속받아 자신의 DB에 맞춰 구현되어 라이브러리로 제공된다.
보통 MySQL JDBC 드라이버, Oracle JDBC 드라이버 등으로 부른다. 또는 그냥 퉁쳐서 JDBC 드라이버라고 부른다.
JDBC 드라이버는
DataSource 인터페이스 (JDBC 기술)
- DriverManager 는 커넥션풀이 아니기 때문에 DriverManager.getConnection() 를 호출할 때마다 매번 신규 커넥션을 생성해서 사용하는 방식이다.
- 만약 HikariCP, DBCP2 등의 커넥션풀로 JDBC 드라이버를 변경하기 원한다면 기존의 코드를 변경해야 하므로 OCP 위반할 수 있다.
- 그래서 DataSource 인터페이스에 의존하여(추상화하여) 커넥션풀(CP)인 것과 아닌 것을 자유롭게 바꿔 끼울 수 있도록 한다. DataSource 구현체만 바꿔 끼우면 되기 때문에 OCP 위반하지 않는다.
- 스프링부트는 데이터소스를 스프링빈에 "dataSource"라는 이름으로 자동으로 등록한다.
- 만약 개발자가 직접 dataSource 를 스프링빈으로 등록하면 스프링부트는 dataSource 를 자동으로 등록하지 않는다.
DriverManagerDataSource (JDBC 기술)
- 위 문장 "그래서 DataSource 인터페이스에 의존하여(추상화하여) 커넥션풀(CP)인 것과 아닌 것을 자유롭게 바꿔 끼울 수 있도록 한다. 구현체만 바꿔 끼우면 되기 때문에 OCP 위반하지 않는다." 에서 커넥션풀(CP)이 아닌 것을 의미한다.
- DataSource 구현체로, JDBC의 기술이다.
- DriverManagerDataSource 는 ConnectionPool 은 아니다.
JdbcUtils (JDBC 기술)
- JDBC API를 직접 사용하여 DB와 연결하고 쿼리를 실행하는 유틸 클래스
- JdbcUtils.closeResultSet(ResultSet)
- JdbcUtils.closeStatement(Statement)
- JdbcUtils.closeConnection(Connection)
- 만약 JdbcUtils 를 쓰면서 트랜잭션(커넥션)을 동기화를 원한다면 특정 로직에서 호출되는 메서드들에서 closeConnection() 을 사용하지 않으면 된다. (하지만 번거롭고 커넥션을 닫지 않기엔 위험하다)
TransactionManager, 트랜잭션 매니저 (스프링 기술)
- ThreadLocal 을 사용하여 트랜잭션(커넥션)을 동기화해준다. ThreadLocal 을 사용하면 각각의 쓰레드마다 별도의 저장소가 부여된다. 따라서 해당 쓰레드만 해당 데이터에 접근할 수 있다.
- 데이터소스를 통해 커넥션을 만들고 트랜잭션을 시작하고 해당 커넥션을 트랜잭션 동기화 매니저에 보관한다.
- 덕분에 커넥션 동기화를 위해 리포지토리에서 파라미터로 커넥션을 받는 방법을 더 이상 쓰지 않아도 된다.
- 이제는 커넥션 동기화를 위해 리포지토리에서 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용하면 된다.
- 트랜잭션이 종료되면 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션도 닫는다.
- 스프링부트는 적절한 트랜잭션 매니저(PlatformTransactionManager)를 자동으로 "transactionManager"라는 이름으로 스프링빈에 등록한다.
- 만약 개발자가 직접 트랜잭션 매니저를 스프링빈으로 등록하면 스프링부트는 매니저를 자동으로 등록하지 않는다.
TransactionSynchronizationManager, 트랜잭션 동기화 매니저 (스프링 기술)
- 트랜잭션 매니저가 Thread Safe 하게 커넥션을 보관하는 곳
DataSourceUtils (JDBC 기술)
- JDBC API를 직접 사용하는 것이 아니라 SQLMapper(JdbcTemplate, MyBatis) 또는 JPA(하이버네이트, 이클립스링크) 등의 기술을 로직과 JDBC 사이에서 사용하여 좀 더 간편하게 DB와 연결하고 쿼리를 실행하는 유틸 클래스
- 트랜잭션(커넥션)을 동기화를 위해서 JdbcUtils 가 아니라 DataSourceUtils 를 사용해야 한다.
- DataSourceUtils.getConnection(DataSource)
- DataSourceUtils.releaseConnection(Connection, DataSource)
- Connection 을 닫지는 않는다. 닫아버리면 커넥션 동기화가 안되기 때문이다.
- transactionManager.commit(TransactionStatus) 또는 transactionManager.rollback(TransactionStatus) 하면 알아서 커넥션 닫고 트랜잭션 동기화 매니저에서도 커넥션 놓아준다.
만약 TransactionManager 을 사용하지 않았다면?
- 특정 로직에서 같은 커넥션을 계속 사용하도록 커넥션을 닫지 않아야 한다. (커넥션을 닫지 않으면 위험하다)
- 또는 파라미터로 넘어온 커넥션을 사용해야 한다. (코드가 더러워진다)
'1일1배움 > Java' 카테고리의 다른 글
[23/08/05] Map.Entry (0) | 2023.08.06 |
---|---|
[23/08/05] Enumeration (0) | 2023.08.06 |
[23/08/03] ThreadLocal (0) | 2023.08.03 |
[23/08/01] checkedException, uncheckedException (0) | 2023.08.01 |
[23/07/25] isEmpty(), == Null 의 차이 (0) | 2023.07.25 |