용어정리
트랜잭션
은 작업의 완전성을 보장해주는 것. 즉, 논리적인 작업셋을 모두 완벽하게 처리하거나 또는 처리하지 못할 경우, 원 상태로 복구하여 작업의 일부만 적용되는 현상이 발생하지 않게 만들어주는 기능잠금
은 동시성을 제어하기 위한 기능이고트랜잭션
은 데이터의 정합성을 보장하기 위한 기능- 하나의 회원 정보 레코드를 여러 커넥션에서 동시에 변경하려고 할 때 잠금이 없다면 하나의 데이터를 여러 커넥션에서 동시에 변경해버릴 수 있으므로
잠금
은 여러 커넥션에서 동시에 동일한 자원을 요청할 경우 순서대로 한 시점에는 하나의 커넥션만 변경할 수 있게 해주는 역할 격리 수준
이라는 것은 하나의 트랜잭션 내에서 또는 여러 트랜잭션 간의 작업 내용을 어떻게 공유하고 차단할 것인지를 결정하는 레벨을 의미
트랜잭션
- 트랜잭션은 꼭 여러 개의 변경 작업을 수행하는 쿼리가 조합됐을 때만 의미 있는 개념은 아님
- 트랜잭션의 특성 (ACID)
- Atomicity 원자성 : 하나의 논리적인 작업 셋에 하나의 쿼리가 있든 두 개 이상의 쿼리가 있든 관계없이 논리적인 작업 셋 자체가 100% 적용되거나 또는 아무것도 적용되지 않아야 함을 보장해주는 것
- Consistency 일관성 : 트랜잭션이 성공적으로 완료되면 일관적인 DB 상태를 유지하는 것, DB의 상태, DB 내부의 계층관계, 컬럼의 속성 등이 항상 일관되게 유지되어야 함
- Isolation 격리성 : 트랜잭션 수행 시 다른 트랜잭션의 작업이 끼어들지 못하도록 보장하는 것, 각 트랜잭션은 독립적으로 수행되어야 함
- Durability 지속성 : 성공적으로 수행된 트랜잭션은 영원히 반영이 되어야 함,
트랜잭션 사용 시 주의해야 할 사항
- 트랜잭션 사용 시 트랜잭션의 범위를 최소화하는 것이 좋음
- 일반적으로 DB 커넥션은 개수가 제한적이기 때문에 각 커넥션을 프로그램이 오래 물고 있을수록 병목이 발생할 확률이 커짐
- 아래 예시에서 실제로 DBMS에 데이터를 저장하는 작업은 5번부터 시작하므로 2,3,4번은 굳이 DBMS의 트랜잭션으로 포함시킬 필요없음
- 메일 전송, FTP 파일 전송, 네트워크 통신과 같은 외부 자원과의 연결 작업은 트랜잭션 내에서 제거하는 것이 좋음
- 해당 작업이 실패하거나 시간이 오래 걸리는 경우 프로그램 전체가 위험해질 수 있음
- 가능하다면 트랜잭션 각각의 범위도 최소화하는 것이 좋음
- 모든 작업이 트랜잭션이 필요하다고 해서 한 단위로 묶는 것보다 여러 개의 트랜잭션으로 쪼개서 각 범위를 최소화하는 것이 좋음
1. 처리 시작
==> 데이터베이스 커넥션 생성 및 트랜잭션 시작
2. 사용자의 로그인 여부 확인
3. 사용자의 글쓰기 내용의 오류 여부 확인
4. 첨부로 업로드된 파일 확인 및 저장
5. 사용자의 입력 내용을 DBMS에 저장
6. 첨부 파일 정보를 DBMS에 저장
7. 저장된 내용 또는 기타 정보를 DBMS에서 조회
8. 게시물 등록에 대한 알림 메일 발송
9. 알림 메일 발송 이력을 DBMS에 저장
<== 트랜잭션 종료 및 데이터베이스 커넥션 반납
10. 처리 완료
1. 처리 시작
2. 사용자의 로그인 여부 확인
3. 사용자의 글쓰기 내용의 오류 여부 확인
4. 첨부로 업로드된 파일 확인 및 저장
==> 데이터베이스 커넥션 생성 및 트랜잭션 시작
5. 사용자의 입력 내용을 DBMS에 저장
6. 첨부 파일 정보를 DBMS에 저장
<== 트랜잭션 종료
7. 저장된 내용 또는 기타 정보를 DBMS에서 조회
8. 게시물 등록에 대한 알림 메일 발송
==> 트랜잭션 시작
9. 알림 메일 발송 이력을 DBMS에 저장
<== 트랜잭션 종료 및 데이터베이스 커넥션 반납
10. 처리 완료
트랜잭션 격리 수준
- 트랜잭션의 격리 수준이란 동시에 여러 트랜잭션이 실행될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것
- 3가지 부정합의 문제
- DIRTY READ : 어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있게 되는 현상
- NON-REPEATABLE READ : 하나의 트랜잭션 내에서 똑같은 SELECT 쿼리를 실행했을 때 항상 같은 결과를 가져와야하는 Repeatable Read에 어긋나는 것
- PHANTOM READ : 다른 트랜잭션에서 수행한 변경작업에 의해 레코드가 보였다 안 보였다 하는 현상
- MySQL의 네 가지 격리 수준 : 뒤로 갈수록 각 트랜잭션 간 데이터 격리 정도 ⬆ 동시에 동시성 ⬇
- READ UNCOMMITED
다른 트랜잭션의 변경 내용이 커밋, 롤백 여부에 상관없이 내 트랜잭션에 보여짐 (DIRTY READ)
데이터가 나타났다가 사라졌다가 하는 현상을 초래하므로 개발자와 사용자를 혼란스럽게 함 - READ COMMITTED : 오라클 DBMS에서 기본적으로 사용되는 격리 수준
다른 트랜잭션이 데이터를 변경하더라도 커밋이 완료된 데이터만 내 트랜잭션에서 조회가능
사용자 A가 커밋하기 전에 이미 데이터의 변경이 일어났어도, 사용자 B는 언두영역의 백업된 레코드에서 변경 전 데이터를 읽어옴
사용자 A가 데이터를 변경하고 커밋하는 과정 앞뒤로 사용자 B가 같은 데이터를 두 번 조회할 경우 서로 다른 결과가 나올 수 있다는 문제가 있음 (NON-REPEATABLE READ) - REPEATABLE READ : MySQL InnoDB 스토리지 엔진에서 기본적으로 사용되는 격리 수준
트랜잭션의 롤백을 대비해 변경 전 레코드를 언두 영역에 백업해두고 실제 레코드 값 변경 (MVCC)
동일 트랜잭션 내에서는 동일한 결과를 보여줄 수 있도록 보장
2.와 3. 모두 언두 영역에 백업하지만 여러 버전 중 몇 번째 버전까지 찾아 들어가야 하는지에 차이가 있음
사용자 A가 INSERT를 진행할 경우, 사용자 B의 조회 결과 값의 수가 달라질 수 있음 (PHANTOM READ)- MySQL InnoDB에서 PHANTOM READ 발생하지 않는 이유
- MVCC Consistent Read
- Next Key Lock : Record Key Lock + Gap Key Lock
- MySQL InnoDB에서 PHANTOM READ 발생하지 않는 이유
- SERIALIZABLE : 가장 단순하면서 가장 엄격한 격리수준
PHANTOM READ 발생하지 않으나 동시성이 많이 떨어짐
읽기 작업까지도 읽기 잠금을 획득해야만 읽기를 진행할 수 있음
잠금
잠금
은 트랜잭션 처리의 순차성을 보장해주고 데이터의 일관성을 보장, 하나의 트랜잭션이 완벽하게 끝날 때까지 다른 요청을 막아줌- SELECT vs SELECT FOR UPDATE
- SELECT : 읽기 잠금
- SELECT FOR UPDATE : 쓰기 잠금
- Shared Lock (Read Lock) : 데이터를 읽을 때 사용
- 테이블에 쓰기 잠금이 걸려 있지 않으면 바로 읽기 잠금을 획득하고 읽기 작업을 시작할 수 있음
- 같은 Read Lock 끼리는 동시 접근 가능 (데이터 변경 없이 읽기만 하니까)
- Exclusive Lock을 막음
- Exclusive Lock (Write Lock) : 데이터를 변경할 때 사용
- 테이블에 아무런 잠금이 걸려있지 않아야만 쓰기 잠금 획득가능
- 해당 Lock이 해제되기 전까지는 다른 Lock 설정 불가능 (읽기/쓰기 불가능)
- Blocking (Race Condition) : Lock들의 경합이 발생한 상태
- Lock들의 경합이 발생하여 특정 세션이 작업을 진행하지 못하고 멈춰 선 상태
읽기 잠금과 쓰기 잠금
또는쓰기 잠금과 쓰기 잠금
끼리 발생 가능- 해결하는 방법은 Transaction commit 또는 rollback 뿐
- SQL 문장이 최대한 빠르게 실행될 수 있도록 리팩터링
- 트랜잭션을 가능한 짧게 정의
- 동일한 데이터를 동시에 변경하는 작업 지양
- 대용량 작업이 불가피할 경우, 작업단위를 쪼개거나 lock_timeout을 설정하여 해당 Lock의 최대시간 설정
- Dead Lock (교착 상태) : 트랜잭션 간 교착상태
- 두 개의 트랜잭션이 서로의 리소스의 Lock을 획득하려고 할 때 발생
-
1번 트랜잭션에서 2번 리소스에 대해 읽기 잠금, 2번 트랜잭션에서 1번 리소스에 대해 읽기 잠금을 획득한 상태에서 동시에 상대방의 데이터에 액세스를 하려고 할 때 계속 기존 Lock이 해제될 때까지 기다리는 상황
-
1번 트랜잭션이 읽기 잠금을 획득하고 Sleep 상태인 상황에서 2번 트랜잭션이 쓰기 잠금을 설정하려고 할 때 무기한 기다리게 됨