2014년 1월 22일 수요일

TokuDB Introduction

TokuDB는 높은 확장성과 zero-maintenance downtime 을 지향하는 MySQL Storage engine으로, 인덱스 기반의 쿼리 속도 향상, 복제 성능 향상, 병렬화되지 않은 압축 및 온라인 스키마 변경이 가능하다.

TokuDB는 drop-in stroage engine의 MySQL로 개발한 애플리케이션을 그대로 사용할 수 있으며,  ACID와 MVCC를 완벽하게 지원한다.
TokuDB는 2013년 초 Open source로 개방하였고, 2014년 1월 현재 TokuDB 7.1.0 Community for MySQL 5.5.30, TokuDB 7.1.0 for Community Edition MariaDB 5.5.30 버전을 제공하고 있다.
한편 Tokutek에서 MongoDB with Fractal Tree Indexes v0.0.2도 발표하여 Fractal Tree Index를 활용할 수 있는 영역을 넓히고 있어 주목된다.
그러나, 아직은 QPS가 높은 OLTP상에서는 여러 안정성에서 문제가 발생하고 있기 때문에, 데이터 size가 크고 QPS가 높지  않은 성격의 DB (Log DB, 분석 DB 등등)라면 권장 해 볼만 하며, 실제로 카카오에서는 이러면 성격의 DB에 도입하여 활용 중에 있다.

Toku DB 사용의 appeal point는 다음과 같다.
장점 : 
  • index scan의 경우 innodb 대비 read속도가 상당히 빠르다.
  • insert speed InnoDB 대비 10x (read가 많지 않은 경우)
  • 압축 size InnoDB 대비 : 1/10~1/20

단점 : 
  • full scan의 경우는 압축을 풀어야 하므로, InnoDB대비 3x이상 느리다.
  • 압축을 사용할 경우 innoDB보다 user CPU사용율이 약간 높다.
  • XtraBackup 등의 innoDB open source backup tool을 사용할 수 없다.

Fast insertions and deletions

  • TokuDB에서는 Fractal Tree기술로 빠른 인덱싱이 가능하다. B-tree의 경우 느린 인덱싱으로 인해 암묵적으로 인덱스 개수를 제한하고 있다는 점에서 주목할만하다. 인덱싱이 빠르다는 것은 인덱스를 많이 만들 수 있다는 것을 의미하고 그만큼 쿼리에서 활용할 수 있는 인덱스의 수가 늘어난다는 것을 의미한다. Fractal Tree는 B-tree의 인덱스 최적 포인트인(indexing sweet spot)인 sequential data에 대비해서, 높은 카디널리티를 가진 랜덤 데이터에서도 2배 이상 빠르다.
  • fast indexing means fast queries. -Tokuteck-

Hot index creation

  • TokuDB에서는 온라인으로 인덱스를 생성할 수 있다. MySQL 5.5 이하 및 MariaDB 5.5 이하에서 지원하지 않음
  • tokudb_create_index_online 세션 변수가 ON (Default) 일 경우 온라인으로 인덱스 생성을 할 수 있다.
  • 온라인 인덱스 생성은 CREATE INDEX 구문으로만 가능하다. ALTER TABLE 구문으로 인덱스를 생성하면 tokudb create index online 세션 변수가 on 으로 설정되어 있다 하더라도 인덱스는 오프라인(해당 테이블은 오퍼레이션이 불가, Locking)으로 생성된다.
  • 인덱스 생성 진행 상황은 다른 클라이언트에서 SHOW PROCESSLIST 명령어에서 확인할 수 있다. (CTAS, LOAD, INSERT...SELECT 등 포함)
show processlist;
+...+---------+------+------------------------------------+----------------------------------------------------+----------+
|...| Command | Time | State                              | Info                                               | Progress |
+...+---------+------+------------------------------------+----------------------------------------------------+----------+
|...| Query   | 1312 | Adding of indexes about 79.5% done | CREATE INDEX ix1_toku_default ON toku_default (c2) | 79.000   |
|...| Sleep   | 30   |                                    | NULL                                               | 0.000    |
|...| Query   | 0    | NULL                               | show processlist                                   | 0.000    |
+...+---------+------+------------------------------------+----------------------------------------------------+----------+

