MySQL Clusterでディスクベーステーブル

前回(MySQL Clusterの導入)に引き続き、今回はMySQL Clusterでディスクベースでのテーブルを作成してみる(長らく記事をアップできずすいませんorz)。MySQL Clusterは元々オンメモリにテーブルとインデックスデータを格納するが、この場合は物理メモリサイズ以上のテーブルを作成することができない。この課題を解決するため、ここではディスクベースのテーブルを作成していく。

まずは、MySQL Clusterにおけるディスクへのデータの格納方法から説明。

MySQL Clusterにおけるデータ保持の仕組み

MySQL Clusterでは、基本的にデータとインデックスはオンメモリで保持される。今回紹介するディスクベーステーブルを使うと、データについてはメモリではなくディスク上に保持することが可能。これにより物理メモリの容量を超えるテーブルを扱うことができるようになる。ただし、ディスクベーステーブルを使用する場合でも、インデックスについてはオンメモリで保持される。

ここで1つ、疑問に思う人もいると思うのでオンメモリでデータを格納する場合になぜデータが消失しないのか、その仕組みについて説明。オンメモリテーブルが文字通りメモリ上だけでデータを保存していると、クラスタの再起動時に大事なデータは全て消失してしまう。MySQL Clusterのメモリテーブルでは、バックグラウンドで実行される2種類チェックポイントにより、この問題を解決している。

MySQL Clusterでは、GCP(Global Check Point)とLCP(Local Check Point)と呼ばれる2つのチェックポイントが存在する。基本的な概念としては、障害に備えメモリ内のデータREDO情報を定期的にディスクにFlushするという一般的な考え方と同じ。

  • GCP(Glabal Check Point)
    • flush対象はRedo情報
      • 「ノードグループの全ノード分のリカバリ可能なRedoログ」を定期的にディスクにFlushする
      • 自ノードが関与しない更新処理についてのRedoログも含まれる
      • デフォルトではディスク上のファイルが4MBになるか、またはメモリ上のバッファが一杯になったとき、2秒ごとにflushされる
    • LCPが走った後ではこの情報は不要になるため、自動で削除されまた新たにディスクへのflushが始まる
  • LCP(Local Check Point)
    • flush対象データは、自ノードにおけるデータ、インデックス
      • flushが終わるとGCPによってディスク上にflushされたRedo情報が不要になる

さて、ここでデータが実際にディスク上にフラッシュされた時にどこに落ちるのか確認。管理ノードの設定ファイル(/var/libmysql-cluster/config.ini)で設定した「datadir」パラメータの設定値にあたるディレクトリが、データノードにてメモリ上のデータがflushされる場所になる。以下の例では、「ndb_3_fs/」ディレクトリがデータがフラッシュされる場所である。ただし、MyISAMのテーブル構成ファイルのようにわかりやすいファイル構造ではない。


# pwd
/var/lib/mysql-cluster
# ll
合計 32
-rw-r--r-- 1 root root 5 11月 24 14:32 ndb_3.pid
drwxr-x--- 9 root root 4096 11月 22 20:34 ndb_3_fs
-rw-r--r-- 1 root root 18915 11月 24 14:33 ndb_3_out.log
-rw-r--r-- 1 root root 568 11月 24 14:33 ndb_pid4309_error.log

クラスタを終了する際には、メモリ上のデータがファイルシステム上のこのディレクトリ配下に保存され、クラスタを再起動するとディスク上のこれらのデータがメモリ上にロードされる。既存のMySQLの機能で言うと、HEAP(MEMORY)エンジンのデータをinit_connectやinit-fileなどのオプションを使って読み込むという処理をクラスタが自動で行ってくれる、というもの。

ディスクベーステーブルの構造

ディスクベールテーブルの場合、オンメモリの場合にとは異なり、テーブルスペースやログファイルグループという概念が登場する。OracleのテーブルスペースとREDOロググループをイメージすると理解しやすい。テーブルスペース、ログファイルグループともに複数ファイルで構成が可能。メモリベーステーブルの場合にはGCPとLCPというチェックポイントでデータの保存性を確保していたが、ディスクベースの場合には一般的なRDBMS同様、REDOの概念が登場する(これ以降は常識だと思うので詳細は割愛)。
なお、ログファイルグループは、1つのクラスタにつき1つだけ持つことが可能。

