More info...

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 件のコメント:

  1. 大変恐縮なのですが、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

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

    返信削除
  2. 田中さん、コメントありがとうございます。

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

    返信削除
  3. 返信ありがとうございます。

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

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

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

    返信削除
  4. 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

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

    返信削除
  5. さむらいさん、

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

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

    返信削除