Hot column addition, expansion and rename(HCADER)

  • TokuDB에서는 일부 데이터 타입에 대해 Locking 없이 온라인으로 컬럼 추가, 확인 및 이름 변경을 할 수 있다. MySQL 5.5 이하 및 MariaDB 5.5 이하에서 지원하지 않음
  • 지원 가능한 데이터 타입은 CHAR, VARCHAR, VARBINARY, INTEGER 이다. TIME, ENUM, BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB 타입은 지원하지 않는다.
  • HCADER를 실행하면 meta 정보를 갱신하기 위해 아주 짧은 시간 동안 테이블 락을 잡고, 락이 풀린 후에는 row가 디스크에서 메모리로 올라올 때 각 row를 수정한다 - 이를 후행 작업이라고 정의하자 -. HCADER은 PK 및 secondary index를 포함한 Fractal Tree의 일부를 건드리는 후행 작업으로 수행된다. 추가된 컬럼, 삭제된 컬럼 및 확장된 컬럼의 각각의 후행 작업을 OPTIMIZE TABLE 구문으로 수동으로 한 번에 실행할 수 있다. OPTIMIZE TABLE 구문은 온라인으로 실행이 가능하다. 즉, OPTIMIZE TABLE 명령어가 실행되는 동안 아무런 블럭킹 없이 쿼리를 실행할 수 있다. 이는 TokuDB v7.0.1 에서 중요한 부분으로 소개되고 있다. 또한 온라인으로 OPTIMIZE TABLE을 수행하더라도 TokuDB 인덱스가 age되지 않은 한 인덱스를 리빌드하지 않는다. 즉, HCADER의 모든 플러시 작업은 백그라운드로 실행된다.
  • 온라인 컬럼 추가, 컬럼 삭제, 컬럼 확장 작업은 각각의 SQL 문장으로 실행해야 한다.
  • 인덱스를 추가 또는 삭제하는 동안에는 HCADER을 하지 않도록 한다.
  • HCADER을 수행하는 동안에 발생하는 테이블 락 시간은 dirty page flush time에 의해 결정된다. 만일 체크포인트가 최근에 발생했다면 이 작업은 길어야 수 초 이내에 끝나고 테이블에 dirty page가 많으면 flush로 인해 수 분이 걸릴 수도 있다.
  • 인덱스의 일부로 사용되고 있는 컬럼의 삭제는 피하는 것이 좋다. 인덱스 컬럼을 삭제 작업은 느리다.  인덱스 컬럼을 삭제하고자 한다면 먼저 해당 인덱스를 삭제한 후 컬럼을 삭제하도록 한다.
  • PK나 Secondary Key의 일부로 사용되는 컬럼에 대해서는 HCADER을 지원하지 않는다.
  • 문법은 아래와 같다.
ALTER TABLE <table-name> CHANGE <old-column-name> <new-column-name> <data-type> <required-ness> <default>
  • 임시 테이블의 경우 HCADER을 이용하는 것보다는 일반적인 방법으로 작업하는 것이 더 빠르다.

Compression

  • TokuDB는 3가지 레벨(tokudb_default, tokudb_fast, tokudb_small)의 압축을 제공한다.
  • 당연한 이야기이지만 압축과 CPU 사용율은 상반 관계를 가진다. 즉, 기본 레벨 (tokudb_default)을 사용하면 CPU 사용율은 낮아지지만, tokudb_small 압축 레벨을 사용하면 CPU 사용율은 높아진다.
  • TokuDB에서의 압축은 백그라운드 스레드로 동작하기 때문에 고레벨 압축을 사용하더라도 데이터베이스는 성능은 나빠지지 않는다. TokuTek왈, 오히려 압축율이 높은 데이터베이스가 좋을 성능을 보이는 것을  보았다.
  • 6 Core 이하에서는 tokudb_default, 그 이상에서는 tokudb_small를 권장한다.
  • 압축은 테이블 단위(row_format option)로 지정한다. 압축 옵션을 지정하지 않으면 기본 값으로 지정된다.
  • 압축 레벨은 crate table 또는 alter table 문장으로 변경할 수 있다. 바뀐 압축 옵션은 새롭게 유입된 데이터에 대해서만 유효하다. OPTIMIZE TABLE 구문을 실행하면 테이블의 모든 데이터 및 인덱스를 재작성할 수 있다.