実際の操作を確認していく。まずはここでの構成を紹介

今回説明する構成は以下の通り。db01、db02は専用データノードで同一データを保持するよう設定、db03、db04は管理ノードとSQLノードを兼ねる。各ノードはVMWareのゲストで割り当てメモリはそれぞれ256MB。SQLノードはクラスタテーブル以外にも別用途でMyISAMInnoDBのデータを持たせたいのでデータノードとは設定を変えている。

設定ファイルの内容は以下の通り。

  • /var/lib/mysql-cluster/config.ini(配置先はdb03、db04のみ。データノードには不要)


[NDBD default]
NoOfReplicas=2
datadir=/var/lib/mysql-cluster

[NDB_MGMD]
Id=1
hostname=192.168.10.153
[NDB_MGMD]
Id=2
hostname=192.168.10.154

[NDBD]
Id=3
hostname=192.168.10.151
[NDBD]
Id=4
hostname=192.168.10.152

[MYSQLD]
Id=5
hostname=192.168.10.153
[MYSQLD]
Id=6
hostname=192.168.10.154

  • /etc/my.cnf(db03,db04に配置)


[mysqld]
port=3306
socket=/var/lib/mysql/mysql.sock
pid-file=/var/lib/mysql/mysqld.pid
old_passwords=0
tmpdir=/tmp
default-character-set=utf8
datadir=/var/lib/mysql/data
ndbcluster

innodb_data_home_dir=
innodb_data_file_path=/var/lib/mysql/data/ibdata1:300M:autoextend
innodb_autoextend_increment=8
innodb_log_group_home_dir=/var/lib/mysql/logs/ilog
innodb_log_file_size=50M
innodb_log_files_in_group=3
innodb_max_dirty_pages_pct=98
innodb_flush_method=O_DIRECT
innodb_flush_log_at_trx_commit=1
innodb_buffer_pool_size=64M
innodb_additional_mem_pool_size=8M
innodb_log_buffer_size=8M
back_log=50

log-error=/var/lib/mysql/logs/elog/mysqld.err
log-warning=2
log-bin=/var/lib/mysql/logs/blog/mysqld-bin.log
log-bin-index=/var/lib/mysql/logs/blog/mysqld-bin.index
sync_binlog=0
max_binlog_size=1G
query_cache_type=0 #SQLノードでSQL結果をキャッシュしないようクエリキャッシュはオフ
table_cache=10000
key_buffer=16M
thread_cache_size=0
max_heap_table_size=32M
max_connections=1000
max_allowed_packet=1000M
read_buffer_size=2M
read_rnd_buffer_size=16M
sort_buffer_size=8M
join_buffer_size=8M
tmp_table_size=16M
binlog_cache_size=1M
bulk_insert_buffer_size=64M
lower_case_table_names=1
transaction-isolation=Read-Committed
max_connect_errors=10000
skip-ssl

[mysql_cluster]
ndb_connectstring=192.168.10.153
ndb_connectstring=192.168.10.154

[ndb_mgmd]
config_file=/var/lib/mysql-cluster/config.ini

[mysqld_safe]
pid-file=/var/lib/mysql/mysqld.pid
log-error=/var/lib/mysql/logs/mysqld_safe.log
log-warning=2

[mysql]
default-character-set=utf8
socket=/var/lib/mysql/mysql.sock

  • /etc/my.cnf(db01,db02に配置)


[mysqld]
port=3306
socket=/var/lib/mysql/mysql.sock
old_passwords=0
tmpdir=/tmp
default-character-set=utf8
ndbcluster
skip-innodb # データノードはクラスタ専用とするため、InnoDBエンジンは不要
datadir=/var/lib/mysql/data

