레이블이 트랜잭션인 게시물을 표시합니다. 모든 게시물 표시
레이블이 트랜잭션인 게시물을 표시합니다. 모든 게시물 표시

2009년 6월 27일 토요일

Falcon : 트랜잭션제어3

처리 대상의 레코드만 lock걸기

처리대상의 레코드만 lock을 걸어보자.

처리예
1. 트랜잭션1 START TRANSACTION;
2. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE value=1;
3. 트랜잭션2 START TRANSACTION;
4. 트랜잭션2 UPDATE tbl2 SET value=100 WHERE value=2;

value컬럼에는 인덱스가 없기 때문에 full scan을 수행한 다음에 조건을 만족하는 레코드만을 갱신하게 된다.

이때 Falcon에서는 어디도 lock대기로는 되지 않지만 InnoDB에서는 4.에서 lock대기상태가 된다.

이것은 InnoDB에서는 갱신대상 레코드만 아니라 unique인덱스검색의 경우를 빼고 lock대상의 레코드 범위를 넓혀서 다른 트랜잭션의 INSERT문등을 막아내기 위한 특징이다.

다른 예도 알아보자.

처리예2
1. 트랜잭션1 START TRANSACTION;
2. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE id >= 10;
3. 트랜잭션2 START TRANSACTION;
4. 트랜잭션2 INSERT INTO tbl2 VALUES(11,11);
5. 트랜잭션2 COMMIT;
6. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE id >=10;
7. 트랜잭션1 COMMIT;
8. 트랜잭션1 SELECT * FROM tbl2 WHERE id >= 10;

Falcon의 경우 모든 처리가 lock대기상태에 빠지지 않고 끝난다.
또 8.의 결과는 다음과 같이 된다.

실행예
mysql> SELECT * FROM tbl2 WHERE id >= 10;
+---------+-------------+
| id | value |
+---------+-------------+
| 10 | 100 |
| 11 | 11 |
+---------+-------------+

의외라고 생각할지도 모르겠지만 id=11의 value컬럼은 6. UPDATE문에서 갱신대상이 되지 않고 4.에 넣었다 값 11이 그대로 남아 있게 된다.

2.와 6.에 따른 갱신대상 레코드는 같고(id=10의 레코드만) 다른 트랜잭션에서의 INSERT결과에 따른 영향을 받지 않는다. (분리레벨 Repeatable Read의 경우. Read Committed의 경우는 id=11의 value 값도 100이 된다. )

InnoDB의 경우, 2.의 영향으로 4.가 lock대기상태가 된다. 이것에 따라서 2.하고 6.의 갱신결과는 같게된다.

예상외의 레코드가 갑자기 갱신되어버리는 사태를 방지할 수 있게 되지만 동시 실행성이 떨어지는 결점이 있다.

InnoDB에서는 innodb_locks_unsafe_for_binlog파라미터로 처리 대상레코드만을 lock되게 되지만 이번에는 6.에 의해서 id=11의 컬럼값이 100으로 갱신되어버리게 된다.

또, SQL문에 따른 replication시에 문제를 일으키는등의 과제도 있으므로 안이한 설정은 금물이다.

Falcon에서는 관계없는 레코드에 lock을 거는 것 없이 이런 문제를 회피하는 것이 가능하다.




2009년 6월 25일 목요일

Falcon : 트랜잭션 제어2

Lost Update의 자동인식

Falcon에서는 여러개의 트랜잭션이 UPDATE문에서 같은 레코드를 타이밍 다르게 갱신했을 때 먼저 온 놈이 이기는 것을 보증하는 것으로 lost update를 방지하는 로직을 가지고 있다.

구체적으로 예를 함 들어보자.

테이블과 레코드 작성예
mysql>CREATE TABLE tbl2 (id INTEGER AUTO_INCREMENT PRIMARY KEY, value INTEGER) ENGINE=Falcon;
mysql>INSERT INTO tbl2 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10);

lost update확인을 위한 처리예
1. 트랜잭션1 START TRANSACTION;
2. 트랜잭션2 START TRANSACTION;
3. 트랜잭션1 SELECT value FROM tbl2 WHERE id=1; //1이 나온다.
4. 트랜잭션2 SELECT value FROM tbl2 WHERE id=1; //1이 나온다.
3. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE id=1;
4. 트랜잭션2 UPDATE tbl2 SET value=10 WHERE id=1;
5. 트랜잭션1 COMMIT;
6. 트랜잭션2 COMMIT;

이 경우 value컬럼값은 InnoDB에서는 트랜잭션1의 갱신값이 트랜잭션2에 의해 덮어씌워져 10이 된다.

그러나 Falcon에서는 어느 분리레벨이어도 6.의 단계에서 다음과 같은 에러가 나와서 최종적으로 value컬럼값은 트랜잭션1에 의한 값인 100이 된다.

