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년 6월 23일 화요일

Falcon 파일: 데이터형과 영역관리

Falcon에서는 GIS(지리정보형)을 포함, MySQL표준 데이터형을 전부 이용하는 것이 가능하다.

이 이외에도 데이터형에 관계없이 모든 컬럼이 가변형으로 다루어진다.

또 데이터형 자신의 정의된 사이즈가 아니라 실제로 사용된 사이즈만큼(+컬럼크기를 관리하기위한 영역)만이지만 파일영역으로 사용된다.

이것은 가변크기 문자열형(VARCHAR, TEXT등)만 아니라 CHAR형이나 정수형등에도 해당된다.

예를 들어 INTEGER형은 InnoDB나 MyISAM에서는 4바이트 고정사이즈 이지만 만약 값이 1이나 10등 1바이트로 표현가능한 범위이면 Falcon에서는 그 1바이트하고 컬럼사이즈를 관리하는 1바이트로 계 2바이트만 사용된다.

또 BLOB형을 효율있게 사용할수 있도록하고 있는 것이 Falcon의 특징이기도 하다.
BLOB형의 컬럼은 타른 컬럼과는 별도의 영역(page)에서 관리된다.

일반적으로 BLOB형의 실제 데이터는 다른 컬럼에 비교해 크게될 경향이 있다.
종래의 스토리지엔진에서는 특히 사이즈가 크게 바뀌는 갱신을 수행하게 되면 레코드의 fragmentation(단편화)가 문제가 되었다.

그러나 Falcon에서는 관리하는 페이지가 BLOB형과 그외의 것이 다르기 때문에 이런 문제를 회피할 수 있다.

또 레코드의 갱신을 수행할 경우등에 있어서 필요가 없는 한 물리적인 이동은 발생하지 않도록 하고 있다.

AUTO_INCREMENT동작의 차이

AUTO_INCREMENT(시퀀스)의 동작은 Falcon하고 InnoDB에서 약간 차이가 있다.
구체적으로 MySQL본체 프로세스 mysqld를 재기동한 다음, 그 다음에 할당되는 AUTO_INCREMENT값의 결정방식이 다음처럼 다르다.

  • Falcon, MyISAM: 해당 테이블에 지금까지 할당된 최대값 +1
  • InnoDB:해당 테이블의 현재의 최대값 +1
Falcon은 MyISAM하고 같은 방식을 채용하고 있다.

레코드의 삽입(오토커밋모드, 컬럼 id는 id int(11) NOT NULL AUTO_INCREMENT)
mysql>INSERT INTO tbl1(id) VALUES(null);
mysql>INSERT INTO tbl1(id) VALUES(null);
mysql>INSERT INTO tbl1(id) VALUES(null);
mysql>INSERT INTO tbl1(id) VALUES(100);
mysql>DELETE FROM tbl1 WHERE id=100;
mysql>SELECT id FROM tbl1;
+---+
| id |
+---+
| 1 |
| 2 |
| 3 |
+---+

이 다음 mysqld를 재기동하고 AUTO_INCREMENT값을 갱신시켜보자.

AUTO_INCREMENT값의 갱신
mysql>INSERT INTO tbl1(id) VALUES(null);

이 때 결과는 Falcon, MyISAM하고 InnoDB는 다르다.

Falcon, MyISAM의 경우
mysql>SELECT id FROM tbl1;
+----+
| id |
+----+
| 1|
| 2|
| 3|
| 101|
+----+

InnoDB의 경우
mysql>SELECT id FROM tbl1;
+----+
| id |
+----+
| 1|
| 2|
| 3|
| 4|
+----+

mysqld를 재기동하지 않는 경우는 어디든 101이 된다.






2009년 6월 22일 월요일

Falcon 파일의 구성

Falcon은 「데이터 파일」과 「시리얼로그 파일」라고 불리우는 2종류의 파일로 구성되어 있다.

전자는 테이블 데이터와 메타데이터등을 저장하기위한 것으로 InnoDB의 InnoDB데이터 테이블에 대응한다.

후자는 트랜잭션로그(REDO로그)를 보존하는 것으로 InnoDB에서의 InnoDB로그파일에 해당한다.

트랜잭션의 커밋이 일어날 때 시리얼 로그 파일에 대해서 추가적으로 동기 저장이 이루어진다.
(데이터 파일의 저장은 비동기)

이런 내용들은 InnoDB 로그 파일이나 다른 RDBMS의 REDO로그 파일하고 기본적으로 다른 것은 없다.

다만 InnoDB로그파일은 사이즈가 고정적인 것에 반해 시리얼 로그 파일의사이즈는 일정하지는 않다.

또 , 동시에 여러개의 쓰레드가 트랜잭션 커밋을 하는 경우에는 따로따로 저장하지 않고 모아서 디스크에 저장함으로 I/O횟수를 줄이는 「그룹 커밋」의 기술을 채용하고 있다.

이외에 Falcon에서는 「데이터파일에는 커밋되지 않은 정보는 쓰여지지않는다」라는 특징이 있다.

