DataBase/MySQL

Real MySQL 트랜잭션

레알윙 2022. 4. 30. 14:27
반응형

트랜잭션

트랜잭션이란?

트랜잭션의 정의

논리적인 작업 셋을 모두 완벽하게 처리하거나, 처리하지 못할 경우에는 원상태로 복구해서 작업의 일부만 적용되는 현상이 발생하지 않게 만들어 주는 것 즉 트랜잭션은 데이터의 정합성을 보장하기 위한 기능

MySQL에서의 트랜잭션

하나의 논리적인 작업 셋에 하나의 쿼리가 있든 두 개 이상의 쿼리가 있든 관계없이 논리적인 작업 셋 자체가 100% 적용되거나(commit을 실행했을때) 아무것도 적용되지 않아야(RollBack 또는 트랜잭션을 RollBack시키는 오류가 발생했을 때)함을 보장해주는 것

잠금

잠금은 동시성을 제어하기 위한 기능

트랜잭션 수준과 잠금의 차이

잠금은 여러 커넥션에서 동시에 동일한 자원(레코드나 테이블)을 요청한 경우 순서대로 한 시점에는 하나의 커넥션만 변경할 수 있게 해주는 역할이다. 트랜잭션 격리수준은 하나의 트랜잭션 내에서 여러 트랜잭션 간의 작업 내용을 어떻게 공유하고 차단할 것인지를 결정

 

주의사항

DBMS 커넥션과 동일하게 꼭 필요한 최소의 코드에만 적용하는 것이 좋다. 즉 프로그램 코드에서 트랜잭션의 범위를 최소화해야 한다.

 

아래는 일반적인 트랜잭션 처리 과정이다.

1) 처리 시작
  ==> 데이터베이스 커넥션 생성
  ==> 트랜잭션 시작
2) 사용자의 로그인 여부 확인
3) 사용자의 글쓰기 내용의 오류 여부 확인
4) 첨부로 업로드된 파일 확인 및 저장
5) 사용자의 입력 내용을 DBMS에 저장
6) 첨부 파일 정보를 DBMS에 저장
7) 저장된 내용 또는 기타 정보를 DBMS에서 조회
8) 게시물 등록에 대한 알림 메일 발송
9) 알림 메일 발송 이력을 DBMS에 저장   
  <== 트랜잭션 종료(COMMIT)   
  <== 데이터베이스 커넥션 반납
10) 처리 완료

위의 순서는 아래의 문제가 발생

  • 실제로 많은 개발자가 데이터베이스의 커넥션을 생성(또는 커넥션 풀에서 가져오는)하는 코드를 1번과 2번 사이에 구현하며 그와 동시에 START TRANSACTION 명령으로 트랜잭션을 시작한다. 그리고 9번과 10번 사이에서 트랜잭션을 COMMIT하고 커넥션을 종료(또는 커넥션 풀로 반납)한다. 실제로 DBMS에 데이터를 저장하는 작업(트랜잭션)은 5번부터 시작된다는 것을 알 수 있다. 그래서 2번과 3번, 4번의 절차가 아무리 빨리 처리된다고 하더라도 DBMS의 트랜잭션에 포함시킬 필요는 없다. 일반적으로 데이터베이스 커넥션은 개수가 제한적이  각 단위 프로그램이 커넥션을 소유하는 시간이 길어질수록 사용 가능한 여유 커넥션의 개수는 줄어들 것이다. 그리고 어느 순간에는 각 단위 프로그램에서 커넥션을 가져가기 위해 기다려야 하는 상황이 발생할 수도 있다.
  • 더 큰 위험은 8번 작업이라고 볼 수 있다. 메일 전송이나 FTP 파일 전송 작업 또는 네트워크를 통해 원격 서버와 통신하는 등과 같은 작업은 어떻게 해서든 DBMS의 트랜잭션 내에서 제거하는 것이 좋다. 프로그램이 실행되는 동안 메일 서버와 통신할 수 없는 상황이 발생한다면 웹 서버뿐 아니라 DBMS 서버까지 위험해지는 상황이 발생할 것이다.  
  •  또한 이 처리 절차에는 DBMS의 작업이 크게 4개가 있다. 사용자가 입력한 정보를 저장하는 5번과 6번 작업은 반드시 하나의 트랜잭션으로 묶어야 하며, 7번 작업은 저장된 데이터의 단순 확인 및 조회이므로 트랜잭션에 포함할 필요는 없다. 그리고 9번 작업은 조금 성격이 다르기 때문에 이전 트랜잭션(5번과 6번 작업)에 함께 묶지 않아도 무방해 보인다(물론 업무 요건에 따라 달라질 수는 있겠지만). 이러한 작업은 별도의 트랜잭션으로 분리하는 것이 좋다. 그리고 7번 작업은 단순 조회라고 본다면 별도로 트랜잭션을 사용하지 않아도 무방해 보인다.

