MySQL Cluster Architecture
From hirohama.wiki
- 今後は、hirohama.mysqlを更新します。
このページでは雑誌掲載の内容とバックアップ/リストアについて紹介しています。 MySQL Clusterのセットアップ手順はPDFファイル([1])の後半にあります。HTML化していません。
最新情報などはMySQL Clusterページへ。
Contents |
MySQL Cluster Architecture
MySQL Clusterのアーキテクチャ
一言で言うとMySQL Clusterは,Alzato 社が開発したNDB ClusterをMySQLサーバと統合した,非共有ディスク型でアクティブ・アクティブ型のインメモリデータベースです.共有ディスクを必要としないために高価なH/Wを必要とせず,アクティブ・アクティブ型なのでフェールオーバに必要な時間は非常に短い.またメモリベースのデータベースであるので高速にデータへアクセスできます.
MySQL Clusterの特徴
- 非共有ディスク型
- アクティブ・アクティブ型
- インメモリデータベース
MySQLにおけるMySQL Clusterの位置づけ
MySQL ABが2003年10月にAlzato社を買収して,MySQLとNDB Clusterの統合が始まりました.統合は未だ完全ではありませんが,執筆時点での最新バージョン4.1.10での状況を下記に見ていきます.
ここでMySQLを簡単に説明します.MySQLはオープンソースのDBMSで安定性と高速性を最重視して作られています.特徴的な構造として,「ストレージエンジン」という形式を採用しています. ストレージエンジン・アーキテクチャによって,
- コネクションなどのハンドリングやSQL文の解析,最適化など,アプリケーションへのインターフェイス部分(MySQLデータベース管理レベル)
- 実際にデータを管理する部分(ストレージエンジンレベル)
という二つの部分から構成されています. NDB Clusterとの統合の過程で,ストレージエンジンとしてNDBが新しく追加されました.
<図2-3>
データベース管理者は,作成するテーブル毎に適したストレージエンジンを選択します.高速性に優れ,検索用途に適した非トランザクション対応のMyISAM,トランザクションをフルサポートするInnoDB,メモリ上で動作するHeap,などの主なストレージエンジンに加えて,NDBが加わりました.
NDBストレージエンジンがNDB APIを実装しています.(コラム「NDBクラスタのアーキテクチャ」参照)
MySQL Cluster
MySQL Clusterは,3つのノードで構成されています.ノードという単語は文脈によって意味が変わりますが,MySQL Cluster周りではプロセスの意味で使用されています.以下で詳細に各ノードとその他の構成要素を見ていきます.
<図2-4>
Managementノード
システム全体の設定を管理します. DataノードとSQLノードは,起動時にMGMノードより構成情報を取得します.各ノード起動後は,Managementノードは必須ではありません. また,Managementノードよりシステムのバックアップとリストが可能です. デフォルト設定ではArbitratorの役割をManagementノードが担い,スプリットブレイン発生時に処理を続行する側を決定します.(コラム「スプリットブレインの回避」参照) またデータベース管理者はManagementクライアントよりManagementノードへアクセスして,各ノードの状態などを確認出来ます.
Dataノード(Storageノード)
実際にデータを管理するノードです.Ver. 4.1.10では,データはメモリ上でのみ管理されますが,開発中のバージョン5.1ではディスク上へのデータ格納もオプションとして選択出来ます.Dataノードの役割は他に,分散トランザクションの管理,ノードリカバリ,ディスクへのチェックポイント,オンラインバックアップと関連するタスクの処理などです. またトランザクションコーディネータ(TC)という役割も担い,ハッシュテーブルを利用してDataノードへ実際のデータを分散して保存,処理します.(詳細は後述します.)
SQLノード(Serverノード)
MySQLのストレージエンジンの一つNDBエンジンがNDB APIを介してDataノードへ接続します.データベース管理者は,ストレージエンジンとしてNDBを選択するだけでMySQLサーバのクライアントからMySQL Clusterを利用する事が出来ます.
Management クライアント
Managementノードへアクセスして各ノードの状態確認,手動によるバックアップなどを行うコマンドラインのユーティリティです.
アプリケーション
データベースを利用するアプリケーションです.JAVA,PHP,.NETなどのプログラミング言語で記述されます.
ブローカー
本来クラスタシステムでは,クライアントアプリケーションから透過的にサーバにアクセスして,(サーバ故障時などの)必要時にはクライアントに意識させずにフェールオーバして処理を続行して欲しい. ところがMySQL Clusterでは従来のクライアント部分がMySQLサーバとなっているために(コラム「NDBクラスタのアーキテクチャ」参照),構成時に注意が必要です.つまり,Dataノードで障害が発生してもフェールオーバは自動でされますが,MySQLサーバの障害時にはフェールオーバ機能は標準ではサポートしていません. これを回避するために「ブローカー」という概念が思いつきます.「ブローカー」をクライアントアプリケーションとMySQLサーバとの間に追加して,アプリケーション側からは実際のMySQLサーバの状態を意識することなく接続,処理を続けます.
サーバ側でのブローカー実装
MySQLの機能として「ブローカー」を実装する予定は,現在のところありません.そのため,必要に応じてLVS(Linux Virtual Server)やHigh-Availability.Com社のRSF-1などで仮想ホストや仮想IPアドレスを実装します.
クライアント側でのブローカー実装
Connector/Jのフェールオーバ機能を,MySQL Clusterと組み合わせて使うことが出来ます.詳細は後述します.その他のコネクターも現在対応中のようです.
データの分散管理
MySQL Clusterでは,HA向上とロードバランスを同時に満たすために複数のDataノード間でデータを分散して管理します.
<図2-5>
2つのデュアルプロセッサ・サーバ上で,それぞれDataノードを2つずつ,合計4つのDataノードを動作させる場合を例に考えます.レプリカ数(複製の数)は2です.(図2-5参照)
データは図のようにF1, F2, F3, F4のフラグメントに分割されて,各Dataノードで管理されます.ノードグループは同じデータを持つDataノードの集合です.
この構成ではサーバ1とサーバ2が全く同じデータを持っているので一つのサーバ障害時にも継続して処理が行えます.
共有ディスクを持たないのでデータの同期が必要ですが,MySQL Clusterでは二層コミットメントで同期を実現しています. 二層コミットメントのメカニズムの下(図2-6参照),Dataノード1とDataノード2はいつでも同一のデータを持っていることが保証されています.また同じフラグメントのうち,MySQL Clusterが内部でプライマリレプリカとセカンダリレプリカと区別します.
図の構成ではF1のプライマリレプリカはDataノード1に,セカンダリレプリカはDataノード2にあります.データはいつでも同じことが保証されているのに,プライマリ,セカンダリの区別があるのは下記の理由からです. 二層コミットメントではinsert, update, deleteする場合に全Dataノードをロックします.全Dataノードが変更可能な状態であることを確認した上で,一斉に更新がかかり,これが同期レプリケーションを可能にしています.
<図2-6>
データの読み取り時,すなわちselect文の発行時にロックされるのがプライマリレプリカです.(committed readの場合にはセカンダリレプリカがロックされます.)また,トリガーやバックアップ,ノードリカバリも基本的にプライマリレプリカが使われます.
TC(Transaction Coordinator,トランザクションコーディネータ)
データは,TCが管理するハッシュテーブルに基づいて各Dataノードへ分散して格納されます. SQLノードからのリクエストは,先ずTCが受け付けます.その後,ハッシュテーブルを基に適切なDataノードへ振り分けます.大量のデータを取得する場合は複数サーバから平行してデータを取得しますので,高速な処理が可能です.
MySQL Clusterでは共有ディスクが不要なのでH/WのSPoFを持ちません.しかしTCがSPoFになり得ます. 実際には各Dataノードが交代でTCの役割を果たすので標準でTCは多重化されています.どのDataノードがTCの役割を担当するかは,ラウンドロビンアルゴリズムを使ってトランザクション毎に代わります.
データのリカバリ
インメモリデータベースでデータの永続性は,どのように保証されるのかを以下に見ていきます.
ノードリカバリ
Dataノードが一つダウンした場合
<図2-7>
Dataノード1がダウンした場合,全く同じデータがDataノード2に入っていますので処理は続行されます.また,Dataノード2のF1がプライマリレプリカとして再構成されます.
Dataノード1を復帰後,データのリカバリはDataノード2のデータがそのままDataノード1へコピーされます.コピー中にDataノード2に行われた変更は,その後で同期され,データの同期が完了した段階で自動的に4ノードで再構成されます.
<図2-8>
上記の構成でサーバ1がダウンした場合も同様です.完全なコピーがサーバ2にありますので,MySQL Clusterの処理は続行されます.新しいハードウェアをセットアップ後,同様に自動でデータの同期と再構成が走ります.
システムリカバリ
MySQL Clusterはメモリデータベースなのでデータは全てメモリ上で管理されます.けれども,MySQL Clusterは一定間隔でデータとログをディスクに保存していますのでデータの永続性は保証されます. LCP(Local Check Point)という点でそれぞれのDataノードが自身で管理しているデータのイメージをディスクに保存します.同時にメモリ上のREDO情報(DBMSに対して行われた全てのコミット済変更の記録)の不要部を削除します. またGCP(Global Check Point)と呼ばれるタイミングで,メモリ上のREDO情報をディスクに保存します.GCPの間隔はデフォルトで2秒ですが必要に応じて変更可能です.またGCPはグループコミットとも呼ばれます. MySQL Cluster全体がクラッシュしても,これらの情報を元にしてデータが自動的に復元されます.
ホットバックアップ
ノードリカバリ,システムリカバリは不慮の事態に陥った時にMySQL Clusterが自動で行う機能です.通常のシステム運用時は管理者が定期的にバックアップを取ります. MySQL Clusterでは,Managementクライアントより手動によるホットバックアップ(データベースを停止しないで行うバックアップ)が可能です.
障害検出の仕組み
全Dataノードが論理的にサークル状に接続して,隣のDataノードの生存確認をします.Dataノードで障害が検出されると,動的に再構成されます.
<図2-10>
スプリットブレインの回避
例えば4つのDataノードがサークル状に接続されて通信する環境では,ネットワーク障害によって2ノードずつに分断された場合に,どちら側で処理を続行すれば良いのか分からなくなります.この現象をスプリットブレイン(分断された脳)と呼び,クラスタリングシステムで一般的に起こりえる問題です. MySQL Clusterでは,スプリットブレインを回避するメカニズムとしてArbitrator(調停者)が機能します. ManagementノードとSQLノードがArbitratorになれますが,デフォルトではManagementノードがオン,SQLノードはオフとなっています.
分断されると,それぞれのDataノードがArbitratorに通信を試みます.Arbitratorと通信出来た側が「マジョリティー」としてその後の処理を続行します.インターコネクト(ノード間通信,コラム「インターコネクト」参照)が正常に戻った段階で,「マイノリティ」側はデータをクリアして,マジョリティーからデータをコピーします.コピー中に発行された更新はデータの同期が終わった段階でマイノリティ側に反映されます.
<図2-11>
設定ファイル
MySQL Clusterを動かすために必要な設定ファイルはmy.cnfとconfig.iniです.ここでは簡単にconfig.ini,my.cnfファイルの中身を見ていきます.
config.iniファイル
[NDB_MGMD] Hostname= host0 DataDir= /usr/local/mysql/ndb/ [NDBD DEFAULT] NoOfReplicas= 2 DataDir= /usr/local/mysql/ndb/ DataMemory = 80MB IndexMemory = 18MB [NDBD] HostName= host1 [NDBD] HostName= host2 [MYSQLD] [MYSQLD] HostName= host3
各セクション([]内の項目)の設定内容は下記の通りです.
[NDB_MGMD](必須) Managementノードに関するパラメータ
Hostname(必須) Managementノードを動かすホスト名.IPアドレスでも指定可.
DataDir Managementノードが吐き出すログファイルなどを保存するディレクトリです.
[NDBD DEFAULT](必須) 全Dataノード共通で使用するパラメータを設定します.
NoOfReplicas(必須) レプリカ(複製)の数.多いほど冗長性は向上しますが,二層コミットメントの対象Dataノードが増えるためにinsert/update/deleteプロセスを吟味する必要が出てきます.MySQL ABの推奨は2で,最大値は4です.
DataDir 各種ログファイルを保存するディレクトリです.
DataMemory 実際のデータ(レコード)を格納する領域です.
IndexMemory ハッシュインデックスを格納する領域です.
[NDBD](必須) 各Dataノードの設定です.
HostName Dataノードを実行するホスト名.
[MYSQLD] (必須) SQLノードに関するパラメータ ホスト名を明示的に指定しない場合はどのホストからでもDataノードへアクセス可能となります.
my.cnfファイル
[MYSQLD] ndbcluster ndb-connectstring=localhost [MySQL_CLUSTER] ndb-connectstring=localhost
各セクション([]内の項目)の設定内容は下記の通りです.
[MYSQLD] ndbcluster(必須) NDB Clusterを利用する事を指定します.
ndb-connectstring Managementノードのホスト名(またはIPアドレス)を指定します.この情報を基にしてSQLノード(MySQLサーバ)がManagementノードへ接続し,config.iniの設定内容を取得します. デフォルトでローカルホストへアクセスします.
[MySQL_CLUSTER] ndb-connectstring Managementノードのホスト名(またはIPアドレス)を指定します.この情報を基にしてDataノードがManagementノードへ接続し,config.iniの設定内容を取得します. デフォルトでローカルホストへアクセスします.
ここでは基本的な項目のみ説明しましたが実際のシステムで使う場合にはシステム要件やH/Wなどの条件を考慮して適切な設定が必要です.特に使用するメモリの割り当て,処理するトランザクション数の制限,Arbitrator(調停者,コラム「スプリットブレインの回避」参照)の多重化,などを考慮します.
NDB Cluster
Alzato社が開発したNDB ClusterではクラスタシステムはMGMノード,DBノード,APIノードの3つのノードより構成されます. NDB Clusterのシステムを利用するためには,アプリケーション開発者がアプリケーションをC++で記述する必要があり,SQLのインターフェイスは実装していませんでした.高速に動作する反面,利用するためには敷居が高かった事も事実です.このような構成になっていたのは,当初はネットワーク機器向けのデータベースソフトとして開発されたためと思われます. NDB Clusterは図のような構成になっています.
各ノードの役割を簡単に見ていきます.
MGMノード
システム全体の設定を管理します.DBノードとAPIノードは起動時にMGMノードへアクセスしてシステムの設定情報を取得します.各ノード起動後は,MGMノードは特に必要ではありません.また,MGMノードよりシステムのバックアップとリストが可能です.
DBノード
実際のデータを管理します.
APIノード
データベースを利用するアプリケーションです.アプリケーション開発者がC++で記述する必要があります.
全体の構成
全DBノードがハートビートによって常にお互いの生存確認を行っています.また,現在のMySQL Clusterとの決定的な違いとして,クラスタシステムのクライアントであるAPIノードは常に全DBノードと接続しているのでフェールオーバに特別な仕組みは不要です.DBノード障害時には,障害ノードの切り離しと再構成が発生しますが,これらは自動で行われます. クライアントであるAPIノードからは,意識することなくフェールオーバが行われます.
バックアップ
MySQL Clusterはシェアードナッシングで各Data Nodeは更新ログを定期的にローカルに保存しており、ノードリカバリは自動で行われる。 冗長化構成を取っている場合は、バックアップは必要に応じて利用する。
MySQL ClusterではData Node側、及びSQL Node側の2種類のバックアップ/リカバリが実装されている。
オンラインバックアップはData Node側のバックアップで実装されている。
SQL Node側でのバックアップ
SQL Nodeからバックアップする方法として、次の2種類が用意されている。
mysqldump
mysqldumpを用いることによりSQL形式でデータベース全体のデータを簡単にバックアップできる。
実行例:mysqldump -uroot --no-autocommit --add-drop-table javadb > test.sql
mysqldump出力例
[a6]$ more test.sql -- MySQL dump 10.9 -- -- Host: localhost Database: javadb -- ------------------------------------------------------ -- Server version 4.1.13-max-log -- -- Table structure for table `t1` -- DROP TABLE IF EXISTS `t1`; CREATE TABLE `t1` ( `c1` int(11) NOT NULL default '0', `c2` int(11) default NULL, PRIMARY KEY (`c1`) ) ENGINE=ndbcluster DEFAULT CHARSET=latin1; -- -- Dumping data for table `t1` -- /*!40000 ALTER TABLE `t1` DISABLE KEYS */; LOCK TABLES `t1` WRITE; set autocommit=0; INSERT INTO `t1` VALUES (14982,0),(11892,0),(12566,0),(26738,0),(49193,0),( 8451,086,0),(29457,0),(56832,0),(1069,0),(43670,0),(34652,0),(41085,0),(48071,0),( 147(51744,0),(44067,0),(14483,0),(49505,0),(32334,0),(42408,0),(7225,0),(50852,0),( 0),(6907,0),(36962,0),(33168,0),(56583,0),(40793,0),(44079,0),(24702,0),(41534,0 7,0),(12893,0),(25075,0),(21671,0),(12711,0),(34141,0),(9159,0),(49398,0),(38600 506,0),(18708,0),(59721,0),(47617,0),(44980,0),(32028,0),(44632,0),(7146,0),(887 56380,0),(11936,0),(28191,0),(40179,0),(16241,0),(47912,0),(44392,0),(27170,0),( --More--(0%)
SELECT INTO OUTFILE
SELECT文のオプションを使用してもバックアップを取得することができる。以下の構文を使用する。 実行例:mysql> SELECT * INTO OUTFILE '/home/mysql/t1.csv' FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n' FROM t1;
SELECT INTO OUTFILE出力例
[a6]$ more t1.csv 14982,0 11892,0 12566,0 26738,0 49193,0 8451,0 46602,0 53127,0 34708,0 48071,0 14764,0 51729,0 59920,0 55753,0 --More--(0%)
※SQL Nodeからバックアップを取得する時の注意点
SQL Nodeからデータをバックアップする際、対象のテーブルに対して更新させないよう、MySQLでは、自動的にLOCK TABLES構文を発行している。
一方、MySQL Clusterの場合、この構文が有効にならないため、バックアップ作成時に他のトランザクションが更新できる状態にある。バックアップ時のデータの不整合を避けるためにも、SQL Nodeからバックアップを取得する際、他のトランザクションによる更新が発生しないよう、コントロールする必要がある。
(SQL Nodeを一度落としてから--skip-networkingオプションを付けてローカル以外からの接続を許可しないようにSQL Nodeを起動する、など。)
Data Node側でのバックアップ
Data Node側でのバックアップ手順を下記の通り確認した。
Management Clientにて”start backup”コマンドを発行する事で各Data Nodeにおいてメモリ上のデータをファイルに格納する。 1. BACKUP-backup_id.<NodeId>.ctl:テーブル定義 2. BACKUP-backup_id-0.<NodeId>.data:レコード 3. BACKUP-backup_id.<NodeId>.log:コミット済トランザクションのログ
Data Node側でのバックアップ
[c1]$ /usr/local/mysql/bin/ndb_mgm -- NDB Cluster -- Management Client -- ndb_mgm> start backup Waiting for completed, this may take several minutes Node 2: Backup 1 started from node 1 Node 2: Backup 1 started from node 1 completed StartGCP: 141 StopGCP: 144 #Records: 64098 #LogRecords: 0 Data: 1505688 bytes Log: 0 bytes
リカバリ
バックアップ・ファイルからのリカバリ手順を確認する。
SQL Node側でのリカバリ
SQL Nodeからデータをリカバリする場合、フルバックアップを使用してバックアップの時点までデータを戻し、差分バックアップを使用して障害直前まで復旧させる。
フルバックアップのリカバリ
上記、SQL Nodeからのバックアップ方法により、リカバリの方法も異なる。
- mysqldump使用時のリカバリ
実行例:mysql> source /usr/local/mysql/test.sql
mysqldump使用時のリカバリ実行例
[mysql:a6]$ source /usr/local/mysql/test.sql Query OK, 0 rows affected (0.22 sec) Query OK, 0 rows affected (0.31 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Query OK, 60000 rows affected (1.70 sec) Records: 60000 Duplicates: 0 Warnings: 0 Query OK, 0 rows affected (0.02 sec) Query OK, 0 rows affected, 1 warning (0.00 sec) Query OK, 0 rows affected (0.00 sec) [mysql:a6]$ SHOW WARNINGS; +-------+------+--------------------------------------------------------+ | Level | Code | Message | +-------+------+--------------------------------------------------------+ | Note | 1031 | Table storage engine for 't1' doesn't have this option | +-------+------+--------------------------------------------------------+ 1 row in set (0.00 sec)
- SELECT INTO OUTFILE使用時のリカバリ
mysqlクライアント利用してリカバリを行う。 前提:実行前にテーブルを作成しておく必要がある。 実行例:mysql> LOAD DATA INFILE '/home/t1.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY `\r\n';
SELECT INTO OUTFILE使用時のリカバリ実行例
[mysql:a6]$ LOAD DATA INFILE '/home/mysql/t1.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'; Query OK, 60000 rows affected (1.43 sec) Records: 60000 Deleted: 0 Skipped: 0 Warnings: 0
差分バックアップのリカバリ
差分バックアップのリカバリは、バイナリログを使用して行う。ただし、ndbcluster利用時にSQL Nodeが複数存在する場合は、バイナリログも複数種類存在するため、この手段によるデータ復元はあまり行われない。バイナリログによる復旧方法は次の通り。
バイナリログからのリカバリ実行例
[root@a6 mysql]# ./bin/mysqlbinlog /home/mysql/data/a6-bin.00000 * |/usr/local/mysql/bin/mysql -uroot
Data Node側でのリカバリ
“ndb_restore”コマンドでリカバリは実装されている。 NDB APIを利用して接続するので空きNode IDがあることを確認する。 リカバリプログラムを実行する際にはデータが空である必要がある。 テーブル定義をリカバリ後、データをリカバリする。
リカバリ手順(a1)
[a1]$ /usr/local/mysql/bin/ndb_restore -b 1 -n 2 -m -c c1 Ndb version in backup files: Version 4.1.13 Connected to ndb!! Successfully restored table javadb/def/t1 Successfully created index PRIMARY on t1 [a1]$ /usr/local/mysql/bin/ndb_restore -b 1 -n 2 -r -c c1 Ndb version in backup files: Version 4.1.13 Connected to ndb!! _____________________________________________________ Restoring data in table: javadb/def/t1(2) fragment 0 _____________________________________________________ Restoring data in table: sys/def/NDB$EVENTS_0(1) fragment 0 _____________________________________________________ Restoring data in table: sys/def/SYSTAB_0(0) fragment 0 Restored 30061 tuples and 0 log entries