보통의 RDBMS에서는 대량의 테이블 데이터를 다루는 경우에 메모리공간이 부족해지기 전에 커밋전/커밋완료와 관계없이 일부 데이터를 데이터 파일과 REDO로그 파일에 강제적으로 저장이 이루어지는 「checkpoint」기술이 사용되고 있다.

Falcon에서는 이런 경우 커밋되지 않은 정보는 데이터 파일에 쓰지 않고 시리얼로그 파일에만 저장한다.
쓰여진 내용은 데이터 파일에 대해서 나중에 (비동기적으로 ) 한꺼번에 반영된다.

이 때 커밋한 정보는 데이터파일에 비동기적으로 반영되지만 롤백한 경우에는 해당 레코드는 단순히 무시되고 데이터파일에는 반영되지 않는다.

이 때문에 보통의 RDBMS하고는 달리 롤백에 따른 데이터파일에 대한 I/O가 필요없게 되기 때문에 롤백 부하가 매우 가볍게 된다.

다른 RDBMS에 비교하면 데이터파일에 랜덤 I/O횟수가 상대적으로 적게되고 시리얼로그에 쓰는 양이 상대적으로 많아질 것으로 생각되어진다.

문제가 되는 것은 대부분 I/O회수이므로 성능은 향상되는 것이 기대된다.

실제로 작성되는 파일
현재는 Falcon테이블을 작성하면 데이터디렉토리 밑에 한개의 데이터베이스당 한개의 데이터파일과 2개의 시리얼로그 파일이 작성된다.

db1데이터베이스에 Falcon테이블을 작성한 예
# ls
db1 db1.fl1 db1.fl2 db1.fts mysql test

db1.fts가 데이터 파일, db1.fl1하고 db1.fl2가 시리얼로그 파일이다.
안정판에서는 서버단위로 데이터 파일과 시리얼로그 파일을 작성하는 등 보다 많은 선택이 될 예정이다.
또 InnoDB 데이터 파일은 필요에 따라서 영역의 자동확장을 수행시키는 것이 가능한 것처럼 Falcon도 자동확장된다.




2009년 6월 21일 일요일

Falcon의 동작확인

