ちょっと硬派なコンピュータフリークのBlogです。

カスタム検索

2011-04-14

MySQL Cluster 7.2見参!Webでも使える熱いヤツがやってきた。

前回のエントリではMySQL 5.6の新機能についてレビューを行ったが、MySQL User Conferenceに合わせる形でMySQL Clusterの新しい開発版であるバージョン7.2も発表された。一見すると追加された新機能の数は少なくMySQL 5.6ほどのインパクトはないが(というかMySQL 5.6の新機能がありすぎなわけだが)、実は7.2ではMySQL Clusterにとって非常に重要な改善がなされているのだ!

というわけで、今日はMySQL Cluster 7.2の新機能を紹介しよう。

JOINの性能が改善!

まず最初に最も重要なことについて述べよう。MySQL Cluster 7.2ではJOINの性能が改善している。非常に大切なことなのでもう一度言おう。MySQL Cluster 7.2ではこれまで最大の弱点であったJOINの性能が改善している!

MySQL Clusterバージョン7.1では、SQLノード上でJOINを行うとJOINされるテーブルは1レコードずつフェッチされる仕組みになっている。何故ならば、どの行がJOINされるかということは、駆動表からフェッチしたレコードを評価するまでは分からないからだ。そのため、駆動表のレコードをひとつ評価するたび、JOINされるテーブルからレコードをフェッチするという動作をするのだ。

この動作はMySQLそのものの挙動であり、どのストレージエンジンでも共通である。InnoDBのようにひとつのホストマシンだけで完結したストレージエンジンにとってはこのような動作は問題にならないが、MySQL ClusterはSQLノード、データノードという2つの物理的に異なるマシンにインストールされたノード同士が通信を行って各種操作を行うようになっているため、レコードをフェッチするたびにネットワーク通信を行う必要があり、JOINの性能はなかなか出ないという問題がある。

その問題が、MySQL Cluster 7.2.0では改善している。かねてから開発者のブログで話題に上っていたSPJという機能が実装されているのだ!SPJについては以前本ブログでも取り上げたことがある。正確にはDistributed Pushed Down Joinと呼ばれる機能であり、文字通りJOINをデータノード側へPush Downし、データノード内でJOINをやってしまおうという機能である。これにより劇的にネットワークのラウンドトリップが減少し、その結果JOINの性能が飛躍的に向上するのである。

ただし、SPJには今のところ次のような制限がある。

  • JOINするカラムの型はまったく同じでなければならない。(BIGINTとINTはSPJできない。)
  • クエリの結果にBLOBまたはTEXTが含まれてはいけない。
  • FOR UPDATEは非サポート
  • サポートしているJOINのレコードアクセスタイプはeq_refまたはconstのみ。
  • ユーザーレベルパーティショニング(別名Distribution Awareness)が行われたテーブルは非サポート

※レコードアクセスタイプについては、こちらの記事を、Distribution Awarenessについてはこちらの記事を参照のこと。

SPJが有効であるかどうかは、ndb_join_pushdownシステム変数で確認することが可能だ。デフォルトはTRUE。つまり有効。上記のような制限にひっかかると、SPJを使わない通常のJOINアルゴリズム(Nested Loop)が使われることになる。SPJが使われているかどうかということについては、EXPLAINのExtraフィールドで確認できる他、統計値をSHOW STATUS VARIABLES(ステータス変数)におけるNdb_pushed_queries_defined、Ndb_pushed_queries_dropped、Ndb_pushed_queries_executed、Ndb_pushed_readsで確認することが出来る。

参照:MySQL Cluster System Variables - MySQL 5.1 Reference Manual

memcachedインターフェイスの追加

