目次

MySQLdを冗長構成にする

Last Update: 2014/10/14

近年インターネットに公開されている様々なシステムは、LAMP(Linux/Apache/MySQL/PHP)を利用しているものが多い。 その上、最近では高可用性が要求されるような状況が増えてきている。

このようなシステムを構築する際に問題になるのが、システムの冗長性を確保して、サービスシステム内の一部が停止してもサービス自体は継続できるようにする手法である。

このような時に、Web側システムはLoadBalancer等をうまく使うことで冗長性を確保できる「場合もある」が、DBはそう簡単に問屋がおろしてくれない。 これは、DBが扱うデータがatomicでなければならず、その状態を保持することが難しいからである。

ここでは、MySQL 5.5.31を利用した冗長構成作成に関して記録しておく。

なお、MySQLはOracleに買収されたので、MariaDBがForkされた。MariaDB5.5では以下の手法は利用できた。

参考URL

手法検討

MySQLdを冗長構成にする方法としては、一般に以下の3つがある。

手法 同期 備考(個人的なイメージとも云う)
Replication async 標準で利用できる。比較的簡単。障害時にデータがLostする可能性がある
Cluster sync 詳しく知らない。Cluster用のMySQLdが必要。チューニングが難しい。システムが大きくなる。
semi-sync Replication semi-sync 標準で利用できるが、Pluginが必要。比較的簡単。障害時のData lostの可能性は低いが、性能も低下する

Replication

Replicationは、最も設定が簡単でその割に比較的よく動作するので、従来(5.3以前)は非常によく利用されていた。 Replicationの問題は、Master-Slave間で同期する際に、同期にタイムラグがあることである。

client           master              slave
      --(SQL)---> 処理
      <--(返事)--      --(binlog)--> 処理
                      <---(同期)--- 

SlaveがMasterからバイナリLOGを取得し、更新を実行することで同期するしくみ。 このような処理の流れなので、同期はasyncとなる。その結果、タイミングによってデータに不整合が起きる可能性がある。

障害 Service継続 Connection継続 Transaction継続 同期
Master停止 × × 停止
Slave停止 停止

障害機を復帰させる際には、一度サービスを停止し、DBの同期をとる必要がある。 復帰させる際の停止を避けるには、Slaveを2台準備し、1台の停止では同期が外れないようにするなどの対策をとる必要がある。

Cluster

Clusterには詳しくないが、Clusterは、Replicationと異なり、システム側で同期を保障する構成である。 MySQLでClusterを構築するには、少なくとも

の3機能が必要になる。縮退すれば最低3台から構築できるが、冗長化を考えるなら、一般には最低で5台のServer(Data x2/SQL x2/Mgmt x1)を準備すべきであろう。(Clusterの実験をするだけなら3台から構築できる)

Web ApplicationのためにMySQL Clusterを利用するなら、サーバー停止などの場合の運用も考慮するとWebServerにhttpd+SQL nodeを入れ、WebServerのApplicationからlocalhostのSQL nodeを参照するようにするのが比較的良い構成と考えられる。こうすれば、WebServerとSQL nodeの通信の部分を非常に単純化して考えられる。近年しばしば利用されるFastCGIを利用するなら、FastCGI ServerにSQL nodeを入れればよい。

障害 Service継続 Connection継続 Transaction継続 同期
SQL node停止 継続
Data node停止 継続
Mgmt node停止 継続
SQL-Data間障害 × 継続
Data-Data間障害 × 状況による

もちろん、常識的に考えて、全Data nodeが非同期に死んだ場合など、同期を継続できない状況は考え得る。

なお、Clusterを構築する場合、ネットワークの設計だけでなく、データ構造などもしっかり検討する必要がある。

Semi-Synchronous Replication

Semi-Synchronous Replication(以下Semi-Sync)は、(いわゆる標準の)Replicationの(同期が外れやすい)問題に対する解決策の一つとして利用できる。

client           master           slave
      --(SQL)---> 処理
                       --(同期)--> 前処理 (binlogのcacheing)
                       <--(返事)--
      <--(返事)--                  後処理 (DBファイルへの変更のCommit)

Semi-Syncでは、上記のような流れで処理が行われるため、データの破損は非常に起こりにくい形になっている。実際、データの破損が起こる条件を想定することは難しい。 その代わりに、この構成の場合、ClientがSQL処理終了を受けとるまでの時間が長くなる。

masterがslaveに送った同期処理は、slave側で一度記録された段階でslaveからmasterに返事が返る。その後、slaveではゆっくり同期処理をする。 その為、「完全な同期状態ではない」瞬間が存在することになる。Semi-Syncと呼ばれるのはその為である。

実際には、slaveが「返事を行った後、同期処理を終了するまで」の段階で障害が発生したとしても、単に同期処理前までRollbackし、再度同期処理を行うことで同期状態を維持する。その為、実質上masterとslaveが非同期な状態になるシチュエーションはほとんど無いと考えられる。

障害 Service継続 Connection継続 Transaction継続 同期
Master停止 × × 継続
Slave停止 継続

障害機を復帰させる場合、通常はサービスを停止する必要はない。復帰時点で再度同期される。つまり、非同期のReplicationでどうしても必要になるFailbackを考慮する必要は無くなる事が期待できる。

注意点

上記説明の際に、DBプログラミングを行う際に必要な注意事項は記載していない。 DBプログラミングは、データのAtom性を確保するために、DB側だけでなくプログラム側でも注意すべき点があるが、そこには触れないものとする。

手法

上記及びその他の理由(割愛)により、Semi-Sync ReplicationとKeepalivedを用いて、MySQLdを冗長化する。

Keepalived

Semi-Sync ReplicationでMultiMaster Replicationを組むことで、いわゆるMultiHeadでありかつFailback操作が不要のDBシステムが組める。

しかし、MultiHead DBで非同期処理をする場合、SQL Queryを出すApplicationの側で考慮すべき事項が非常に多い。特に、データのAtomic性に関しては非常に短時間であっても保障されない場合がある。Slave側がMasterにAckを帰した直後からData操作が終了するまでの間は、MasterとSlaveのDataは同期していない(つまりSlave側のDataは古い)状態になる。これは、特にトランザクションの多いDBで問題が発生する。 このような用途ならばClusterやOracle/DB2等を利用するべきであろう。

Semi-Sync ReplicationでMultihead DBを組むのが(実用上)ベストだと筆者が思う状況は、

である。つまり、

場合である。要するに、

という場合である。

このような要望に答えるための実装にはいくつかあるが、今回はKeepalivedを利用する。

Keepalivedは「VRRP」を利用して、「あるサービスが死んだ」ら「それをトリガーとしてVRRP的切替を行う」daemonである。

これを利用することで、アプリケーションからは『冗長化されたMySQLサーバー』を『いつも通り』に利用できるようになる。

技術的には半端なかき方をしてますので、気持ちだけわかってください

構成

HW OS MySQL keepalived その他
XCP上のVM CentOS 6.4 5.5.31 1.2.7 MySQLdはremiで公開されているもの

上記VMを2台(以下 my01, my02とする)構築し、冗長系を構築した。

構築

CentOSのInstall等は全て割愛。注意点は

MySQL 初期構成

Semi-Synchronous Replication

MariaDB 5.5.31でも動作する可能性がありますが未検証です。しました。

ここまでくればあと少し。一気に行きます。

落ち穂拾い

今の状態で、mysqldはMultimaster Semi-Synchronous Replicationが組めている状態になる。 あとは、keepalivedでClientからの通信を受けるIPアドレスを生かすだけ。

これでOKのはず。