log-error=/var/lib/mysql/logs/elog/mysqld.err
log-warning=2
sync_binlog=0
max_binlog_size=1G
query_cache_type=0 #クエリキャッシュはオフ
table_cache=10000
thread_cache_size=0
max_heap_table_size=32M
max_connections=1000
max_allowed_packet=1000M
read_buffer_size=2M
read_rnd_buffer_size=16M
sort_buffer_size=8M
join_buffer_size=8M
tmp_table_size=16M
binlog_cache_size=1M
bulk_insert_buffer_size=64M
sql-mode="ONLY_FULL_GROUP_BY,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_DIR_IN_CREATE,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE"
lower_case_table_names=1
transaction-isolation=Read-Committed
max_connect_errors=10000
skip-ssl

[mysql_cluster]
ndb_connectstring=192.168.10.153
ndb_connectstring=192.168.10.154

[ndb_mgmd]
config_file=/var/lib/mysql-cluster/config.ini

[mysql]
default-character-set=utf8
socket=/var/lib/mysql/mysql.sock

[mysqld_safe]
pid-file=/var/lib/mysql/mysqld.pid
log-error=/var/lib/mysql/logs/mysqld_safe.log
log-warning=2

  • クラスタを起動し、正常に管理クライアント(ndb_mgm)でステータスが取得できていることを確認。


[root@db04 elog]# ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Connected to Management Server at: 192.168.10.154:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 2 node(s)
id=3 @192.168.10.151 (mysql-5.1.27 ndb-6.3.17, Nodegroup: 0, Master)
id=4 @192.168.10.152 (mysql-5.1.27 ndb-6.3.17, Nodegroup: 0)

[ndb_mgmd(MGM)] 2 node(s)
id=1 @192.168.10.153 (mysql-5.1.27 ndb-6.3.17)
id=2 @192.168.10.154 (mysql-5.1.27 ndb-6.3.17)

[mysqld(API)] 2 node(s)
id=5 @192.168.10.153 (mysql-5.1.27 ndb-6.3.17)
id=6 @192.168.10.154 (mysql-5.1.27 ndb-6.3.17)

実際にディスクベーステーブルを作成してみる

  • 1.ログファイルグループの作成

まず最初にUndo Logを保存するためのファイルグループを作成。create logfileコマンドの詳細はここを参照(12.1.9. CREATE LOGFILE GROUP 構文)。「initial_size」、「undo_file_size」の指定でうまくいかないケースも報告されているが今回はこの辺りの説明は割愛(気になる方はこちらをどうぞ)。


mysql> create logfile group lg_1 add undofile 'undo_1.dat' engine ndb;
Query OK, 0 rows affected (23.21 sec)

  • 2.テーブルスペースの作成

続いてテーブルスペースを作成。Oracleによく似ている。ディスクベースの実際のデータを格納するファイルを作成する。


mysql> create tablespace ts_1 add datafile 'data_1.dat'
-> use logfile group lg_1 engine ndb;
Query OK, 0 rows affected (15.54 sec)
テーブルスペースは複数ファイルで構成可能。ここでは試しにもう1つ構成ファイルを足してみる。これでts_1のテーブルスペースをdata_1.datとdata_2.datの2ファイルで構成できた。

mysql> alter tablespace ts_1 add datafile 'data_2.dat'
-> engine ndb;
Query OK, 0 rows affected (3.52 sec)

  • 3.ディスクベーステーブルの作成

ここでようやくテーブルの作成。ここまで来ればもうだいたいわかりましたよね。テーブルを作成する際に、テーブルスペース名を指定する。挿入されたデータは指定されたテーブルスペースに含まれるデータファイルに格納される。


mysql> create table disk_table (
-> a int primary key,
-> b char(10))
-> tablespace ts_1 storage disk engine=ndb;
Query OK, 0 rows affected (1.41 sec)

  • 4.ファイルの確認

MySQL5.0から追加されたinformation_schemaデータベースのfilesテーブルに、MySQL Clusterのディスクベーステーブルの構成ファイル情報が追加される。以下はdata_1.datファイルの情報を検索した例である。


