Yakstは、海外の役立つブログ記事などを人力で翻訳して公開するプロジェクトです。
10年弱前投稿 修正あり

mysqlnd_msによるシンプルなMySQLのマスタHA

PHP言語組込みのMySQLネイティブドライバmysqlndへのエクステンションへの高可用プラグインを紹介する。 障害時に夜起きたくないし、翌朝対処すればいい、というミッションクリティカルでないシステムに気軽にHAを導入したい場合にご検討を。

原文
Simple MySQL Master HA with mysqlnd_ms (English)
原文ライセンス
CC BY-NC-SA
翻訳依頼者
B5aa4f809000b9147289650532e83932
翻訳者
B5aa4f809000b9147289650532e83932 taka-h
原著者への翻訳報告
未報告


2、3日前にmysqlnd_msをフィリピンのPHPユーザーグループに発表できたことをうれしく思っている。MySQLのマスタ-スレーブ構成での、mysqlndプラグインは、mysqlndエクステンション(訳注: mysqlndはPHP言語に対するPHPライセンスのMySQLネイティブドライバであり、PHP5.3以降には組込となっている)への透過的レイヤである。 これによって、アプリケーションを一切変更することなく、Read-Writeを分離したりスレーブへの読取りを負荷分散させたりできる。 しかし、このプラグインで、高可用の1つのパターンが実現できることをご存知だろうか? プレゼンテーションでは2種類の方法を紹介した。1つ目はMySQL非同期レプリケーションをマスタ-スレーブ構成かマスタ-マスタ構成で利用したもの、2つ目は全ノードが書込み可能な全プライマリなクラスタとなるものである。 最初の章では、シンプルなHAソリューションを1つ目の方法で達成できたことを示す。最初に述べておくと、ここにあるサンプルコードは私のGithubレポジトリ から取得できる。

mysqlnd_msプラグインを使う為には、JSONフォーマットの外部コンフィグが追加で必要となる。 このコンフィグファイルは、マスタとスレーブノード群、フェイルオーバーの設定、およびフィルタ(コネクション選択方法)を定義する。

では、私が使ったmysqlnd_msのコンフィグから始めてみよう。 mysqlnd_ms_ms.ini ファイルは以下の通りである。

{
  "primary": {
    "master": {
      "master_1": {
        "host": "127.0.0.1",
        "port": "33001"
      }
    },
    "slave": {
     }
  },
  "standby": {
    "master": {
      "master_1": {
        "host": "127.0.0.1",
        "port": "33002"
      }
    },
    "slave": {
    }
  }
}

ここでは2つアプリケーションを定義した。1つは"primary"、もう1つは"standby"だ。単純化するため、スレーブは定義しなかった。 2つのMySQLインスタンスは、33001, 33002ポートでマスタ-マスタ構成で動作している。

mysqlnd_ms.enable = 1
mysqlnd_ms.disable_rw_split = 1
mysqlnd_ms.force_config_usage = 1
mysqlnd_ms.config_file = /home/revin/git/demo-me/phpugph201407/mysqlnd_ms_ms.ini

これがテスト用に使った master-slave.ini だ。最初の行は単にプラグインを有効化している。 2行目の、mysqlndms.disablerw_splitは、全てのクエリをマスタに送ることを示しており、このケースではマスタのみを利用するためである。

PHPスクリプトとしては、フルコピーがここにあり、これがどういう仕組みで何をしているかを、幾分長い説明をしたいと思う。

  1. テストを開始するにあたって、DROPとCREATEクエリでテストテーブルをセットアップする。
  2. それから、for文のループに突入する。SELECT文のvalidateの後にINSERTを発行し、SELECT文では、新しく挿入された行や、現在のアクティブなサーバIDやコネクションIDといった追加情報をvalidateする。
  3. ループ内のイテレーションごとに、データベースサーバへの非永続的なコネクションを疑似する為に新しいmysqliオブジェクトが生成される。
  4. 新しいコネクションを生成する為に、 connect_mysql 関数が呼ばれ、成功すればmysqliオブジェクトが返される。ここで気にとめておかなければいけないのは、mysqlnd_msは標準では遅延コネクションを利用しており、すなわちmysqliオブジェクトが生成されただけでは、サーバーにまだ実際に接続されていない。 'SELECT 1' のようなステートメントを手動で発行するか、 mysqli::real_connect を呼ぶ必要がある。 mysqli::ping さえも、前述の行為のあとでなければ動作しなかったので、これに関してはバグ報告をした。
  5. mysqliオブジェクトが返却されたあと、INSERT文が実際に接続を確立し、ステートメントを実施する為に、mysqlnd_msのトリガを引く。コネクションが確立されていなかったら、query_write_mysql関数が検知して、connect_mysqlで再接続がされる。connect_mysql関数では、コネクション関係のエラーコード 20022003 で接続に失敗した場合、プライマリへの接続が最低10回リトライされる。10回のリトライで接続できなかったら、アプリケーションは /tmp/PRIMARY_HAS_FAILED という識別用のファイルを生成し、セカンダリ(スレーブ、またはパッシブなマスタ)に接続を試行する。

下記にプライマリがサーバID 101、スタンバイが102の時の実行例を示す。

[revin@forge phpugph201407]$ php -c master-slave.ini master-slave-ng.php
Last value 0001 from server id 101 thread id 7
Last value 0003 from server id 101 thread id 8
37: [2002] Connection refused
Connection to host 'primary' failed: [0] Connection refused, retrying (1 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (2 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (3 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (4 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (5 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (6 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (7 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (8 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (9 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (10 of 10) in 3 seconds
The primary host 'primary' has failed after 30 seconds, failing over to standby!
52: [2002] Connection refused
Last value 0004 from server id 102 thread id 635
Last value 0006 from server id 102 thread id 636
Last value 0008 from server id 102 thread id 637
[...]

これは完全なものではなく、たくさんの制約がある。しかし、簡易なHA要件が求められているとき、例えばものすごくクリティカルなアプリケーションを運用しているわけではないが、夜起こされたくなく、どちらかというと朝になってから問題に対応するようなケースには向いているだろう。ここにいくつか注意を記載する。

  • マスタ-スレーブ構成の場合、failoverではプライマリ(マスタ)を使えなくするだけなので、翌朝データを再構成する必要がある。
  • マスタ-マスタ構成の場合、マスタを起動するだけでオンラインで復旧できる。マスタのレプリケーションが追いつき、 /tmp/PRIMARY_HAS_FAILED ファイルをアプリケーションのスイッチバック用に削除すればよい。
  • /tmp/PRIMARY_HAS_FAILED の使い方は、まだ基礎段階で他の方法もとりうる。マスタを復旧させる為には人手の介入が必要となる為、failover発生時に通知を送る、など考慮すべきである。

同じことは少しコーディングすれば達成できるだろうが、このプラグインを利用すればより手間要らずで恩恵が受けられる。

このプラグインを、複数のMasterが書込みを受け付けられる2番目の活用例としてPercona XtraDB Clusterを使って検証を行っている。いくつか興味深いことが分かったので、しばしお待ちいただきたい。

次の記事
Linuxで、どのプロセスがページングを行っているのか調べるには?
前の記事
結果整合性データベースのいま

Feed small 記事フィード

新着記事Twitterアカウント