CREATE TABLE <table_name> (c1 INT NOT NULL PRIMARY KEY, c2 INT NOT NULL) ENGINE=TOKUDB ROW_FORMAT=<row_format>;
ALTER TABLE <table_name> row_format=<row-format>;
  • 또한 세션 변수로 tokudb_row_format을 사용해도 된다.
SET tokudb_row_format = <row_format>;
  • row_format
    • tokudb_default : 기본값으로 TokuDB 7.0.1에서는 (=tokudb_fast)로 동작한다. 앞으로 이 값은 바뀔 예정이다.
    • tokudb_fast : quicklz 라이브러리를 사용하여 기본 압축을 한다.
    • tokudb_small : lzma 라이브러리를 사용하여 고압축을 한다.
  • 참고

Elimiates slave lag

  • MySQL의 복제는 단일 스레드로 디자인되어 있기 때문에 slave log가 종종 발생한다. 하지만 TokuDB에서는 high insertion rates로 인해 slave lag가 거의 발생하지 않는다. 
  • 참고

No Aging/Fragmentation

  • TokuDB Fractal Tree Index에서는 단편화가 절대 발생하지 않기 때문에 Reorg가 필요하지 않다.

Clustering keys and other indexing improvements

  • TokuDB 테이블들은 PK로 clustered된다.
  • secondary key도 clustering key로 생성할 수 있다. 하지만 clustering key의 leaf level에는 모든 컬럼이 포함되므로 문자열 데이터의 경우 데이터 사이즈의 최소 2배 이상의 공간이 필요하다 (단, 압축율에 따라 달라짐) . 
  • 따라서 clustering key를 이용하여 범위 검색을 할 경우 clustering factor가 높은 검색을 할 수 있어 쿼리 성능을 상당히 향상시킬 수 있다.
  • auto-increment 컬럼을 모든 인덱스의 모든 포지션에서도 사용할 수 있다.
  • 최대 32 컬럼까지 지원한다.

Bulk loader

  • 오프라인 테이블 및 인덱스 생성 작업은 parallel loader로 작업하기 때문에 multiple core에서 빠르게 작업할 수 있다.

Fast recovery

  • TokuDB 에서의 recovery는 100% 자동으로 실행된다. 
  • 빠른 복구를 지원한다. (일반적으로 1분이내)

참고

Linux Hot Copy(hcp) – Snapshot Tool


Overview

Linux Hot Copy(HCP) 유틸리티가 무료로 풀리면서, 고가의 스냅샷 유틸리티를 구입 없이도 얼마든지 사용할 수 있게 되었습니다. 스냅샷을 멋지게 활용할 수 있다면, 단순히 데이터 백업 뿐만 아니라 DB 시스템과 같은 매번 디스크에 데이터 변화가 많은 시스템에서도 특정 시점의 스냅샷 데이터를 추출할 수 있습니다.

Feature

HCP는 다음과 같은 특성을 가집니다.
  1. Point-in-Time 디스크 볼륨 스냅샷
  2. Copy on Write 방식의 Snapshot
  3. 서비스 영향없이 스냅샷을 생성
"Copy on Write"을 통한 스냅샷과 다른 방식의 차이를 비교해보도록 하겠습니다.

<Snapshot 비교>

  1. Copy-on-WriteWrite 시 원본 데이터 Block을 Snapshot 스토리지로 복제하는 방식으로 Snapshot 데이터 Read 시 변경되지 않은 데이터는 원본 블록에서,변경된 데이터는 Snapshot 블록에서 처리데이터 변경 분만 저장하므로 공간을 효율적으로 활용하나 블록 변경 시 원본 데이터와 스냅샷 데이터 양쪽 모두에서 Write이 발생
  2. Redirect-on-WriteCopy-on-Write와 유사하나, 원본 볼륨에 대한 Write을 별도의 공간에 저장하는 방식으로 Copy-on Write(원본 Read, 원본 Write, 스냅샷 Write)에 비해 Write이 1회만 발생하나, 스냅샷 해제 시 변경된 블록들을 원본 데이터 블록으로 Merge시켜야함
  3. Split mirror원본 볼륨과 동일한 사이즈의 별도 복제 볼륨 생성하는 방식으로 데이터를 Full Copy하므로 즉시 생성이 어렵고 용량 또한 많이 필요

Installation