Falcon은 MySQL AB의 Web사이트에서 입수가능하다. ( http://dev.mysql.com/downloads/mysql/5.2.html)

MySQL Community Server 하고 같이 운영체제에 따른 빌드된 바이너리파일이 제공되고 있기때문에 자기가 빌드할 필요는 없다.

또 당연하지만 소스코드도 공개되어있으므로 상세한 동작원리를 알고 싶은 경우에는 자기가 소스코드를 확인하는 것도 가능하다.

mysqld프로세스를 기동한 다음에 명령어 툴인 mysql로 접속해서 다음과 같은 명령어를 실행하면 Falcon이 이용가능한지 알 수 있다.

이용가능한 스토리지엔진 확인
mysql> SHOW ENGINES\G
...생략
********************** 4.row **********************
Engine: Falcon
Support: YES
Comment: Falcon storage engine
Transactions:YES
XA:NO
Savepoints:YES
.....생략

Engine: Falcon이라는 항목이 있고 Support: YES이면 Falcon이 이용가능하다는 뜻이 된다.

Falcon테이블의 작성은 CREATE TABLE문 말미에 ENGINE=Falcon을 붙여서 실행한다.

설정파일 my.cnf에서 default-storage-engine=Falcon을 지정해두면 ENGINE구를 생략한 경우에도 자동적으로 Falcon테이블이 된다.

Falcon테이블 생성
mysql> CREATE TABLE tbl1(id INTEGER AUTO_INCREMENT PRIMARY KEY, value VARCHAR(30) ) ENGINE=Falcon;
mysql> SHOW CREATE TABLE tbl1\G

트랜잭션제어는 다음과 같이 조작한다.

트랜잭션 개시
mysql> START TRANSACTION;
mysql> INSERT INTO tbl1 VALUES(null, 'abc');
mysql> ROLLBACK;
mysql> SELECT * FROM tbl1;
Empty set (0.00 sec)

롤백을 함으로 INSERT문이 무효화된다는 것을 알 수 있다.

보면 알겠지만 Falcon은 어디까지나 스토리지엔진의 하나에 지나치지 않으므로 어플리케이션에서 봤을 때의 조작방법(DDL/SQL문등)은 특별히 바뀌지는 않는다.

그것보다는 파일구성이나 아키텍쳐, 다른 스토리지엔진(특별히 InnoDB)와의 작은 동작의 차이등이 고민될 것이다.

기본적으로 RDBMS에 있어서 bottleneck이 되기 쉬운 디스크I/O 회수와 그 양을 줄이자라는 의도가 있으므로 이것을 염두에 보고 아래 내용들을 알아보자.

  • 파일의 구성
  • 데이터형과 영역관리
  • 트랜잭션
  • 프로세스/스레드, 메모리구성
  • 인덱스
  • 설정파라미터






2009년 6월 19일 금요일

새로운 스토리지엔진 Falcon

2007년 1월에 MySQL AB는 새로운 스토리지엔진인 Falcon의 알파버전을 릴리스 했다. (5.2.0-alpha)
Falcon은 현재 폭넓게 쓰이고 있는 스토리지엔진 InnoDB의 다른 선택지의 하나로써 기대받고 있다.

Falcon은 관계형 데이터베이스(RDBMS) 계의 권위자인 Jim StarKey씨와 그의 아내 Ann Harrison씨를 중심으로 개발이 진행되고 있다.

Jim StarKey씨는 InterBase의 아버지이자 또 Ann Harrison씨와 함께 Firebird의 메인커밋터로도 활약하고 있다.

그들은 Netfrastructure라 불리우는 회사에서 차세대 RDBMS의 개발을 진행하고 있었지만 2006년 2월에 MySQL AB가 Netfrastructure를 매수하게 됨으로 MySQL AB로 바뀌게 되었다.

Falcon의 가장 기본적인 특징은 트랜잭션을 서포트한다는 것이다.

커밋/롤백에 한하지 않고 row level lock, MVCC(Multi-Version Concurrency Control), lock을 걸지 않고 하는 online backup, crush recovery등은 어느것이던지 트랜잭션 지원을 기반으로 어플리케이션 개발에 있어서 매우 중요한 기능이다.

MySQL5.0에서 트랜잭션을 서포트하고 있는 스토리지엔진은 InnoDB와 NDB(MySQL Cluster)가 있다.

InnoDB는 MyISAM등과 마찬가지로 주로 MySQL 서버 1대 또는 Master/Slave형 replication구성에서 채용된다.

한편 NDB는 클러스터 구성을 전제로 하는 것으로 in-memory형의 고속, 고가용스토리지엔진이고 초당 수만~수십만트랜잭션등의 자릿수가 다른 처리능력을 요하는 환경에 쓰여지고 있다. (유럽과 미국를 중심으로 다수의 실적이 있다.)

Falcon은 이 중 InnoDB와 MyISAM과 같은 영역을 타겟팅하고 있다.

유저 입장에서 보는 기능은 InnoDB와 거의 비슷하므로 InnoDB의 대체적인 존재라고도 말해지고 있다. 그렇지만 Falcon은 InnoDB하고는 내부적으로 아키텍쳐가 전혀 다르고 InnoDB만 커버가능한 특징도 일부 존재한다.

이때문에 InnoDB상위호환(완전한 치환가능한 존재)이 되지는 않으므로 주의해야한다.

Falcon 안정판 릴리스시기는 MySQL 5.1의 다음 버전(5.2)을 예정하고 있다.
다만, 앞으로 개발상황이나 사용자의 요구에 따라서는 5.0이나 5.1에 back-porting될 가능성도 있다.


InnoDB에서만 커버되는 특징
InnoDB가 가지고 있지만 Falcon에는 없는 특징은 다음과 같은 것이 있다.
  1. Falcon에서는 cluster index를 채용하고 있지않다.
  2. Falcon에서는 분리 레벨 Read Uncommitted을 지원하지 않는다.
  3. 바이너리 로그의 기록방식인 「statement 기준(종래형)」,「행 기준」중에서 Falcon에서는 후자만 지원한다.
3의 「행 기준」기록형식은 MySQL 5.1이후에 지원되는 기능으로 바이너리 로그에의 기록형식이 종래의 DDL/DML문 그대로가 아니라 물리적인 값이 된다는 것이다.
InnoDB에서는 2개를 전부 지원하고 있지만 Falcon에서는 기존 방식으로 지원하지 않는다.
바이너리로그의 기록방식의 차이는 replication뿐만아니라 디스크장해가 있을 때의 복구에도 영향을 끼친다.
기존방식에서는 정확히 기록되지 않았던 UUID()등이 이용가능하다라는 merit가 있는 반면 전체적인 사이즈가 증가한다는 결점도 있다.

현 단계에서는 1,2,3 어느것도 지원할 계획은 없지만 요구에 맞게 장래적으로 지원될 가능성도 있을 것이다.

이것 이외에도 InnoDB는 가지고 있고 현시점의 Falcon이 가지고 있지 않는 중요한 기능이 몇개 더 있다.

SELECT FOR UPDATE, foreign key제약, online backup, Two-Phase Commit등은 베타버전에서는 지원될 예정이다.






2009년 6월 17일 수요일

MySQL Cluster도입시의 포인트2

데이터형의 선정

정수형, 부동소수점형, 날짜시각형에 대해서는 큰 문제는 없다.

문자열형에 대해서는 5.0까지는 VARCHAR형이어도 고정형으로 사이즈가 확보되어버리는 문제도 있었다. (예를 들어 VARCHAR(30)인 경우 30문자분)

5.1에서부터는 실제로 사용한 만큼만 확보되기 때문에 메모리 사용효율은 크게 개선되었다.

TEXT형이나 BLOB형은 내부적으로 테이블을 만드는 특징이 있다. 테이블관리를 위해 메모리영역을 상당히 사용하기 때문에 메모리 사용효과를 크게 악화시킨다.

그 때문에 피하는 것이 좋다. 정수/수치형, 날짜/시각형, VARCHAR형의 범위내에서 사용하는 것이 좋지 않나 싶다.



MySQL Cluster도입시의 포인트1

ndb_size.pl를 이용한 메모리 사이즈 견적

실제로 MySQL Cluster환경을 셋업할 때에는 메모리사이즈를 어느정도하면 좋을까하는 의문이 반드시 생기게 될 것이다.

메모리는 고가이므로 디스크처럼 충분한 여유를 가지고 미리 준비해 둘 수도 없다.

메모리사이즈 견적에는 테이블수, 인덱스수, 컬럼의 데이터형, 레코드수를 기준으로 산출하게 된다.

계산식도 있기때문에 수작업으로 산출하는 것도 가능하지만 복잡하므로 매우 피곤할 것이다.

이를 위해 MySQL Cluster에서는 사이즈 견적 툴로서 ndb_size.pl이라는 Perl스크립트를 제공하고 있다.

이것을 사용하면 간단히 사이즈를 견적내는 것이 가능하다.

ndb_size.pl 의 실행에는 DBI와 HTML::Template모듈이 필요하다.

또, MySQL인스톨 디렉토리밑/share/mysql/ndb_size.tmpl이라는 파일을 현재 디렉토리에 복사해둘 필요가 있다.

ndb_size.pl은 다음과 같이 실행한다.

ndb_size.pl의 실행
shell>perl ndb_size.pl db_name hostname username password > file_name.html

ndb_size.pl에서는 MySQL 서버에 접속해서 지정된 데이터베이스안의 테이블과 레코드수를 보고 NDB 스토리지 엔진으로 이행했을 때 필요한 메모리 사이즈를 계산해준다.

Data Node(ndbd)가 기동되어 있을 필요는 없다. NDB스토리지엔진으로 만들고 싶은 테이블을 MyISAM등으로 작성해놓고 ndb_size.pl을 실행해서 NDB이행시의 메모리양을 견적내보는 것이 전형적인 방법이다.

예를 들어 다음과 같이 MyISAM 테이블이 있는 상태에서 ndb_size.pl명령을 실행해보자.

ndb_size.pl의 실행예
mysql> create table tbl1(col1 integer primary key, col2 varchar(30) ) engine=myisam;
mysql> insert into tbl1 values(1, 'abc'),(2,'def');
mysql> select * from tbl1;

shell> cp share/mysql/ndb_size.tmpl ./
shell>./bin/ndb_size.pl db1 192.168.0.3:3306 user1 pass1 > size1.html

size1.html이라는 파일이 생성된다.
이것은 테이블 tbl1에 대해서 데이터형과 인덱스(INTEGER형의 primary key와 varchar(30)의 컬럼)을 계산에 넣어서 데이터부분(DataMemory), 인덱스부분(IndexMemory)의 값을 견적해준다.

현재는 레코드 수가 2개밖에 없으므로 Total DataMemory와 Total IndexMemory값은 작게 된다.
중요한 것은 Total DataMemory/Row 하고 IndexMemory/Row이다.
각각 한개의 레코드에 대한 DataMemory양과 IndexMemory양에 해당된다.

실제로 어플리케이션에서 100만레코드를 다룬다고 한다면 이 값을 100만배한 값이 대충 그 DataMemory, IndexMemory가 된다고 생각하면 된다.

물론 테이블수가 여러개 있다면 그것도 고려해야하지만 말이다.

한가지 더, 현재 ndb_size.pl에서는 DECIMAL형을 0바이트로 봐버리는 문제가 있으니 주의하길 바란다.



※NDB인덱스에는 2종류가 있다.

NDB인덱스에는 해쉬인덱스와 T-Tree인덱스(Ordered Index) 2종류가 있다.

전자는 키워드 검색에 사용되는 것으로 primary key제약과 unique key제약을 정의할 때 자동적으로 작성된다.

후자는 범위 검색 에 사용되는 것으로 primary key제약, unique key제약, 보통 인덱스를 정의할 때 자동적으로 작성된다.

다시말해 primary key제약과 unique key제약을 정의하면 해쉬 인덱스와 T-Tree인덱스 양쪽이 정의되게 된다.

T-Tree인덱스를 작성하지 않고 해쉬인덱스만을 정의하는 것은 가능한데 다음과 같이 USING HASH 문을 사용한다.

해쉬 인덱스만을 정의
mysql> CREATE TABLE tbl1(col1 INTEGER PRIMARY KEY USING HASH(col1) ENGINE=NDBCLUSTER;

해쉬인덱스와 T-Tree인데스는 구문만이 아니라 메모리 사용방법도 틀리다.

해쉬 인덱스는 IndexMemory에 들어가지만 T-Tree인덱스는 DataMemory에 들어간다.

T-Tree인덱스1개에 대해서 1레코드당 10바이트 정도이다.

해쉬인덱스는 인덱스1개에 대해서 25바이트정도 이다.

ndb_size.pl에서는 인덱스 차이를 자동적으로 고려해서 견적을 내준다.

덧붙여서, unique key제약등으로 해쉬 인덱스를 추가하는 경우는 1개의 해쉬 인덱스가 1개의 테이블에 대응하기때문에 생각외의 메모리를 사용하게 됨으로 주의가 필요하다.




2009년 6월 15일 월요일

MySQL Cluster - SQL문의 실행

db1이라는 데이터베이스를 준비해서 그곳에 테이블을 작성하는 조작을 해보자.

우선 SQL Node의 어딘가에서 데이터베이스 작성을 수행한다.

5.0까지는 작성한 데이터베이스 정보가 다른 SQL Node에 전달되지 않기 때문에 모든 SQL Node에서 각각 CREATE DATABASE명령어를 실행할 필요가 있었다.

5.1부터는 다른 SQL Node가 기동되어 있으면 그 SQL Node에서 데이터베이스가 자동적으로 작성되기 때문에 수고를 덜 수 있게 되었다.

데이터베이스 db1의 작성
mysql>CREATE DATABAE db1;
mysql>USE db1;

다음으로 어딘가 한 군데의 SQL Node에서 테이블을 작성한다. 다른 SQL Node에는 자동적으로 그 메세지가 전달되어 테이블이 만들어진다. 이것은 5.1이후 버전만이 아니라 5.0이전에서도 같다.

테이블의 작성
mysql> CREATE TABLE tbl1(col1 INT PRIMARY KEY, col2 VARCHAR(10)) ENGINE=NDBCLUSTER;

테이블을 작성했으면 그 다음은 보통 SELECT/INSERT/UPDATE/DELETE문등을 실행한다.
실행결과는 다른 SQL Node에서도 곧바로 볼 수 있다.

이것은 어느 SQL Node도 Data Node에 접근함으로 레코드를 조작/취득하고 있고 Data Node는 동기 replication에 의해서 언제나 일관된 값을 되돌려주는 것이 보증되어있기 때문이다.

예를 들어 , Node id=4의 SQL Node에서 다음과 같이 INSERT문을 실행한다.

레코드의 삽입
mysql> INSERT INTO tbl1 values(100, 'abc');

tbl1의 내용을 Node id=5의 SQL Node에서 보면 커밋후의 값이 보인다.

tbl1 내용을 확인
mysql> SELECT * FROM tbl1;
+---------+----------+
| col1 | col2 |
+---------+----------+
|100 | abc |
+---------+----------+

일부 클러스터형 RDBMS에서는 어느 노드에서의 커밋 결과가 다른 노드에는 곧바로 반영되지 않는 문제가 있지만 MySQL Cluster에서는 이런 문제는 발생하지 않는다.




2009년 6월 14일 일요일

MySQL Cluster 노드 기동/정지

Management Node, Data Node, SQL Node의 각 노드의 기동과 정지 방법에 대해서 알아보자.

Management Node의 기동
MySQL 인스톨디렉토리/bin의 아래 (mysqld, mysqld_safe등과 같은 디렉토리)에 ndb_mgmd라고 불리우는 프로그램이 있다.

이것이 Management Node 실체로 이것을 실행하면 Management Node를 기동할 수 있다.

인수 -f로 설정파일 config.ini를 지정할 수 있다.

Management Node의 기동
shell> ndb_mgmd -f /path/to/config.ini

Management Node로의 접속은 bin아래에 있는 ndb_mgm이라는 프로그램을 이용한다.

Management Node하고 같은 호스트에서 인수 없이 ndb_mgm명령어를 실행하면 Management Node에 접속가능하다.

여기에서 일련의 관리조작을 수행하는 것이 가능하다.

Management Node로의 접속
shell> ndb_mgm

SHOW 명령어를 실행하면 설정한 노드로의 접속 상황을 확인할 수 있다.

SHOW명령어에 의한 접속상황을 확인
ndb_mgm>SHOW
Connected to Management Server at: localhost:1186
Cluster Configuration
------------------------
[ndbd(NDB)] 2 node(s)
id=2 .....

id는 config.ini에서 설정한 Id의 값이 된다.

Data Node의 기동

Data Node의 기동의 흐름은 다음과 같다.

①Management Node에 접속해 자신의 Data Node의설정정보를 취득한다.
②읽어들인 설정정보를 기초로 Data Node를 기동한다.

따라서 Data Node기동시에는 Management Node를 특정할 수 있는 정보(호스트명, 포트번호)를 지정한다.

Data Node의 기동
shell>ndbd --ndb-connectstring="192.168.0.1" --initial <--처음 기동할 때
shell>ndbd --ndb-connectstring="192.168.0.1"

--ndb-connectstring에서는 Management Node의 호스트명, 포트 번호를 지정한다.

Management Node의 포트 번호의 디폴트값은 1186으로 config.ini에 명시적으로 설정하지 않은 경우에는 디폴트값이 쓰여지기 때문에 여기에서 지정할 필요는 없다.

또 처음 기동할 때에는 --initial을 지정한다. 그 다음의 기동시에는 --initial을 지정하지 않고 기동한다.
Data Node는 테이블 데이터라든지 REDO로그 등의 기동/복구에 필요한 데이터를 정기적으로 디스크에 써낸다.

--initial을 지정하면 이것들의 데이터 파일을 재 작성한 다음에 기동할 때 수정한다.

그렇기 때문에 어플리케이션 가동후에 정상운용으로 정지나 기동을 하는 경우에는 --initial을 지정해서는 안된다.

초기 상태에서는 기동후 조금 시간이 걸린다. 지금 어느 단계에 있는지에 대해서 알아보고 싶으면 ndb_mgm 콘솔에서 노드 ID STATUS라는 명령어를 사용하면 된다.

노드 ID STATUS명령어로 상태확인
ndb_mgm> 3 STATUS
Node 3: starting (Phase 4) (Version 5.1.14)

starting은 현재 기동중이라는 것을 나타낸다. 또 Management Node의 ndb_1_cluster.log라는 파일에는 기동상태의 변화가 상세하게 기술되어 있다.

마지막으로 기동이 완료된 시점에서 ndb_mgm콘솔에 Node 2: Started (version 5.1.14)등의 메세지가 자동적으로 출력된다.

또 SHOW명령어를 실행했을 때에 [ndbd(NDB)] 엔트리의 출력이 다음과 같이 되어있으면 기동이 완료되었다는 것을 의미한다.

SHOW명령어의 실행
ndb_mgm> SHOW
Connected to Mangement Server at: localhost:1186
Cluster Configuration
-------------------------------------------
[ndbd(NDB)] 2 node(s)
id=2 @192.168.0.2 (Version: 5.1.14, Nodegroup: 0, Master)
id=3 @192.168.0.3 (Version: 5.1.14, Nodegroup: 0)


SQL Node의 기동
SQL Node는 mysqld에 해당하는 것으로 통상의 흐름처럼 my.cnf의 설정항목을 기술하고 mysqld_safe를 이용해서 mysqld프로세스를 기동한다.

다만 my.cnf에는 MySQL Cluster특유의 설정파라미터가 있어 다음과 같이 추가할 필요가 있다.

[mysqld]에 다음의 엔트리를 추가
ndbcluster
ndb-connectstring=

ndbcluster에 의해서 MySQL Cluster를 사용하는 것을 선언하고 ndb-connectstring에 의해서 Management Node에 접속하기 위한 정보를 지정한다.

config.ini에서 사전에 할당해놓은 SQL Node의 번호가 적당하게 할당된다.

ndb_mgm콘솔에서 [mysqld(API)] 엔트리 출력이 바뀐 것을 확인할 수 있다.

SHOW 명령어로 확인
ndb_mgm> SHOW
Connected to Mangement Server at: localhost:1186
Cluster Configuration
-------------------------------------------
[ndbd(NDB)] 2 node(s)
id=2 @192.168.0.2 (Version: 5.1.14, Nodegroup: 0, Master)
id=3 @192.168.0.3 (Version: 5.1.14, Nodegroup: 0)

[ndb_mgmd(NDB)] 1 node(s)
id=1 @192.168.0.1 (Version: 5.1.14)

[mysqld(API)] 4 node(s)
id=4 @192.168.0.4 (Version: 5.1.14)
id=5 (not connected, accepting connect from any host)

남은 SQL Node를 기동하면 SHOW명령어 실행결과로 id=5부분이 id=4하고 같게 된다.

노드의 정지

노드 전체를 정지시킬려면 ndb_mgm콘솔에서 다음과 같이 SHUTDOWN명령을 실행한다.

Node의 정지
ndb_mgm> SHUTDOWN

이것에 의해서 Management Node하고 Data Node가 정지한다. SQL Node는 기동된 상태이지만 Data Node가 멈춰있기 때문에 NDB스토리지엔진을 작성하는지는 못한다.

Data Node를 개별로 정지하는 것은 ndb_mgm콘솔에서 다음처럼 실행한다.

Data Node의 정지
ndb_mgm> node_id STOP

node_id에는 Data Node 노드 번호를 지정한다. 이번 예의 경우에 nodeid=2로 기동했으므로 2 STOP이라고 실행하게 되면 Data Node를 정지시킬 수 있다.





2009년 6월 10일 수요일

MySQL Cluster의 환경구축 2

○MySQL Cluster용 설정파일 config.ini의 작성

MySQL Cluster에서는 설정파일 my.cnf이외에 MySQL Cluster용의 설정파일을 별도 작성할 필요가 있다.

설정파일의 기술 예는 다음과 같다. Management Node, Data Node, SQL/API Node 전 노드의 설정을 한다.

>MySQL Cluster설정 파일 config.ini
[NDBD DEFAULT]
NoOfReplicas=2

[NDB_MGMD]
HostName=192.168.0.1

[NDBD]
Id=2
HostName=192.168.0.2

[NDBD]
Id=3
HostName=192.168.0.3

[MYSQLD]
Id=4

[MySQLD]
Id=5

Management Node의 설정항목은 [NDB_MGMD], Data Node의 설정항목은 [NDBD], SQL Node의 설정항목은 [MYSQLD]에 기술한다.

Data Node나 SQL Node는 거의 예외없이 2대이상 셋업하기 때문에 설정항목에 따라서 중복하는 부분도 나오게 된다.

그런 항목은 [NDBD DEFAULT]처럼 [노드타입 DEFAULT]라는 엔트리를 준비하고 그곳에 기술하는 것으로
해당하는 모든 노드의 설정항목이 맞추어지게 된다.

MaxNoOfReplicas에서는 replication수를 나타내고 1~4범위에서 설정가능하다.
NoOfReplicas가 1이면 replication은 수행되지 않게 된다.

2이면 2대, 3이면 3대의 Data Node로 동일한 레코드가 복제된다.

갯수가 많을 수록 가용성은 높아지지만 replication 오버헤드는 증가한다.
2또는 3으로 설정하는 것이 일반적이다.

NoOfReplicas의 값은 어느 Data Node에서도 같은 설정을 필요로 하기 때문에 [NDBB DEFAULT]란에 기술하고 있다.

이 란에 기술하는 것으로 각 [NDBD]엔트리에 기술하지 않아도 디폴트값으로 반영된다.

Data Node의 파라미터 종류는 이외에도 많이 있다.

데이터 영역의 메모리크기를 결정하는 DataMemory, 인덱스의 메모리크기를 결정하는 IndexMemory, REDO로그 파일의 크기를 결정하는 MaxNoOfFragmentLogFiles등 다방면에 걸쳐져 있다.

설정파일 config.ini는 Management Node의 기동시에 읽어들일 필요가 있다.

이렇게 함으로 Management Node는 전부 몇개의 노드가 있는지등의 정보를 파악할 수 있게 된다.

파일명은 뭐든지 괜찮지만 config.ini라는 이름이 관례적으로 쓰인다.

my.cnf에 따른 mysqld의 파라미터 지정은 별도 설정할 필요가 있다.

my.cnf에서는 MySQL특유의 파라미터로서 다음의 2개가 있어 이것들을 지정할 필요가 있다.

>MySQL Cluster특유 파라미터
ndbcluster
ndb_connectstring="Management Node의 호스트명과 포트 번호"





2009년 6월 9일 화요일

MySQL Cluster의 환경구축

MySQL Cluster의 특징에 대해서 개념 레벨정도 이해한 수준에서 다음은 실제로 MySQL Cluster환경을 셋업해보자.

○서버의 구성 결정
여기에서는 Management Node 1대, SQL Node 2대, Data Node 2대 구성을 해보는 걸로 하자.

실제로 어플리케이션은 노드에 걸리는 부하와 성능을 감안해서 SQL Node의 수를 Data Node 수배정보(Data Node수가 2대이면 4~8대정도)로 하는 것이 일반적이라고 한다.

이것은 어디까지나 대충짐작의 이야기 임으로 실제로 대수를 결정할 때에는 사전에 벤치마킹을 해보는 것이 좋을 것이다.

또 Data Node는 체크포인트에 의한 디스크 저장이 있음으로 디스크 저장성능이 나쁜 RAID5는 피하는 게 좋을 듯 싶다.

RAID1이던지 RAID1+0가 무난하다.

○MySQL Cluster 인스톨
MySQL 다운로드 사이트에서 MySQL본체의 소스코드, 또는 max edition 바이너리를 다운로드 한다.

max edition 바이너리의 경우 인스톨 방법은 standard edition하고 다르지 않기 때문에 생략한다.

소스코드의 경우는 configure옵션에 --with-ndbcluster를 추가지정하는 것으로 MySQL Cluster가 설치된다.

디폴트 설정만으로는 설치되지 않으므로 조심해야한다.

MySQL Cluster 라이센스를 구입한 경우에는 전용사이트에서 안정버전을 다운 받을 수 있다.

인스톨이 끝나면 bin디렉토리 밑(소스코드에서 컴파일 한 경우에는 디폴트로 libexec밑)에

Management Node용 프로그램인 ndb_mgmd, Data Node용 프로그램인 ndbd, NDB API탑재 MySQL 본체 프로세스인 mysqld을 확인할 수 있을 것이다.







2009년 6월 6일 토요일

MySQL Cluster의 특징 7

◆Data Node의 fail over의 구조를 스스로 가지고 있다.

Data Node는 가동감시를 위해 서로 heart beat를 송수신한다.

어느 한 부분의 Data Node가 다운된 경우에는 다른 Data Node가 그것을 검지해 fail over를 수행한다.

이 때문에 Data Node에 heart beat등의 클러스터링 소프트웨어를 넣어 가동감시, fail over처리를 수행할 필요성이 반드시 있는 것은 아니다.

SQL Node에는 이 fail over 구조는 없다.

MySQL Cluster 말고도 MySQL의 고가용성을 위한 구성으로서 mysqld가 가동되는 서버에 클러스터링 소프트웨어를 넣어서 fail over처리를 구성하던지 mysqld의 앞단의 load balancer를 두고 배분하는( mysqld장해시에는 load balancer기능에의해 배분대상에서 제외시킴)것이 일반적이다.


MySQL Cluster의 특징 6

◆shared nothing형

일반적인 클러스터 구성으로서 복수의 노드가 한개의 디스크를 공유하는 공유디스크형(shared everying) 과 각 노드가 각각 각자의 디스크에 저장, 서로 공유하지 않는 비공유디스크형(shared nothing)이 있다.

MySQL Cluster는 후자를 채용하고 동기 replication에 의한 가용성을 높이고 있다.

shared everyting형은 노드의 추가가 용이한 반면, 고가의 공유 디스크가 필요하거나 Lock경합에 따른 성능저하가 우려되는 등의 과제가 있다.

한편 shared nothing형인 MySQL Cluster의 장점, 단점은 shared everything형의 정반대라고 말할 수 있다.

다만, Data Node 추가에는 시스템 전체의 정지와 노드 재구축이 필요로 하는 한다는 과제가 있다.

그 때문에 관리해야만 하는 데이터양이 급격히 증가하거나 하는 경우, 그것을 전부 NDB스토리지엔진에서 관리하는 것은 문제가 있다.

MySQL은 InnoDB와 MyISAM등 그 이외의 스토리지엔진도 선택가능하고 이것을 병용하는 것도 가능함으로 오래된 데이터를 별도의 스토리지 엔진에 이행해서 NDB스토리지 엔진에서 사용하는 레코드를 일정 이하로 유지하는 운용방침도 검토하면 좋을 것이다.

이러한 유연한 대처가 가능한 것도 MySQL의 매력중에 하나라고 말할 수 있다.


MySQL Cluster의 특징 5

◆복수의 replication단위

replication수(NoOfReplicas)가 1~4라는 것은 Data Node의 수는 4대가 최대라고 생각하는 사람도

있겠지만 그건 아니다. replication을 상호 수행하는 Data Node의 구성은 복수구성 가능하다.

이 1개의 구성을 노드그룹이라고 부른다. MySQL Cluster에서는 이 노드 그룹을 복수 구성하는 것이

가능하다. 각각의 노드 그룹의 각 Data Node는 상호 replication이 이루어진다.

가용성이라는 관점에서는 노드 그룹내의 어딘가 1개의 Data Node가 생존해 있으면 서비스 정지까지는 다다르지 않는다.

한편 노드그룹내의 전 Data Node가 정지한 경우에는 서비스는 정지하게 된다.

replication수, 노드 그룹수, Data Node 수 사이에는 다음과 같은 관계가 있다.

  • Data Node수 = replication수 X 노드 그룹수
머신 대수가 정해져 있는 경우, replication수를 늘려서 가용성을 추구하던지 노드 그룹수를 늘릴 까 SQL Node를 잘 이용해서 성능을 추구할까 하는 제어를 하는 것도 가능하다.

그치만 어플리케이션 가동후에 Data Node의 수를 변경하는 것에는 매우 귀찮은 작업이 필요하기 때문에

미리 견적을 내두는 것이 필요하다.



2009년 6월 5일 금요일

MySQL Cluster의 특징 4

◆동기 replication
한개의 레코드를 한개의 Data Node로만 관리하면 그 Data Node가 다운했을 경우에 레코드에

접근할 수 없게 되기 때문에 가용성 향상이 되지 않는다.

그 때문에 MySQL Cluster에서는 한개의 레코드를 여러개의 Data Node에서 관리한다.

각 레코드당 레코드를 주관리하는 Primary Replica가 되는 Data Node가 존재하지만

replication이 되는 Secondary Replica가되는 Data Node가 존재하게 된다.

이런 구조는 동기 replication에 의해서 실현되고 있다.

각 레코드는 한개의 Primary Replica 노드하고 0개 이상의 Secondary Replica 노드에

동기 replication되는 것이 된다.

replication수는 MySQL Cluster의 설정 파라미터 NoOfReplicas로 1~4의 범위로 설정가능하다.

1이면 Secondary Replica가 존재하지 않기 때문에 replication도 작동하지 않고 가용성 향상도

되지 않는다. 4로 설정했을 경우에는 4대의 Data Node로 replication되기 때문에 가장 가용성이

높게 되지만 반대로 replication의 오버헤드가 커지게 되어 성능은 떨어지게 된다.

그 밸런스를 잘 맞추어서 2또는 3으로 하는 것이 일반적이다. 2로 하는 케이스가 많지만

한개의 노드가 여하의 이유(계획정지 또는 긴급정지)에 의해 다운된 경우에도 reaplication에 의한

여유를 유지하고자 하는 경우에는 3을 지정한다.










2009년 6월 1일 월요일

MySQL Cluster의 특징 3

◆테이블 데이터를 분산배치

MySQL Cluster에서는 복수의 데이터 노드를 기동한다. 이 때 NDB스토리지 엔진의 테이블 데이터는 각 Data Node에 분산배치된다.

MySQL Cluster에서는 Primary Replica라고 불리우는 개념이 있다.

이것은 레코드 단위로 그것을 주관리하는 Data Node가 한개할당된다라는 것이다.

행1은 Node1, 행3은 Node2처럼 된다. 실제로 할당알고리즘으로써 기본적으로 hash partitioning라는 알로리즘이 사용된다.

이것은 주키 값에 대해서 md5 hash을 수행하여 그 결과에 따라 주관리하는 Data Node를 결정하는 것이다.

이에 따라서 한개의 테이블에 대한 처리가 한개의 Data Node에 집중하지 않기 때문에 리소스를 효율있게 이용가능하다.

full table scan등도 각 노드가 각각 담당가능하기 때문에 특히 레코드 수가 많은 경우에는 한개의 노드에 전부 맡기는 것보다

빨라질 가능성이 높아진다.