NDB Clusterをmemcachedで簡単にアクセス

さて、来年は<a href="https://northwood.blog.fc2.com/blog-entry-9182.html">1TB以上のメモリ</a>(<a href="https://pc.watch.impress.co.jp/docs/column/semicon/721621.html">NVDIMM-F</a>)がリリースされる可能性があるため
インメモリDBの話を。

NDB Clusterについての説明は<a href="https://thinkit.co.jp/story/2015/07/21/6191">Oracle社員の公式資料</a>をご確認ください。
以前はMySQL Clusterと呼ばれてたけど
InnoDB ClusterがリリースされてNDB Clusterに名前が変わったインメモリDBですね。
日本ではほとんどサポートなしでは使われてませんが
中国ではサポートなしで使われていることが多いようです。

現バージョンは7.5がリリースされて1年ちょっと。
7.6が来年前半にリリースされることでしょう。
NDB Clusterは勘違いされやすいですが
SSDにデータを保存できたり(性能は1/10)、
Memcache APIを使ってmemcachedでアクセスできたりします。
本日は後者について書きます。

#マニュアル
残念ながらMemcache APIについて英語のマニュアルしかないです。
https://dev.mysql.com/doc/ndbapi/en/ndbmemcache.html
NDB Clusterそのもののコンフィグなどは日本語マニュアルが後述しますがあります。

#インストール準備
仮想マシンはSQLノード2台、データノード2台でMGMTはSQLノードに。最低4台必要です。
DockerはNDB ClusterをK8sやホストレスコンテナで本番構築するはずがないのであまり意味がないかな。
DockerでNWのホットスポットは作るべきではないです。

インストールドキュメント
日本語ドキュメント
https://dev.mysql.com/doc/refman/5.6/ja/mysql-cluster-install-linux.html
英語ドキュメント
https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-install-linux.html
とありますが
MySQLのインストールをよくする人はもっと簡単な方法があることを気が付くと思います。
aptやyumでレポジトリを登録し、NDB Clusterをインストールする方法です。

```
Debian/Ubuntu系
https://dev.mysql.com/downloads/repo/apt/ でバージョンを確認し
sudo su
VER=0.8.9-1
wget https://dev.mysql.com/get/mysql-apt-config_${VER}_all.deb
dpkg -i mysql-apt-config_${VER}_all.deb
exit

Oracle Linux/RHEL系
https://dev.mysql.com/downloads/repo/yum/ でバージョンを確認し
wget https://dev.mysql.com/get/mysql57-community-release-xxx-xx.noarch.rpm
yum -y install mysql57-community-release-xxx-xx.noarch.rpm
```
どちらを使うべきかですが
https://www.mysql.com/jp/support/supportedplatforms/cluster.html
を見るとサポートが広範囲に及ぶOracle Linux/RHEL系を使うべきだと思います。
ただし、Kernelが新しいと性能向上が期待できるのと
私がRHEL系の記事を書くはずがないのは知られているのでUbuntu16.04で今回は構築をします。

