트랜잭션은 작업을 모두 완벽하게 처리(commit)하거나 또는 처리하지 못할 경우에는 원 상태로 복구(rollback)해서 작업의 일부만 적용되는 현상을 막아주는, 작업의 완전성을 보장해 주는 것
(참고) MyISAM에서 트랜잭션을 처리하기 위해선 위와 같은 코드가 필요하다고 한다.
프로그램 코드에서 트랜잭션의 범위를 최소화하라
게시물을 작성을 서버에서 처리하는 내용을 순서대로 정리한 것이다.
위 절차 중 트랜잭션 처리에 좋지 않은 영향을 끼치는 부분을 파악해보자.
최적의 트랜잭션 설계는 아닐 수 있지만, 아래와 같이 프로그램의 코드가 데이터베이스 커넥션을 가지고 있는 범위와 트랜잭션이 활성화돼 있는 프로그램의 범위를 최소화하는 것이 좋다.
- “각 트랜잭션이 같은 레코드를 변경할 가능성은 희박할 것”이라는 가정
- 변경 작업을 수행하고, 마지막에 잠금 충돌이 있었는지 확인해 문제가 있다면 Rollback 처리하는 방식을 의미
- “현재 변경하고자 하는 레코드를 다른 트랜잭션에서도 변경할 수 있다”라고 가정
- 현재 트랜잭션에서 변경하고자 하는 레코드에 대해 잠금을 획득하고, 변경 작업을 처리하는 방식
높은 동시성 처리
에는 비관적 잠금이 유리하며, InnoDB의 잠금 방식
InnoDB 스토리지 엔진은
레코드 기반
의 잠금 기능을 제공
- InnoDB 잠금은 레코드를 잠그는 것이 아니라 인덱스를 잠그는 방식으로 처리.
- 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 잠가야 한다.
-- // 예제 데이터베이스의 employees 데이블에는 아래와 같이 first name 칼렁만
-- // 멤버로 담긴 ix_firstname이라는 인덱스가 준비돼 있다.
-- // KEY ix_firstname (first_name)
-- // employees 테이블에서 first_name=‘Georgi’인 사원은 전체 253명이 있으며,
-- // first_name='Georgi'이고 last_name=‘KIassen’인 사원은 딱 1명만 있는 것을 아래 쿼리로
-- // 확인할 수 있다.
mysql> SELECT COUNT(*) FROM employees WHERE first_name=‘Georgi’;
+-------+
253
+-------+
mysql> SELECT COUNT(*) FROM employees WHERE first_name=‘Georgi’ AND last_name=‘KIassen’;
+-------+
1
+-------+
-- // employees 데이블에서 first_name=‘Georgi’이고 last_name=‘KIassen’인 사원의
-- // 입사 일자를 오늘로 변경하는 변경하는 쿼리를 실행해보자.
mysql> UPDATE employees SET hire date=NOW() WHERE first_name=‘Georgi’ AND last_name=‘KIassen’
update 문장의 조건에서 인덱스를 이용할 수 있는 조건은 frist_name=‘Georgi’
,
last_name칼럼은 인덱스에 없기 때문에 first_name=‘Georgi’ 레코드 253건 모두 잠긴다.
하지만, MySQL 5.1 이후부터는 위와 같이
1. 인덱스만으로 이일치 여부를 판단 : first_name=‘Georgi’ 레코드를 모두 잠근다.
2. 인덱스를 이용하지 않는 나머지 조건의 일치 여부를 판단 : 실제 업데이트 대상이 아니면 1번에서 걸었던 잠금을 해제
3. 최종적으로 first_name=‘Georgi’ AND last_name=‘Klassen’ 레코드에 대해서만 잠금을 가짐
만약, 이 테이블에 인덱스가 없다면,
테이블을 full scan
하면서 update 작업을 한다.
이 과정에서 테이블에 있는 30만 건의 모든 레코드를 모두 잠근다.
격리 수준이란 동시에 여러 트랜잭션이 처리 될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것이다.
각 트랜잭션에서의 변경 내용이 commit 또는 rollback 여부에 상관 없이 다른 트랜잭션에서 보여진다.
commit이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있다.
- 동일 트랜잭션 내에서는 동일한 결과를 보장한다.
- InnoDB의 격리 수준으로, 바이너리 로그를 가진 MySQL은 최소 REPEATABLE READ 격리 수준 이상을 사용해야 한다.
실행 중인 트랜잭션 중, 가장 오래된 트랜잭션 번호
보다 앞 선 언두 영역의 데이터
는 삭제할 수 없다.다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다가 안 보였다가 하는 현상
한 트랜잭션에서 읽고 쓰는 레코드들은 다른 트랜잭션에서는 절대 접근할 수 없다.