설치 버전을 다운로드 하기 위해서는 하단 페이지에서 등록해야하는데, 등록하게 되면 설치 바이너리를 다운로드 받을 수 있는 별도의 링크를 메일로 보내줍니다. 무료로 배포가 되었을 뿐 오픈 소스가 아닌지라, 설치하기 위한 절차가 까다롭습니다. 

http://www.idera.com/productssolutions/freetools/sblinuxhotcopy

설치 파일을 받고  압축을 풀면 아래와 같이 OS 별로 설치 바이너리가 있습니다.

$ unzip Idera-hotcopy.zip
$ cd Idera-hotcopy
$ ls
Idera-hotcopy.zip Installing+Hot+Copy.html idera-hotcopy-5.2.2.i386.rpm idera-hotcopy-5.2.2.x86_64.rpm idera-hotcopy-amd64-5.2.2.deb idera-hotcopy-i386-5.2.2.deb idera-hotcopy-i386-5.2.2.tar.gz idera-hotcopy-x86_64-5.2.2.tar.gz

서버에 사용하고자 하는 설치 바이너리를 설치 한 후 리눅스 커널에 맞는 모듈을 업그레이드해야 합니다. hcp-setup 명령어로 쉽게 가능하며, 업그레이드 시 https접근(443포트)이 필요합니다.

$ rpm -i idera-hotcopy-5.2.2.x86_64.rpm
$ hcp-setup --get-module

Usage

사용법은 다음과 같이 “hcp –help” 명령어를 통해 확인해볼 수 있습니다.

$ hcp --help
Usage: hcp -h | -m <MOUNT POINT> <DEVICE> | -l | -r <DEVICE>
Options:
  -h, --help             Show this help message.
  -l, --list             List active Hot Copy sessions.
  -r, --remove           Remove Hot Copy session.
  -m, --mount-point      Specify mount point.
  -o, --read-only        Mount hcp fs read only.
  -c, --changed-blocks   Specify changed blocks storage device.
  -q, --quota            Sets quota for changed blocks storage.
  -s, --show-hcp-device  Show the Hot Copy device path for a given 
                         device.
  -v, --version          Show the Hot Copy driver version.
Examples:
    Start session:
        hcp /dev/sdb1
        hcp -m /mnt/tmp /dev/sdb1
    Remove session:
        hcp /dev/hcp1
    List sessions:
        hcp -l

변경된 블록들이 저장한 디바이스가 별도로 존재한다면 -c 옵션으로 스토리지를 분리할 수 있습니다. 그렇지 않으면, 스냅샷을 생성한 디스크에 기본적으로 변경 블록이 기록됩니다.

Example

1) 스냅샷 생성 – 변경 블록이 저장될 스토리지(/dev/sdb1) 

$ hcp -c /dev/sdb1 /dev/sdc1
Idera Hot Copy     5.2.2 build 19218 (http://www.r1soft.com)
Documentation      http://wiki.r1soft.com
Forums             http://forum.r1soft.com
Thank you for using Hot Copy!
Idera makes the only Continuous Data Protection software for Linux.
Starting Hot Copy: /dev/sdc1.
Changed blocks stored: /backup/.r1soft_hcp_sdc1
Snapshot completed: 0.000 seconds
File system frozen: 0.019 seconds
Hot Copy created: Tue Jul 18:31:02 KST 2013
Creating hotcopy snaphost device: /dev/hcp1, Please wait...
Hot Copy created at: /dev/hcp1
making new path: /var/idera_hotcopy/sdc1_hcp1
Mounting /dev/hcp1 read-write
Hot Copy mounted at: /var/idera_hotcopy/sdc1_hcp1

2) 스냅샷 현황

