백엔드 개발자(node.js)가 되는 과정

트랜잭션의 기본 이해

soopy 2023. 7. 10. 08:58
728x90

Transaction

트랜잭션(Transaction)은 하나의 쿼리(작업) 단위를 하나의 묶음으로서 처리하는 기능을 의미한다.
가령 통장 입출금 시스템의 경우 A가 B에게 1000원을 송금하는 작업을 1. A가 B에게 송금한다. 2. B의 잔액이 1000원 증가한다. 와 같은 작업이 이뤄져야 하는데 해당 작업이 알 수 없는 오류로 인해 중단된다면 A가 보낸 1000원이 증발할 수도 있다. 바로 이러한 현상을 방지하기 위해서 트랜잭션이 존재한다.

Transaction의 특징 ACID

Atomicity(원자성) - 여러 쿼리 명령들을 하나의 작업 단위로 취급하여, 내부 쿼리 명령들이 전부 성공하거나, 아니면 모두 실패해야한다는 특징이다.
Consistency(일관성) - 트랜잭션 처리 과정에서 데이터의 일관성을 유지해야하는 특징입니다. 즉 트랜잭션의 성공, 실패에 따라 데이터 또한 처리완료 또는 처리 전 이렇게 중간 단계 없이 이분법적으로 데이터가 유지 되어야 한다는 것을 의미한다. 원자성이 쿼리 명령 그룹의 관점에서 본다면 일관성은 데이터 처리 그룹의 관점에서 보는 것을 의미한다.
Isolation(격리성) - 트랜잭션을 수행하는 중에는 외부에서 데이터에 접근하여 중간 상태를 보거나 변경할 수 없도록 구성하는 특징입니다. 만약 계좌 이체를 총 세번하는 트랜잭션이 처리 중인데 두 번째 계좌 이체 처리 후 누군가가 잔액을 조회할 수 있다면 안되는 일이기 때문에 작업 도중에는 해당 DB 오브젝트에 Lock을 걸어 또 다른 클라이언트의 접근을 막는다. 이는 여러 클라이언트가 하나의 DB에 동시에 접근할 수 있는 동시성(Concurrency)이 보장됨으로 인해 발생하는 문제인데 트랜잭션 처리 과정에서는 동시성 이슈를 방지하기 위해 트랜잭션을 수행한 유저에게만 해당 데이터를 점유할 수 있도록 하는 것이다.
Durability(지속성) - 트랜잭션을 성공적으로 수행했다면 수정된 데이터가 영구적으로 데이터베이스에 반영 되어야 한다는 특징이다. 이는 데이터베이스에 이상이 생겨 재부팅을 한다 하더라도 이전까지의 트랜잭션 변경 사항이 그대로 반영되어 지속시킬 수 있어야 함을 의미한다.

LOCK의 종류

하나의 트랜잭션이 수행되는 동안 외부 클라이언트의 점유를 방지하기 위한 수단이다.
동시성을 다소 해치게 되더라도 데이터의 일관성을 유지하기 위한 Trade-off를 조절하기 위해 여러 Lock의 종류가 존재한다.

Shared and Exclusive Locks

Shared Lock (공유락)의 경우 다른 트랜잭션이 읽기(READ)는 가능하지만 쓰기(WRITE)는 불가하도록 제한하는 Lock이다. 누군가가 읽고 있다면 수정이 불가하며, READ 전용 LOCK이라고도 한다.
Exclusive Lock (배타락)의 경우 다른 트랜잭션이 읽기, 쓰기 모두 불가하도록 제한하는 Lock이다. 누군가가 데이터를 점유하고 있다면 타 사용자는 점유 자체가 불가하며, WRITE 전용 LOCK이라고도 부른다.

LOCKING LEVEL