위의 문제를 보장한 다음 설계를 확인해보자

어떤 부분에서 문제가 있는지 보완해야할 부분이 무엇인지 알수 있다.

1) 처리 시작  
2) 사용자의 로그인 여부 확인
3) 사용자의 글쓰기 내용의 오류 발생 여부 확인
4) 첨부로 업로드된 파일 확인 및 저장
   ==> 데이터베이스 커넥션 생성(또는 커넥션 풀에서 가져오기)    
   ==> 트랜잭션 시작
5) 사용자의 입력 내용을 DBMS에 저장
6) 첨부 파일 정보를 DBMS에 저장   
   <== 트랜잭션 종료(COMMIT)
7) 저장된 내용 또는 기타 정보를 DBMS에서 조회
8) 게시물 등록에 대한 알림 메일 발송   
   ==> 트랜잭션 시작
9) 알림 메일 발송 이력을 DBMS에 저장   
   <== 트랜잭션 종료(COMMIT)   
   <== 데이터베이스 커넥션 종료(또는 커넥션 풀에 반납)
10) 처리 완료

 

 

MySQL 엔진의 잠금(Lock)

잠금의 종류

글로벌락

  • MySQL에서 제공하는 잠금 가운데 가장 범위가 크다.
  • 한 세션에서 글로벌 락을 획득하면 다른 세션에서 SELECT를 제외한 대부분의 DDL 문장이나 DML 문장을 싱행하는 경우 글로벌 락의 해재할 때까지 해당 문장이 대기상태로 남는다.
  • 데이터베이스에 존재하는 MYISAM이나 MEMORY 테이블에 대해 mysqldump로 일관된 백업을 받아야 할 때는 글로벌 락을 사용해야 한다.

테이블락

  • 개별 테이블 단위로 설정되는 잠금
  • 명시적 또는 묵시적으로 특정 테이블의 락을 획득할 수 있다.
    • 명시적인락은 사용자가 설정하는 락이고, 묵시적인 락은 쿼리가 실행되는 동안 자동으로 획득했다가 쿼리가 완료된 후 자동 해제되는 락을 뜻한다.

네임드락

  • 대상이 테이블이나 레코드 또는 AUTO_INCREMENT와 같은 데이터베이스 객체가 아니라는 것
  • 단순히 사용자가 지정한 문자열(String)에 대해 획득하고 반납(해제)하는 잠금
  • 데이터베이스 서버 1대에 5대의 웹 서버가 접속해서 서비스하는 상황에서 5대의 웹서버가 어떤 정보를 동기화해야 하는 요건처럼 여러 클라이언트가 상호 동기화를 처리해야 할 때 네임드 락을 이용
  • 많은 레코드에 대해서 복합한 요건으로 레코드를 변경하는 트랜잭션에 유용하게 사용
    • 배치 프로그램처럼 한꺼번에 많은 레코드를 변경하는 쿼리는 자주 데드락의 원인이다.
    • 각 프로그램의 실행 시간을 분산하거나 프로그램의 코드를 수정해서 데드락을 최소화할 수 있지만, 이는 간단한 방법이 아니며 해결책이 될 수도 없다. 이러한 경우에 동일 데이터를 변경하거나 참조하는 프로그램끼리 분류해서 네임드 락을 걸고 쿼리를 실행하면 아주 간단히 해결이 가능하다.
    •  

 

 

참고

Real MySQL 8.0.1

반응형