$ hcp -l
Idera Hot Copy     5.2.2 build 19218 (http://www.r1soft.com)
Documentation      http://wiki.r1soft.com
Forums             http://forum.r1soft.com
Thank you for using Hot Copy!
Idera makes the only Continuous Data Protection software for Linux.
****** hcp1 ******
 Real Device:           /dev/sdc1
 Virtual Device:        /dev/hcp1
 Changed Blocks Stored: /backup/.r1soft_hcp_sdc1.cow_hcp1
 Mounted:               /var/idera_hotcopy/sdc1_hcp1
 Time Created:          Tue Jul 18:31:02 KST 2013
 Changed Blocks:        0.25 MiB (262144 bytes)

백업 및 복구 방안

InnoDB 백업 시에는 Xtrabackup을 사용하면 시점 백업이 가능하나, 문제는 InnoDB가 아니거나, 스토리지 엔진이 섞여 있는 경우(예를들면 Mroonga, MyISAM, TokuDB) 특정 시점의 백업을 만들어 내기 난해합니다. 이 경우 HCP를 통해 백업을 활용한다면 간단하게 해결할 수 있습니다.

위와 같이, Global Read Lock을 걸고 스냅샷을 걸고 바로 Global Read Lock을 해제하면, 그 시점의 테이블 데이터에 대한 스냅샷 데이터를 추출할 수 있습니다. 물론 스냅샷을 거는 순간 바이너리 로그 포지션을 기록해놔야 하겠지요.

간단하게 Perl로 예제를 적어보겠습니다.

먼저 백업할 DB 커넥션을 맺고 Global Read Lock을 획득합니다.
$dbh = DBI->connect('DBI:mysql:mysql;host=127.0.0.1', 'root', 'pass')
           or die $DBI::errstr;

# Flush and Lock tables
$dbh->do("flush tables with read lock");

이 순간부터는 더 이상 데이터 변경은 없습니다. 즉, 이 상태에서 바이너리 포지션을 기록하면, 그 이후부터의 변경 사항은 바이너리 로그를 활용하여 복구할 수 있습니다.

$sth = $dbh->prepare("show master status");
$sth->execute();
$sth->bind_columns( \( @row{ @{$sth->{NAME_lc} } } ));
while ( @row = $sth->fetchrow_array ) {
   $status_info .= "[MASTER STATUS]\n";
   $status_info .= @row{file}."\t".@row{position}."\n";
}

백업은 슬레이브에서 하는 경우가 대부분이므로, 슬레이브 상태를 필히 기록해야겠죠.

$sth = $dbh->prepare("show slave status");
$sth->execute();
$sth->bind_columns( \( @row{ @{$sth->{NAME_lc} } } ));
while ( @row = $sth->fetchrow_array ) {
 $status_info .= "[SLAVE STATUS]\n";
 $status_info .= "CHANGE MASTER TO MASTER_HOST='@row{master_host}', MASTER_LOG_FILE='@row{relay_master_log_file}', MASTER_LOG_POS=@row{exec_master_log_pos};\n";
}

아직 로그로 Flush되지 않은 Commit된 트랜잭션을 유실하지 않기 위해 1초 대기합니다. InnoDB를 예를 들자면, innodb_flush_log_at_trx_commit이 0으로 설정된 경우를 들 수 있겠네요.

sleep(1);

이제 스냅샷을 만들고, 앞서 획득했던 Global Read Lock을 해제합니다. 여기서 명시적으로 마운트 포인트를 지정한 이유는 추후 hcp 언마운트를 위해서입니다.

# create snapshot
system "hcp -m /var/hcp/snapshot1 /dev/sdc1;
system "hcp -m /var/hcp/snapshot2 /dev/sdc1;

# unlock tables and disconnect 
$dbh->do("unlock tables");
$dbh->disconnect();

이제, 원하는 데이터를 다른 스토리지로 옮기면 됩니다. 로컬 디스크라면 cp로, 원격으로 옮긴다면 scp 또는 rsync를 생각해볼 수 있겠네요.

cp -R /var/hcp/snapshot1/mysql-data /backup/20140122/
cp -R /var/hcp/snapshot2/mysql-iblog /backup/20140122/

백업이 모두 마무리되었다면, 이제 스냅샷을 해제합니다.

system "hcp -r /var/hcp/snapshot1";
system "hcp -r /var/hcp/snapshot2";

만약 /var/hcp 밑에 마운트된 스냅샷을 한번에 지운다면, 아래로 한방에 처리해도 됩니다.

system "df | grep '/var/hcp/' | awk '{print \$1}' | xargs -i hcp -r {}";

간단하지만, HCP를 활용하여 백업할 수 있는 방안에 대해 설명하였습니다. ^^

Conclusion

Linux Hot Copy(hcp)는 설치 및 사용이 편리할 뿐만 아니라 무료로 사용할 수 있기에, 조금만 활용하면 멋진 백업 솔루션도 만들어낼 수 있습니다. 물론, 디스크에 두번 적는 추가 I/O가 발생하나, 스냅샷으로 백업을 할 수밖에 없는 스토리지 엔진 경우 매우 유용합니다.