apt-getで先程のパッケージをインストールしていけば
下記の画面になりますので
![Clipboard01.png](https://qiita-image-store.s3.amazonaws.com/0/156034/f1c17a92-39c9-de29-eabf-d21e0592bd94.png)![Clipboard02.png](https://qiita-image-store.s3.amazonaws.com/0/156034/3f342a11-0a59-8fa3-ef26-c67a1958cd84.png)
と7.5を選んでOKでパスワード入力、インストールします。sudoでは最後にエラーになります。

#インストール
[全台]
sudo apt-get update
パッケージリストで反映を確認
sudo apt-cache search mysql | grep ^mysql-cluster

[mysql1][manager]
sudo apt-get -y install mysql-cluster-community-server
sudo apt-get -y install mysql-cluster-community-memcached(Memcache API時のみ)
sudo apt-get -y install mysql-cluster-community-management-server

[mysql2]
sudo apt-get -y install mysql-cluster-community-server
sudo apt-get -y install mysql-cluster-community-memcached(Memcache API時のみ)

[data1]
sudo apt-get -y install mysql-cluster-community-data-node

[data2]
sudo apt-get -y install mysql-cluster-community-data-node

#config
まずはマネージャーサーバのセットアップ
特にサンプルコンフィグなどはないので作成します。
sudo mkdir /var/lib/mysql-cluster
sudo vi /var/lib/mysql-cluster/config.ini

```
[ndb_mgmd]
DataDir = /home/mysql/mysqlc/data/mgmd/
HostName = 10.146.0.1

[ndbd default]
NoOfReplicas                  = 2
DataMemory                    = 2048M
IndexMemory                   = 512M
ODirect                       = true

[ndb_mgmd]
NodeId=1
HostName=10.146.0.2

[ndbd]
NodeId=4
HostName=10.146.0.4
DataDir= /var/lib/mysql-cluster

[ndbd]
NodeId=5
HostName=10.146.0.5
DataDir= /var/lib/mysql-cluster

[mysqld]
NodeID=2
HostName=10.146.0.2

[mysqld]
NodeID=3
HostName=10.146.0.3

[api]
NodeID=6

[api]
NodeID=7

[api]
NodeID=8
```
[mysqld][api]は同じ意味のセクションなのですが今回は意図して別々に書いてます。
[mysqld][api]は設定次第でたくさん使うので多少余裕があったほうがいいかもしれません。

#MGMTノード起動と注意
[ndb_mgmd]で設定したサーバへの設定です
sudo ndb_mgmd --initial --ndb-nodeid=1 --config-file=/var/lib/mysql-cluster/config.ini
で起動し
sudo ndb_mgm -c 127.0.0.1:1186 -e show

```
Connected to Management Server at: localhost:1186
Cluster Configuration
---------------------
[ndbd(NDB)]     2 node(s)
id=7 (not connected, accepting connect from 10.146.0.7)
id=8 (not connected, accepting connect from 10.146.0.8)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.146.0.2  (mysql-5.7.20 ndb-7.5.8)

[mysqld(API)]   4 node(s)
id=2 (not connected, accepting connect from 10.146.0.2)
id=3 (not connected, accepting connect from 10.146.0.3)
id=5 (not connected, accepting connect from any host)
id=6 (not connected, accepting connect from any host)
id=8 (not connected, accepting connect from any host)
```

という風に表示されます。
停止はsudo ndb_mgm -e "1 STOP"
注意としてコンフィグはキャッシュされているため
sudo ndb_mgmd --config-file=/var/lib/mysql-cluster/config.ini --reload
と停止後、リロードオプションを必要とします。

#SQLノード起動
[mysqld]で指定したサーバーへの設定です
sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

```
[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
log-error       = /var/log/mysql/error.log
# By default we only accept connections from localhost
# Disabling symbolic-links is recommended to prevent assorted security risks
ndbcluster
ndb_use_exact_count             = 0
ndb_index_stat_enable           = 0
ndb_force_send                  = 1
ndb_log_update_as_write         = OFF
optimizer_switch                = "engine_condition_pushdown=on"
transaction_isolation           = REPEATABLE-READ
explicit_defaults_for_timestamp = ON
default_storage_engine          = InnoDB
sql_mode                        = NO_ENGINE_SUBSTITUTION
max_connections                 = 1000

ndb-connectstring = 10.146.0.2:1186
```
sudo /etc/init.d/mysqld start
mysql -uroot -p でログインします。

#データノード起動
[ndbd]で指定したサーバーへの設定です
それぞれnodeidで指定してサーバで実行します。
sudo mkdir /var/lib/mysql-cluster
sudo ndbd --initial --ndb-nodeid=4 --ndb-connectstring=10.146.0.2:1186
sudo ndbd --initial --ndb-nodeid=5 --ndb-connectstring=10.146.0.2:1186
ls -l /var/lib/mysql-cluster/
でディレクトリとファイルが作成されているはずです。

#接続の確認
sudo ndb_mgm -c 127.0.0.1:1186 -e show
設定したサーバ5台が認識していることを確認します。

```
Connected to Management Server at: 127.0.0.1:1186
Cluster Configuration
---------------------
[ndbd(NDB)]     2 node(s)
id=4    @10.146.0.4  (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0, *)
id=5    @10.146.0.5  (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.146.0.2  (mysql-5.7.20 ndb-7.5.8)

[mysqld(API)]   5 node(s)
id=2    @10.146.0.2  (mysql-5.7.20 ndb-7.5.8)
id=3    @10.146.0.3  (mysql-5.7.20 ndb-7.5.8)
id=6 (not connected, accepting connect from any host)
id=7 (not connected, accepting connect from any host)
id=8 (not connected, accepting connect from any host)
```

#Memcache APIの起動
ndb_memcache_metadata.sqlを確認します。
ls -l /usr/share/mysql/memcache-api/ndb_memcache_metadata.sql
SQLノードから設定ファイルを流し込みします。
cat /usr/share/mysql/memcache-api/ndb_memcache_metadata.sql  | mysql -uroot -p
念のためもう片方のSQLノードから確認します。
mysql -uroot -p -e "SELECT * FROM ndbmemcache.containers"
各SQLノードでMemcache APIを起動します。
memcached -E /usr/lib/x86_64-linux-gnu/ndb_engine.so -e "connectstring=10.146.0.2:1186" &

#接続の確認
コマンド実行結果から[api]が使われているのがわかります。
空きapiスロットという言葉の意味が最初わからなく苦労しました。
[api]を一つ残しているのはバックアップにも使うとどこかで見た記憶があるからです。

sudo ndb_mgm -c 127.0.0.1:1186 -e show

```
Connected to Management Server at: 127.0.0.1:1186
Cluster Configuration
---------------------
[ndbd(NDB)]     2 node(s)
id=4    @10.146.0.4  (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0, *)
id=5    @10.146.0.5  (mysql-5.7.20 ndb-7.5.8, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @10.146.0.2  (mysql-5.7.20 ndb-7.5.8)

[mysqld(API)]   5 node(s)
id=2    @10.146.0.2  (mysql-5.7.20 ndb-7.5.8)
id=3    @10.146.0.3  (mysql-5.7.20 ndb-7.5.8)
id=6    @10.146.0.2  (mysql-5.7.20 ndb-7.5.8)
id=7    @10.146.0.3  (mysql-5.7.20 ndb-7.5.8)
id=8 (not connected, accepting connect from any host)
```
sudo netstat -natp | grep 11211

```
tcp        0      0 0.0.0.0:11211           0.0.0.0:*               LISTEN      4917/memcached
tcp6       0      0 :::11211                :::*                    LISTEN      4917/memcached
```
でもmemcachedが起動していることを確認できます。

#ベンチマーク試験
普通にmemcachedにデータを入れてもいいのですが
通常のmemcachedと比較しやすいようにベンチマークできるようにします。
Higepon’s blogよりツールをお借りして行います。

```
wget https://github.com/downloads/higepon/mcbench/mcbench-0.0.1.tar.gz
tar zxvf mcbench-0.0.1.tar.gz
cd mcbench-0.0.1
sudo apt-get -y install make gcc erlang
sudo make
cd scripts
./mcbench -b 10000 -t 10 -n 1000 -s 127.0.0.1 -p 11211 -c get
データが格納されていることを確認します。
mysql -uroot -p -e "SELECT * FROM ndbmemcache.demo_table"
```

#バックアップ
バックアップは指定したタイミングで行うことが出来ます。
自動的にディスクに書き出したりはしてくれないようです。
方法は2種類ありますがmysqldumpでバックアップしたほうが簡単だと思います。
mysqldump -h127.0.0.1 -uroot -p ndbmemcache > dump.sql
less dump.sql

#後はMySQL5.6の日本語マニュアルで
NDB Cluster7.4までは日本語化されておりますので
怖がらずに読んで触ってみて下さい。
https://dev.mysql.com/doc/refman/5.6/ja/mysql-cluster-config-example.html
https://dev.mysql.com/doc/refman/5.6/ja/mysql-cluster-system-variables.html
7.5は英語じゃんと思うかもしれませんが
パラメーターで重要な更新はSQLノードで
ndb_read_backup
ndb_fully_replicated
の2つくらいなので後は日本語資料でなんとかなります。

# 終わり
さて、どうでしたでしょうか。
今回はGCP上構築しましたが最低4台なものの、
MySQLとも違い難しいとされるNDB Clusterがmemcachedとして簡単に使える気がしたでしょうか。
もちろん通常のNDB Clusterとしても動作しますので
mysqlからアクセスして5.7互換としてjoinなどできるはずです(性能は純MySQLより遅いですが)
マニュアルに1台50000TPSと目安が書かれているのも助かります。

思いつきでMemcache APIにしましたが資料が日本語ではyoku0825さんのものしかなく苦労しました。

ニッチな要件ではありますがアドテクでは需要がなくはないかな。
NVDIMM-FでNDB Clusterが再起動してもデータが保持できると需要が広がりそうです。

#お詫び
当初Memcache API経由で書き込むと永続化されると書きましたが
検証したところ永続化はされないことがわかりました。
使い物にならないreplicatedの代わりになるというのが正しいようです。
ただ、NDB Clusterもバックアップは同じ制約を受けるため
驚きと共にとても勉強になりました。