MySQL 5.6でもテクニカルプレビューとしてmemcachedインターフェイスが追加されたが、MySQL Clusterも同様にテクニカルプレビューとしてmemcachedインターフェイスが追加されている。これまで、MySQL Clusterに対して高速に(SQLによるオーバーヘッドを生じずに)アクセスする方法としては、NDB APIという独自のAPIを叩く必要があった。NDB APIはトランザクション、スキーマ、スキャン、ソートなどに対応した高機能なAPIであり、かなり複雑な処理にも対応している。だが、いかんせん一般的なAPIであるとは言えず、これを用いた開発などは普及していないのが現状だ。

そこで、皆さんおなじみのmemcached APIを通じてMySQL Clusterへアクセスしてもらおうということになったわけだ。memcachedなら既に一般的なので、アプリケーションの接続先をMySQL Cluster版memcachedへ向けるだけで対応が可能だ。MySQL Clusterは不揮発、高可用性、自動フェイルオーバー、オンラインバックアップ、レプリケーションといった便利な機能を備えている。そのような高機能なDBに対してアクセスするのに使うAPIは、memcachedのそれと同じなのだ。しかもmemcached経由でSETしたデータをSQLでもアクセスすることが出来るのだ!アプリケーションが独自にMySQLへ格納したデータをmemcachedへキャッシュしなおす(またはその逆)という必要がないのである。便利極まりないと言えよう。

MySQL 5.6では、mysqldの内部にmemcached APIを提供するためのプラグインが追加される形となっている。一方、MySQL Clusterではmemcachedの透過的なストレージとして、NDB APIを経由してMySQL Clusterへアクセスすることになる。つまり、memcachedがAPIノードのひとつとして機能する形となるのである。なので、どのようにmemcachedを配置するかということについては、どのようにSQLノードを配置するかという問題と基本的には同じである。この点については、こちらの記事で以前解説したので見て欲しい。

使い方については、下記のサイト(英語)で詳細に解説されているので読んでいただきたい。現在の仕様では、プレフィックスによって格納先のテーブルを分ける、MySQL Clusterではなく本来どおりmemcacheに格納するといった使い分けが出来るようになっている。

参照:Scalable, persistent, HA NoSQL Memcache storage using MySQL Cluster

権限情報の共有

これまで、GRANTによって付与されたユーザーの権限情報は、SQLノードごとに設定する必要があった。しかし、MySQL Cluster 7.2ではユーザーの権限情報はデータノード上に格納され、すべてのSQLノードで共有出来るようになっている。これにより、DBAによる管理タスクがひとつ減ることになる。

デフォルトパラメーターの変更

これまでの設定値はリアルタイムデータベースとしての矜持を反映した内容となっており、Web等で利用するには厳しすぎるタイムアウトが設けられていた。そこで、制限を次のように緩和している。

  • HeartbeatIntervalDbDb: 1.5秒から5秒へ。
  • ArbitrationTimeout: 3秒から7.5秒へ
  • TimeBetweenEpochsTimeout: 4秒から無制限へ
  • SharedGlobalMemory: 20MBから128MBへ
  • MaxParallelScansPerFragment: 32から256へ

これらは主にWebでディスク型テーブルを用いる場合向けの設定内容となっているので、インメモリ型のテーブルだけを利用する場合には改めてチューニングする必要があるだろう。(ただし制限が緩くなっているので管理の手間は減るはずだ。)

まとめ

既存のユーザーにとっては、JOINの性能が改善しているというだけでこのバージョンを利用するだけの価値があるだろう。また、これまでJOINの性能があまり良くないという理由でMySQL Clusterの利用を躊躇っていたユーザーにとっても、これは朗報である。制限があるとは言え、かなりの場合にはSPJが利用できると考えられるので、JOINの性能を気にする必要性は少なくなるだろう。

広く一般的に利用されているmemcached APIに対応したのは非常に意味があると個人的には思っている。Web開発の現場においても、高機能かつ高性能なMySQL Clusterをガンガン利用して貰えると喜ばしい限りである。memcachedではなく、MySQL Clusterのデータノードがズラリと並べるのがWeb開発におけるトレンドになる。そんな日が来るのを予感させてくれるのである。