ERROR 1020(HY000): Record has changed since last read in table 'tbl2'

InnoDB의 경우 갱신결과는 lost update라는 현상이다. 나중의 녀셕이 이기는 꼴이 되지만 모든 갱신이 성공했다라는 응답이 나오므로 먼저 갱신했던 쪽은 나중에 당황하게 된다.

많은 경우에 많아들여지지 않을지 모르겠지만 대처법도 많이 준비되어있다.

전형적인 것은 SELECT FOR UPDATE로 검색시에 배타lock을 거는 방법이다.

이 예에서는 4.에서의 트랜잭션2에 의한 검색이 5.에서의 트랜잭션1에 의한 커밋이 되기 전까지 기달리게 됨으로 4.에서 취득한 값은 100이 되게 되고 그 값을 확인한 후의 처리가 가능하게 된다.

Falcon의 경우는 갱신시점에서의 컬럼값과 트랜잭션개시 시점의 컬럼값을 비교해서 다른 트랜잭션에 의한 갱신때문에 값이 변화되어 있으면 갱신을 하지 않고 에러처리하게 되는 것이다.

웹 어플리케이션처럼 화면표시를 위한 검색에서 1개의 트랜잭션, 입력값으로 부터 갱신을 위한 1개의 트랜잭션처럼 트랜잭션이 나누어져 있는 경우에는 안타깝지만 효과는 없다.

이 경우는 정석으로 버전번호관리용 정수형 컬럼을 추가하는 것이 좋을 듯 싶다.






2009년 6월 24일 수요일

Falcon : 트랜잭션 제어( MVCC와 배타제어)

트랜잭션중에 정합성제약위반등에 의해 SQL문이 에러가 냈을 경우, 그 SQL문만이 롤백되는 것이지 트랜잭션 전체가 롤백되지는 않는다. 이것은 InnoDB도 그렇고 Falcon에서도 마찬가지이다.

Lock에 의한 배타제어에 대해서는 조금더 파내려가보자.
Falcon에서는 InnoDB와 마찬가지로 행레벨 lock을 지원한다.
행레벨 lock기능이 없으면 페이지단위나 테이블단위등 필요이상의 lock을 확보해버리기 때문에 동시 실행성이 크게 떨어지게 된다.

매우 중요하고 기본적인 기능인 것이다.

또, Multi Version Concurrency Control(MVCC)도 채용하고 있다. 이것은 읽어들일 때에 레코드를 lock하는 것이 아니라 커밋된 값을 읽는 것이 가능한 기능으로 읽는 것과 갱신의 경합을 방지할 수 있다.

한편 읽어들 일때에 강제적으로 배타lock을 거는 SELECT FOR UPDATE도 지원되고 있다. (베타판을 예정) 이것도 InnoDB하고 마찬가지이다.

또 트랜잭션 분리레벨은 현재에는 Read Committed와 Repeatable Read를 지원하고 있다.
Serializable은 베타판까지는 지원될 예정이다.

한편 Read Uncommitted는 그 필요성이 낮기 때문에 서포트예정은 없다. 기본적인 트랜잭션분리레벨은 InnoDB하고 마찬가지로 Repeatable Read이다.

InnoDB에서는 Repeatable Read로 한 경우에 동일 트랜잭션내에서의 SELECT문이 항상 같은 결과를 주는 특징이 있지만 (SELECT FOR UPDATE나 SELECT LOCK IN SHARE MODE로 lock을 거는 경우나 자기자신의 트랜잭션내에서 갱신한 정보는 빼고... ) Falcon도 마찬가지이다.

이 때문에 InnoDB에서도 Falcon에서도 동일 트랜잭션내에서의 읽어들인 결과가 다른 트랜잭션의 영향에 따라서 같이 변해버리는 퀀텀리드현상을 막을 수 있게된다.

InnoDB에서는 Repeatable Read와 바이너리로그 특징을 활용해서 Lock을 거의 사용하지 않는 온라인 백업과 rollforward recovery가 되지만 Falcon에서도 이런 기능을 서포트할 예정이다.

그외에 다음과 같은 특징이 있다.

  • 일부 RDBMS에서는 행Lock에서 관리하는 레코드수가 많아지면 Lock을 페이지 레벨이나 테이블 레벨로 승격하는 Lock escalation이 수행되는 것도 있지만 이것은 Falcon에서도 InnoDB에서도 발생하지 않는다. Lock escalation은 동시 실행성을 극단적으로 떨어트리는 하나의 원인이 되므로 이것이 발생하지않는 것은 중요하다.
  • deadlock의 검사는 자동적으로 수행된다. 이것도 Falcon, InnoDB 모두 같다.