Global Locks | Database Locks - 데이터베이스의 모든 테이블에 락을 걸어, 현재 트랜잭션을 제외한 나머지 트랜잭션들이 모든 테이블을 사용할 수 없도록 만든다. 즉 한 사람씩 데이터 베이스에 접근할 수 있음을 의미한다.
Table Locks - 다른 사용자가 작업중인 테이블에 락을 걸어 테이블 단위로 동시에 수정하지 못하도록 한다.
Named Locks - 특정한 문자열을 점유하는 락을 의미한다.
Metadata Locks - 다른 사용자가 작업중인 테이블의 동일한 행과 동일한 데이터베이스의 객체에 락을 걸어 동시에 수정하지 못하도록 한다.

🚨 교착상태
락을 잘못 걸게되면 아무도 특정 리소스에 접근할 수 없게 되는 교착 상태(Dead Lock)에 빠지게 된다.
가령 A 트랜잭션이 a테이블에서 처리 후 b 테이블을 처리하는 작업이고, B 트랜잭션이 b테이블 처리 후 a 테이블을 처리하는 작업이라고 하자
A와 B가 비슷한 시기에 트랜잭션을 수행한다면 A는 a처리 후 b로 가야하는데 B가 b를 점유하고 있고, 반대로 B는 b처리 후 a로 가야하는데 A가 a를 점유하는 상황이 발생한다.
그러면 A와 B는 각자의 남은 트랜잭션을 수행하기 위해 무한정 대기하게 되는 교착 상태에 빠지게 된다.

Isolation Level

트랜잭션은 격리 수준을 구분하여 접근 권한의 정도를 구분한다.

READ UNCOMMITTED - 커밋 되지 않은 데이터의 읽기(Uncommitted Read)를 허용하는 격리 수준입니다. 이는 다른 트랜잭션에 의해 작업 중인 데이터를 읽는 것을 의미하므로 잘못된 참조를 부르게 된다. 이는 곧 일관성이 꺠짐을 의미한다.
READ COMMITTED - 커밋 된 읽기(Committed Read)만을 허용하고, SELECT 문을 실행할 때 공유락을 걸어 쓰기는 불가한 수준을 의미한다.
REPEATABLE READ - 트랜잭션이 완전히 종료될 때 까지 락을 유지합니다. 하지만 다른 트랜잭션에서 데이터를 삽입하는 것은 가능하므로 팬텀 읽기가 발생할 수 있는 문제점이 있다. 여기서 팬텀 읽기는 존재하지 않는 데이터를 참조하고 있는 상태를 의미한다. 즉 누군가 데이터를 중도 삽입했지만 점유 중인 트랜잭션 처리에 따라 즉시 삭제될 수도 있는데 이럴 경우 해당 데이터는 있다고 인식되지만 실제하지 않게 된다.
SERIALIZABLE - 가장 높은 수준의 격리 수준으로 점유 시 다른 클라이언트는 아무 것도 못한다. 동시성이 떨어지는 문제점이 존재한다.

MySQL 트랜잭션 사용의 기본 예시

-- MYTABLE 테이블을 생성합니다.
CREATE TABLE IF NOT EXISTS MYTABLE
(
    id       INT(11)      NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name     VARCHAR(255) NOT NULL,
    location VARCHAR(255) NOT NULL
);

-- 1번째 트랜잭션을 실행합니다.
START TRANSACTION;

-- SPARTA 테이블에 더미 데이터 3개를 삽입합니다.
INSERT INTO MYTABLE (name, location)
VALUES ('apple', 'SEOUL'),
       ('ball', 'BUSAN'),
       ('cat', 'DAEGU');

-- 1번째 트랜잭션을 DB에 적용합니다.
COMMIT;


-- 2번째 트랜잭션을 실행합니다.
START TRANSACTION;

-- 마이 테이블에 더미 데이터 3개를 삽입합니다.
INSERT INTO MYTABLE (name, location)
VALUES ('doll', 'SEOUL'),
       ('egg', 'BUSAN'),
       ('fish', 'DAEGU');

-- 2번째 트랜잭션을 롤백합니다.
ROLLBACK;
728x90
728x90