mysql> select * from information_schema.files where file_name='data_1.dat'\G
*************************** 1. row ***************************
FILE_ID: 0
FILE_NAME: data_1.dat
FILE_TYPE: DATAFILE
TABLESPACE_NAME: ts_1
TABLE_CATALOG: NULL
TABLE_SCHEMA: NULL
TABLE_NAME: NULL
LOGFILE_GROUP_NAME: lg_1
LOGFILE_GROUP_NUMBER: NULL
ENGINE: ndbcluster
FULLTEXT_KEYS: NULL
DELETED_ROWS: NULL
UPDATE_COUNT: NULL
FREE_EXTENTS: 128
TOTAL_EXTENTS: 128
EXTENT_SIZE: 1048576
INITIAL_SIZE: 134217728
MAXIMUM_SIZE: 134217728
AUTOEXTEND_SIZE: NULL
CREATION_TIME: NULL
LAST_UPDATE_TIME: NULL
LAST_ACCESS_TIME: NULL
RECOVER_TIME: NULL
TRANSACTION_COUNTER: NULL
VERSION: 1
ROW_FORMAT: FIXED
TABLE_ROWS: NULL
AVG_ROW_LENGTH: NULL
DATA_LENGTH: NULL
MAX_DATA_LENGTH: NULL
INDEX_LENGTH: NULL
DATA_FREE: NULL
CREATE_TIME: NULL
UPDATE_TIME: NULL
CHECK_TIME: NULL
CHECKSUM: NULL
STATUS: NORMAL
EXTRA: CLUSTER_NODE=3
(中略)
2 rows in set (0.05 sec)
次に、OSのファイルシステム上のファイルについても確認する。Id=3のデータノードであるdb01号機で確認してみる。以下のように、クラスタのdatadir配下にデータファイル、ログファイルが作成されていることがわかる。

# hostname -s
db01
# find /var/lib/mysql-cluster/ -name undo_1.dat -type f
/var/lib/mysql-cluster/ndb_3_fs/undo_1.dat
# find /var/lib/mysql-cluster/ -name data_?.dat -type f
/var/lib/mysql-cluster/ndb_3_fs/data_2.dat
/var/lib/mysql-cluster/ndb_3_fs/data_1.dat

  • 補足.性能チューニング

詳細は今後ブログで触れたいが、影響の大きいものとして、DiskPageBufferMemoryパラメータがある。参考:17.3.4.5. Defining Data Nodes 以下は該当箇所の抜粋。


DiskPageBufferMemory

This determines the amount of space used for caching pages on disk, and is set in the [ndbd] or
[ndbd default] section of the config.ini file. It is measured in bytes. Each page takes up 32 KB.
This means that Cluster Disk Data storage
always uses N * 32 KB memory where N is some non-negative integer.

ディスクベーステーブルを削除してみる

最後に、ディスクテーブルの削除方法についても触れておく。テーブルスペース、ログファイルグループなど、MySQLのテーブルとしては少し変わった方法で作成したテーブルだが、削除方法も少し変わっている(Oracleでは普通だけど)ので、以下で簡単に説明。

  • 1.ディスクベーステーブルを削除


mysql> drop table disk_table;
Query OK, 0 rows affected (1.42 sec)

  • 2.テーブルファイルの削除


mysql> alter tablespace ts_1 drop datafile 'data_2.dat' engine ndb;
Query OK, 0 rows affected (0.57 sec)

mysql> alter tablespace ts_1 drop datafile 'data_1.dat' engine ndb;
Query OK, 0 rows affected (0.43 sec)

  • 3.テーブルスペースの削除


mysql> drop tablespace ts_1 engine ndb;
Query OK, 0 rows affected (0.50 sec)

  • 4.ログファイルグループの削除


mysql> drop logfile group lg_1 engine ndb;
Query OK, 0 rows affected (0.59 sec)

以上でMySQL Clusterのディスクベーステーブルの紹介終了。久々のブログ更新でしたが、今回はこれでおしまい。MySQL Clusterについては、今後バックアップ・リカバリやチューニング、制限事項、ハマリポイントなどのTipsについてちょいちょい記事をアップしていきたいと思いますm(_ _)m