이렇게 보면 트랜잭션제어에 대해서 InnoDB도 Falcon도 완전히 같은 것으로 보여지지만 Falcon 우위적인 차이가 몇개 있으니 다음에 알아보자.





2009년 4월 20일 월요일

InnoDB - 트랜잭션5

○트랜잭션 분리 레벨의 변경

InnoDB 트랜잭션 분리레벨은 표준으로는 REPEATABLE READ로 되어있다. 

사용 분리레벨을 변경하는 경우에는 다음과 같이 조작한다. 

트랜잭션분리레벨을 READ COMMITTED로 변경
mysql> SET TRANSACTION ISOLATION LEVEL Read Committed;

○MySQL표준의 트랜잭션 분리레벨의 변경
MySQL의 표준 트랜잭션 분리레벨은 REPEATABLE READ이다. 이것을 변경할 때는 가각 다음과 같이 조작한다. 

1. SET문을 이용해서 트랜잭션 분리레벨을 변경한다. 

  SET문에서 tx_isolation변수의 값을 변경함으로써 표준 트랜잭션분리레벨을 변경가능 하다. 
서버 변수를 이용하면 서버를 멈추지 않고 MySQL표준 트랜잭션분리레벨을 변경가능하다. 

SET문으로 변경한다.
mysql>SET GLOBAL tx_isolation="read-committed";

SET문으로 지정한 경우에는 명령을 실행한 후에 접속한 클라이언트에 대해서만 유효하다. 
SET문 실행중에 접속하고 있던 클라이언트(SET을 실행하고 있던 자신포함)에는 영향을 끼치지 않는다. 또 tx_isolation변수에 설정하는 값에는 다음과 같이 하이픈(-)를 적어 넣는 것을 잊어버리지 말기 바란다. 

READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE

이 서식은 GLOBAL이외에도 SESSION 지정도 가능하다.  SESSION의 경우는 자신이 사용하는 분리레벨의 설정이 된다. 

2. --transaction-isolation옵션으로 트랜잭션 분리레벨을 변경한다. 
명령어 라인에서 --transaction-isolation옵션을 사용해서 변경하는 경우는 다음과 같이 조작한다. 

기동할 때 옵션을 변경한다. 
mysqld --transaction-isolation=read-committed 

이 경우에는 지정하는 트랜잭션 분리레벨에 하이픈(-)을 넣을 필요가 있다. 
또 이 옵션을 설정파일인 my.cnf 또는 my.ini의 [mysqld] 그룹에 추가하는 것으로 트랜잭션 레벨을 변경할 수 있다. 

설정파일을 추가한다. 
[mysqld]
transaction-isolation=read-committed






2009년 4월 15일 수요일

InnoDB - 트랜잭션4

○4개의 트랜잭션 분리레벨

여러개의 클라이언트가 동시에 같은 테이블에  대해서 트랜잭션처리를 실행하는 경우는 데이터의 조회, 저장의 동작이 문제가 되곤 한다. 
이것을 ANSI/ISO SQL규격에서 규정한 것이 「트랜잭션 분리레벨」이다. 

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE
InnoDB는 이 4개의 트랜잭션 분리레벨을 전부 지원하고 있다. 
그러나 NDB하고 BDB는 READ COMMITTED만을 지원하고 있다. 

MySQL에서는 SQL문에서 사용하는 트랜잭션 분리레벨을 지정가능하다. 
이 4개의 트랜잭션 분리레벨에 대한 데이터의 조회방법의 차이는 다음과 같다. 

 dirty readnon repeatable readphantom read
READ UNCOMMITTEDooo
READ COMMITTEDxoo
REPEATABLE READxxo
SERIALIZABLExxx

o:발생을 허가, x:발생하지 않음

1. dirty read
여러 개의 클라이언트가 동시에 테이블에 접속해 있다고 하자. 
이 때 한쪽에서 아직 COMMIT하고 있는 데이터를 다른 한쪽에서 조회가 가능한 상황이 dirty read이다. 

2. non-repeatable read (반복불능 조회)
여러 개의 클라리언트가 동시에 테이블에 접속해 있다고 하자. 
한쪽(A)이 어떤 레코드를 SELECT하고 다른 한쪽(B)가 A가 SELECT한 레코드를 갱신하고 COMMIT한다. 
여기에서 다시 A가 같은 레코드를 SELECT했을 때  값이 B가 변경한 것으로 변해 있는 상황이 non repeatable read이다. 
다시 말하면 지난번 조회내용과 이번 조회내용이 다르다는 것이다. 

