데이터 베이스에 다수의 사용자가 접근하더라도 데이터베이스 내의 데이터는 손상되지 않고 반드시 정확하고 일관되게 유지되어야 한다. 데이터 베이스의 데이터가 손상이 되었어도 빠르게 복구하여 원래의 기능을 다 할 수 있도록 해야 한다. DB 내에는 데이터를 보호하기 위해 다양한 기능들이 제공되는데 그 중심에 트랜잭션이 있다.
한빛 아카데미의 <데이터베이스 개론> 책을 참고하였습니다.
Transaction
트랜잭션이 수행되는 예로 비밀번호를 변경하기 위한 과정을 생각해보자. 비밀번호 변경하기를 누르고, 해당 이메일로 관련 Entity를 가져오고 업데이트하는 과정을 하나의 작업이라 볼 수 있다. 이 작업을 처리하기 위해 다수의 연산이 동작해야 하며 이 연산들의 집합을 트랜잭션이라고 볼 수 있다. 트랜잭션은 데이터베이스의 데이터가 변경되었음을 의미한다.
Transaction의 특징 - ACID
Property | Responsibility for maintaining properties |
Atomicity | Transaction Manager |
Consistency | Application programmer |
Isolation | Concurrency Control Manager |
Durability | Recovery Manager |
1. Atomicity (원자성)
트랜잭션을 구성하는 연산들이 완벽히 실행되거나 하나도 실행되지 않아야 하는 all or nothing 방식을 의미.
트랜잭션으로 관리되는 데이터의 값이 변경되는 과정에서 장애가 발생하여 작업을 마치지 못했다면 트랜잭션이 일어나기 전의 데이터 상태로 복구하여 변경된 값이 저장되는 일이 없도록 해야 한다. (데이터베이스에 전혀 반영이 되지 않도록)
- Abort : 변경 내역을 폐기한다면 데이터베이스에는 변경사항이 적용되지 않음
- Commit : Commit이 완료되면 데이터베이스 변경사항 적용
2. Consistency (일관성)
트랜잭션이 성공적으로 수행된 후에도 데이터베이스가 일관된 상태를 유지해야 함을 의미.
트랜잭션 작업이 잘 마무리되었다면, 데이터베이스는 바뀐 데이터 정보를 저장하여 트랜잭션 이전의 데이터베이스 상태처럼 일관성을 유지해야 한다.(데이터베이스가 모순된 상태가 되면 안 된다.)
3. Isolation (격리성)
현재 수행 중인 트랜잭션이 완료될 때까지 트랜잭션이 생성한 중간 연산 결과에 다른 트랜잭션들이 접근할 수 없음을 의미.
동시에 병행적으로 진행 중인 트랜잭션 작업에 관해서는 둘 중 하나의 트랜잭션 작업이 다른 트랜잭션 작업을 건드릴 수 없다(결과 참조 또한 불가능). 즉 트랜잭션 작업 내의 중간 연산 결과를 다른 트랜잭션 작업이 건드려 데이터의 값이 변경되는 일이 없도록 해당 트랜잭션을 독립시켜놔야 한다.
4. Durability (지속성)
영속성이라고도 하며 트랜잭션이 성공적으로 완료된 후 데이터베이스에 반영한 수행 결과는 어떠한 경우에도 손실되지 않고 영구적이어야 함을 의미
트랜잭션의 연산
- commit() : 트랜잭션이 성공적으로 수행되었고 데이터베이스가 일관된 상태로 되었음을 선언(작업 완료)
- rollback() : 트랜잭션이 수행하는데 실패하여 데이터베이스의 일관성이 깨져 트랜잭션 이전 상태로 되돌림을 선언(작업 취소) rollback 시에는 해당 트랜잭션을 재시작하거나 폐기함
자바의 경우 인터페이스 내부에 구현시켜 놓음으로써 해당 클래스를 상속할 시 사용자가 트랜잭션 연산을 건들지 않게 하였다.
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
...
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw translateException("JDBC commit", ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw translateException("JDBC rollback", ex);
}
}
...
}
트랜잭션의 6가지 상태
- 활동 상태 : 트랜잭션이 실행되고 있는 상태, 실패 상태 혹은 부분 완료상태로 넘어갈 수 있다.
- 부분 완료 상태 : 트랜잭션의 마지막 연산이 실행된 직후, commit 연산이 실행되기 전 상태(데이터베이스에 아직 반영되지 않은 상태)
- 완료 상태 : 트랜잭션의 마지막 연산이 실행되고 commit 연산을 실행한 상태, 최종 결과가 데이터베이스에 반영되었다.
- 실패 상태 : 트랜잭션 수행이 오류로 인해 작업이 중단된 상태. 트랜잭션 이전의 데이터로 복구를 진행해야 한다.
- 철회 상태 :트랜잭션 작업 중 오류로 인한 실패로 rollback 연산을 수행한 상태. 하드웨어, 소프트웨어적 오류라면 철회된 트랜잭션 작업을 다시 시작하지만, 해당 데이터가 데이터베이스 내에 없거나, 오류가 논리 연산 작업 중 발생했다면 철회된 트랜잭션을 폐기한다.
- 종료 상태 : 트랜잭션을 완전히 빠져나온 상태(종료 상태를 빼고 5가지로 보기도 한다.)
트랜잭션의 회복과 병행 제어
데이터베이스의 데이터를 변경하기 위해서는 디스크에 있는 데이터베이스의 데이터를 메모리로 가져온 후 변경시켜 다시 데이터베이스에 저장한다.
회복
트랜잭션에서는 원자성(Atomicity)과 지속성(Durability)을 위해 회복 기능을 제공한다. 장애가 발생했을 때, 장애가 발생하기 전의 데이터베이스 상태로 복구시킨다. 회복 단계에서는 데이터베이스를 사용할 수 없으므로 빠른 시간에 회복시키는 것이 관건이다.
회복을 위한 연산
- Dump : 데이터 베이스 전체를 다른 저장 장치에 주기적으로 복사
- log : 데이터베이스에서 변경 연산이 실행될 때마다 데이터의 변경 이전과 변경 이후의 값을 별도의 파일에 기록
- redo (재실행) : 가장 최근에 저장된 DB의 정보를 가져와 log의 내용을 바탕으로 실행된 변경 연산을 재실행하여 장애가 발생하기 직전의 DB 상태로 복구(전반적으로 손상된 경우에 주로 사용)
- undo (취소) : log를 이용해 지금까지 실행된 모든 변경 연산을 취소하여 DB를 원래 상태로 복구
회복 기법
- 로그 회복 기법 : log를 이용한 회복 기법
- 즉시 갱신 회복 기법 : 트랜잭션 작업 도중에 변경된 데이터를 DB에 즉시 반영, 장애 발생에 대비하기 위해 로그파일에 변경 내용을 기록 후 DB에 반영한다. commit이 발생되고 장애가 발생했다면 변경 연산을 모두 재실행해야 하므로 redo, commit log가 찍히기 전에 장애가 발생했다면 undo로 되돌리자.
- 지연 갱신 회복 기법 : 트랜잭션 작업이 완료되면 한 번에 DB에 반영. 따라서 log 파일에는 트랜잭션이 끝나기 전까지 commit log가 찍히지 않게 된다. 트랜잭션 작업 도중 장애가 발생한다면 log 내용만 무시하고 버리면 되고, 트랜잭션이 완료 후에 장애가 발생했다면 redo 연산으로 재실행하면 된다. 따라서 undo 연산은 필요 없다.
- 검사 시점 회복 기법 : 로그 회복 기법의 redo, undo가 비효율적이라 생각하여 만든 기법으로, 일정 간격으로 checkpoint를 생성한다. 그리고 장애가 발생하면 checkpoint 시점의 DB 상태로 되돌려 회복한다.
- 미디어 회복 기법 : Disk에 문제가 생길 경우에 대비한 회복 기법. 일정 간격으로 DB의 정보를 다른 Disk에 저장한다.
병행 제어
트랜잭션에서는 일관성(Consistency)과 격리성(Isolation)을 위해 병행 제어 기능을 제공한다. DB는 다수의 사용자가 이용하기 때문에 병행 수행을 해야 성능을 높일 수 있다. 하지만 병행 수행을 함으로써 발생할 수 있는 문제를 대비하지 않을 경우 데이터의 원자성과 일관성은 깨지게 된다. 동시다발적으로 발생하는 트랜잭션이 서로에게 영향을 미치지 않게끔 적절한 상황에 병행 제어를 해주어야 한다.
트랜잭션 스케줄
인터리빙 방식 : 트랜잭션들이 차례로 번갈아 가면서 수행되는 방식
- 직렬 스케줄 : 인터리빙 방식을 이용하지 않고 각 트랜잭션 별로 연산들을 순차적으로 실행시키는 것. 정확한 결과를 얻을 수는 있지만 병행 수행이라고는 할 수 없어 잘 쓰이지 않음
- 비직렬 스케줄 : 인터리빙 방식을 이용하여 트랜잭션을 병행해서 수행하는 것. 최종 결과를 보장할 수 없다.
- 직렬 가능 스케줄 : 인터리빙 방식을 사용하면서 직렬 스케줄과 같이 정확한 결과를 생성하는 스케줄. 병행 제어 기법을 통해 직렬 가능성을 보장한다.
병행 제어 기법
- locking 기법 : 데이터에 대한 접근 권한을 두어 하나의 트랜잭션만이 데이터에 접근할 수 있다. 해당 트랜잭션은 데이터에 대한 사용이 끝났다면 권한을 반환한다. 다만 locking 범위가 커질 경우 병행성이 낮아진다.
- 2단계 locking 규약 : 트랜잭션이 lock과 unlock 연산을 확장 단계와 축소 단계로 나누어 수행해야 함. 이 규약을 준수하면 직렬 가능성을 보장받을 수 있음
참고하면 좋은 사이트
'CS > 데이터베이스' 카테고리의 다른 글
[Database] MongoDB 이해하기 (1) | 2024.01.03 |
---|---|
[Database] NoSQL 이해하기 (0) | 2024.01.03 |
[DATABASE] 인덱스란? (1) | 2023.10.26 |
Key of Database (0) | 2022.08.17 |