というわけで、MySQL 5.6と同じく現時点では開発版なので利用には注意が必要だが、是非こちらも試して頂きたい!

5 コメント:

田中 さんのコメント...

大変恐縮なのですが、SQLクラスタのバックアップ〔START BACKUP〕について教えて下さい。

データノードで『ndb_restore』のコマンドを打つと、
Stop GCP of Backup: 9780
Configuration error: Error : Could not alloc node id at ec2-46-51-225-140.ap-northeast-1.compute.amazonaws.com port 1186: Connection done from wrong host ip 10.146.91.231.
Failed to initialize consumers

NDBT_ProgramExit: 1 - Failed

とエラーが帰ってきてリストアが上手くできません。
このような結果は何がいけないのでしょうか?

Mikiya Okuno さんのコメント...

田中さん、コメントありがとうございます。

ndb_restoreはAPIノードとして動作するので、ndb_restoreを実行するホストに対するAPIノードの空きスロットがなければいけません。"Connection done from wrong host ip"と出ているので、おそらくconfig.iniに登録されているAPIノード以外のノードからndb_restoreを実行されたのではないでしょうか。

田中 さんのコメント...

返信ありがとうございます。

無事にndb_restoreが上手くできました。
空きスロットを作る!の意味がすぐに理解できませんでしたが、ようやく理解できました。

他の人も解るように、ここに追記でかかせて頂きます。
空きスロットを作るというのは、config.iniにhostnameを設定せずに、NodeIdだけ設定した[MYSQLD]を作る事でした。

ご多忙の中、返信を頂き誠にありがとうございました。

さむらい さんのコメント...

okunoさん

こんにちは、いつも漢道でmysqlをはじめ、人生を学ばせていただいております。

ただいま、mysqlcluster7.2.2とmysql5.5.16を使用して、クラスタを構築していますが、sqlノード起動時にエラーが発生したため、ご知恵をお借りしたく投稿いたします。

具体的なエラー内容は以下になります。
120116 13:24:36 [ERROR] /usr/local/mysql-5.5.16/bin/mysqld: unknown option '--nd
bcluster'
120116 13:24:36 [ERROR] Aborting

my.cnfでndbclusterを記載したのがダメなようですが、5.5のリファレンスマニュアルを読んでもndbclusterオプションは対応しているとのことなので、不可解です。

一応my.cnfファイルの内容も添付します。
[mysqld_multi]
mysqld=/usr/local/mysql/bin/mysqld_safe
mysqladmin=/usr/local/mysql/bin/mysqladmin
user=mysql
password=mysql

[mysqld]
#federated

[mysqld1]
#skip-federated
ndbcluster
ndb_connectstring = "192.168.157.219"
server-id = 1
port = 3306
socket = /tgn/mysql5.5/mysqldb/mysql.sock
pid-file = /tgn/mysql5.5/mysqldb/mysql.pid
skip-innodb
skip-federate
skip-name-resolve
log-error = /tgn/mysql5.5/mysqldb/mysqld.err
log-warning = 2
sync_binlog = 0
max_binlog_size = 500M
query_cache_size = 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 = 8M
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
skip-ssl

[mysql.server]
basedir=/usr/local/mysql

[client]
port=3306
#socket=/tgn/mysql/client/mysql.sock
socket=/tgn/mysql5.5/mysqldb/mysql.sock

[mysqld_safe]
pid-file=/tgn/mysql5.5/mysqldb/mysqld_safe.pid
log-error=/tgn/mysql5.5/mysqldb/mysqld_safe.err
#log-warning=2

お手数ですが、ご回答宜しくお願いします。

Mikiya Okuno さんのコメント...

さむらいさん、

コメントありがとうございます。

short answerですが、MySQL Clusterに同梱されているmysqldを使って下さい。

コメントを投稿