3. phantom read
non-repeatable read가 UPDATE에 관한 사항이었던 것에 비해 phantom read는 INSERT에 관한 사항이다. 
한쪽(A)이 어떤 레코드를 SELECT한 후 다른 한쪽(B)가 새로운 레코드를 INSERT하고 COMMIT한다고 하자.  여기에서 다시 A가 같은 조건으로 레코드를 조회할 경우 B가 추가한 새로운 레코드도 조회되는 상황이 phantom read이다. 
지난번 조회내용과 이번 조회내용이 다르다.


InnoDB에서는 REPEATABLE READ대해서 phantom read는 발생하지 않는다. 
또 InnoDB의 SERIALIZABLE는 lock방법을 명시하지 않은 모든 SELECT문을 
SELECT ... LOCK IN SHARE MODE로서 취급한다. 






2009년 4월 11일 토요일

InnoDB - 트랜잭션2

◎트랜잭션이 무효인 SQL문

MySQL에서는 모든 SQL문이 트랜잭션 대상이 되지는 않는다. 
다음과 같은 SQL문이 실행되었을 때  만약 트랜잭션모드이었다고 하더라도 자동적으로  commit되어버린다.  주의 해야한다!

  • ALTER FUNCTION  ※묵시적 commit은 5.0.13부터..
  • ALTER PROCEDURE ※묵시적 commit은 5.0.13부터..
  • ALTER TABLE
  • BEGIN
  • CREATE DATABASE  ※묵시적 commit은 5.0.8부터..
  • CREATE FUNCTION  ※묵시적 commit은 5.0.13부터..
  • CREATE INDEX
  • CREATE PROCEDURE  ※묵시적 commit은 5.0.13부터..
  • CREATE TABLE      ※묵시적 commit은 5.0.8부터..
  • DROP DATABASE  ※묵시적 commit은 5.0.8부터..
  • DROP FUNCTION  ※묵시적 commit은 5.0.13부터..
  • DROP INDEX
  • DROP PROCEDURE  ※묵시적 commit은 5.0.13부터..
  • DROP TABLE
  • LOAD MASTER DATA
  • LOCK TABLES
  • LOAD DATA INFILE
  • RENAME TABLE
  • SET AUTOCOMMIT=1
  • START TRANSACTION
  • TRUNCATE TABLE  ※묵시적 commit은 5.0.8부터..
  • UNLOCK TABLES

◎스냅샷의 타이밍 
보통 InnoDB의 REPEATABLE READ에서는 SELECT한 시점의 데이터가 스탭샷용으로 보존된다.  그러나 START TRANSACTION WITH CONSISTENT SNAPSHOT의 경우에는 이 문장을 실행한 때의 데이터를 스냅샷용으로 보존한다.


InnoDB - 트랜잭션1

MySQL에서는 모든 스토리지 엔진이 트랜잭션기능을 지원하는 것은 아니다. 

트랜잭션을 지원하는 스토리지 엔진은 InnoDB하고 NDB, BDB이다. 

또  MySQL에는 auto commit 모드라는 개념이 있다. 
auto commit이 유효(MySQL의 표준)인 경우 트랜잭션개시에는 반드시 START TRANSACTION이나 BEGIN이 필요하다. 

이것을 잊어버리면 각 문장을 트랜잭션 처리가 되지 않고 실행할 때 곧바로 Commit되어 버린다. 

덧붙여 BEGIN은 Stored Preocedure에서도 사용하는 키워드이기도 하기 때문에 
이글에서 트랜잭션에 관해서는 START TRANSACTION을 사용하는 것으로 한다. 

START TRANSACTION예
mysql>START TRANSACTION
mysql>UPDATE t SET a=a+1 WHERE a<100;
mysql>INSERT t(a) values(200);
mysql>ROLLBACK;

MySQL에서는 트랜잭션중에  또다시 트랜잭션을 작성하는 것은 불가능하다. 
또 트랜잭션 도중에 COMMIT/ROLLBACK을 잊어버리고 다시 START TRANSACTION을 실행했을 때,  지금까지의 트랜잭션데이터는 데이터에 반영되어 버리기 때문에 주의 해야한다. 



2009년 2월 12일 목요일

트랙잭션 기능의 유무

트랜잭션기능의 유무에 따라서 스토리지 엔진를 분류한다. 
트랜잭션기능은  많은 시스템에 있어서 없어서는 안되는 기능의 하나이지만 성능면에서 보면 매우큰 오버헤드를 낳는 기능이기도 하다.  

트랜잭션기능이 필요하지 않는 경우에는 MyISAM등의 트랜잭션이 없는 스토리지엔진을 사용하는 것으로 고속화가 기대된다. 

현재 사용하고 있는 MySQL에서 어느 스토리지 엔진이 사용가능할 까는 SQL명령어 SHOW ENGINES으로 확인할 수 있다. 

사용가능한 스토리지엔진의 확인
mysql> SHOW ENGINES;