MySQL Clusterの検証をする機会があったのでメモ。
通常のMySQLサーバを運用する場合、Masterサーバ×1+Slaveサーバ×nな構成とし、Master→Slaveへレプリケーションを行いつつ、更新系のクエリはMasterに投げ、参照はSlaveサーバのいずれかへ行うことで負荷分散を行っていると思います。
しかしこの構成の場合、ソーシャルゲームのような更新系のクエリが多数投げられるシステムではMasterサーバの負荷が問題になる場合があります。
そこで今回使ったMySQL Clusterを使うとマルチマスター構成を実現することが可能となります。 どちらのサーバにINSERT/UPDATEを投げてもデータの整合性が損なわれる事なくHA構成なシステムを組めるわけです。
環境
管理ノード/データノード/SQLノード×1
- CentOS 6.4
- Intel(R) Xeon CPU 2.90GHz
- メモリ2GB
データノード/SQLノード×1
- CentOS 6.4
- Intel(R) Xeon CPU 2.90GHz
- メモリ2GB
今回のシステム構成
今回以下の図のような2台構成でテストしました。 本来高可用性の要件を満たすにはホストが3台必要とのことですが(MySQL Cluster構築・運用バイブル)、サーバを用意する都合上この構成となりました。
インストール以前に
RPMパッケージが用意されているのでこれを使ってインストールすることにしました。 ダウンロードはdev.mysql.comのダウンロードページからどうぞ。
が、ここでとても悩みました。
RPMパッケージのラインナップが7.1とちょっと変わっているような気が… 前述の「MySQL Cluster構築・運用バイブル」に各ノードに必要なRPMパッケージが細かく書いてあったんですがこれは7.1系ベースで書かれているようでダウンロードページの名称と一致しませんでした。 結局1h以上考えてしまいました。
管理ノードのセットアップ
cd /usr/local/src
rpm -ivh rpm -ivh MySQL-Cluster-server-gpl-7.2.12-1.el6.x86_64.rpm
mkdir /var/lib/mysql-cluster
cd /var/lib/mysql-cluster
mkdir mgm data1 data2 sql1 sql2
vi /var/lib/mysql-cluster/config.ini
[NDBD DEFAULT]
NoOfReplicas=2
NoOfFragmentLogFiles=16
###Disk data related
DiskPageBufferMemory=4M
SharedGlobalMemory=0
IndexMemory=100M
DataMemory=1024M
Datadir=/var/lib/mysql-cluster
MaxNoOfAttributes=4000
MaxNoOfConcurrentOperations=1M
[NDB_MGMD]
NodeId=1
Hostname=192.168.1.1
[NDBD]
NodeId=2
Hostname=192.168.1.1
Datadir=/var/lib/mysql-cluster/data1
[NDBD]
NodeId=3
Hostname=192.168.1.2
Datadir=/var/lib/mysql-cluster/data2
[MYSQLD]
NodeId=50
Hostname=192.168.1.1
[MYSQLD]
NodeId=51
Hostname=192.168.1.2
データノード・SQLノードのセットアップ
今回は管理ノード兼データノード兼SQLノードが1台あるので、使用する全てのマシンで実行しました。
#依存関係のエラーが出る場合yum remove mysql-libs、yum remove mysql-server等する
cd /usr/local/src
rpm -ivh MySQL-Cluster-client-gpl-7.2.12-1.el6.x86_64.rpm
rpm -ivh MySQL-Cluster-server-gpl-7.2.12-1.el6.x86_64.rpm
rpm -ivh MySQL-Cluster-shared-compat-gpl-7.2.12-1.el6.x86_64.rpm
rpm -ivh MySQL-Cluster-devel-gpl-7.2.12-1.el6.x86_64.rpm
rpm -ivh MySQL-Cluster-shared-gpl-7.2.12-1.el6.x86_64.rpm
rpm -ivh MySQL-Cluster-server-gpl-7.2.12-1.el6.x86_64.rpm
service mysql start
/usr/bin/mysql_secure_installation
vi /etc/my.cnf
[mysqld]
ndbcluster
ndb-connectstring = 192.168.1.1
log-error=/var/log/mysql/error.log
[mysql_cluster]
ndb-connectstring = 192.168.1.1
mkdir /var/log/mysql
chown -R mysql:mysql /var/log/mysql
mkdir -p /var/lib/mysql-cluster/data1
起動
#管理ノードで実行
ndb_mgmd -f /var/lib/mysql-cluster/config.ini --initial
#データノードで実行
ndbmtd --initial(シングルスレッド版で起動する場合はndbd --initial)
#SQLノードで実行
service mysql start
/usr/bin/mysql_secure_installation
#権限テーブルがMyISAMのため、ndbclusterエンジンに変更する
mysql -uroot -p < /usr/share/mysql/ndb_dist_priv.sql
#MySQLにログイン後
mysql > use mysql;
mysql > CALL mysql_cluster_move_privileges();
mysql > use information_schema;
mysql> select table_name,engine from tables where table_schema='mysql';
+---------------------------+------------+
| table_name | engine |
+---------------------------+------------+
| user | ndbcluster |
+---------------------------+------------+
#管理ノードで実行
> ndb_mgm
> show
Connected to Management Server at: localhost:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 2 node(s)
id=2 @192.168.1.1 (mysql-5.5.30 ndb-7.2.12, Nodegroup: 0, Master)
id=3 @192.168.1.2 (mysql-5.5.30 ndb-7.2.12, Nodegroup: 0)
[ndb_mgmd(MGM)] 1 node(s)
id=1 @192.168.1.1 (mysql-5.5.30 ndb-7.2.12)
[mysqld(API)] 2 node(s)
id=50 @192.168.1.1 (mysql-5.5.30 ndb-7.2.12)
id=51 @192.168.1.2 (mysql-5.5.30 ndb-7.2.12)
エラーが出た場合の対処
データをimportしようとすると以下のようなエラーが出ました。
ERROR 1297 (HY000) at line 1207: Got temporary error 410 'REDO log files overloaded (decrease TimeBetweenLocalCheckpoints or increase NoOfFragmentLogFiles)' from NDBCLUSTER
#NoOfFragmentLogFiles=4にしていたのをNoOfFragmentLogFiles=16(デフォルト値)に戻した
ERROR 1114 (HY000) at line 1213: The table 'sample_table' is full
#→通常のMySQLでも出るが一旦以下のようにして回避
#innodb_data_file_path=ibdata1:10M:autoextend→ダメだった
#config.iniの[NDBD_DEFAULT]セクション内のDataMemory、IndexMemoryを増やす必要がある
#IndexMemory=100M
#DataMemory=1024M
#※本来はndb_size.pl(http://dev.mysql.com/doc/refman/5.1/ja/mysql-cluster-utilities-ndb-size.html)や
#sizer(https://github.com/severalnines/sizer)で適正な数値を探る方が良さそう
ERROR 1297 (HY000) at line 1821: Got temporary error 233 'Out of operation records in transaction coordinator (increase MaxNoOfConcurrentOperations)' from NDBCLUSTER
#デフォルト32Kを1Mにした
使ってみての感想
起動・再起動手順がちょっと煩雑かも…
管理ノード起動→データノード起動→SQLノード起動というような儀式を経る必要がある。 停止の時は逆に SQLノード停止→データノード停止→管理ノード停止
通常のMySQLよりパラメータの設定がシビア
普段使っている通常のMySQLサーバだとmy.cnfの設定がいい加減でも動かないということはないと思います。 ですがMySQL Clusterでは事前にデータから設定値を見積もっておかないとレプリケーションに失敗したりするリスクがあると思いました。
テーブル設計がいい加減なシステムでは導入できなそう
DB内でindexの数が多かったりtext型のカラムを多用していたりするとNoOfFragmentLogFilesを増やすかTimeBetweenLocalCheckpointsの値を減らせってすぐ言われる。
CREATE TABLEする時のENGINE指定がndbclusterとなる
一括置換できるレベルですが地味にハマりました。 InnoDBなどを使っているMySQLサーバからDDLを作成してもそのままでは使えません。
importを実行したノードではimportできるんですが、他のノードに同期されない!エラーも特に出ません。 ENGINE=InnoDBやENGINE=MyISAMとなっている部分をENGINE=ndbclusterと変換してやる必要があります。
ENGINE=ndbclusterのテーブルのみ同期して、InnoDBやMyISAMのテーブルは同期しないけど一応使えるよっていう設計思想っぽい…
関連してMySQLの権限テーブルがMyISAMのためデフォルトでは同期されず、個々のSQLノードで実行する必要があります。 上の手順に書いてあるようにストアドプロシージャを実行してndbclusterエンジンを使うようにすれば権限テーブルも同期されるようになります。
JOINが遅いらしい
分散型システムということでJOINが遅いという情報を事前に聞いていて気になっていましたが、手元でMySQL5.5と比較した限りでは感じませんでした。 MySQL Cluster7.2からさらに高速化しているとのことですが今回使った7.1でも通常のMySQLより高速な場合もありました。
最後に
Webアプリで気軽に使うっていう感じではなさそう。 少なくともインフラ専任がいないプロジェクトで使うと運用で死ぬ気がします。
MySQLの垂直分割(テーブルを物理的に別のサーバに分ける)や水平分割(Sharding:最近あんまり使わないか?)、さらにはioDriveとか使ってどうにもならない場合には検討する価値があるかも。
おしまい。