ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

Galera Cluster Documentを読む Configuration編(2) STATE SNAPSHOT TRANSFERS

はじめに

Cofiguration編第2弾。

Galeraで最も重要な概念、state transferのうち、State Snapshot Transfer(SST)について。

State Snapshot Transfers — Galera Cluster Documentation

ノードがクラスタからstate transferを要求するとき、デフォルトではISTメソッドを用います。しかし、ISTが可能なノードが見つからなかったり、wsrep_sst_donorパラメタで手動で相手をしていした場合、State Snapshot Transfer(SST)メソッドを用います。

Galera ClusterはSSTに対していくつかのバックエンド・メソッドをサポートしています。これには2種類あり、データベースサーバのインタフェースとクライアントを通じて行うLogical State Snapshotと、物理的に、ノードから直接データをコピーするPhysical State Snapshotがあります。

(訳注:表は省略しますが、mysqldump、rsyc、xtrabackupの3つのメソッドがあります。それぞれ同期のスピード、そしてdonorとして選択したノードが更新を受け付けるかどうか、生きてるノードで利用可能かは、Blocks Donorとなにが違うんだろう?Typeは前に言った論理か物理(logicalかphysical)、最後のDB root accessもわかんないな。mysqldサーバにroot権限でアクセスするかどうかかな?)

このメソッドはwsrep_sst_methodパラメタで指定します。

State Snapshot Transferにとってベストなメソッドは1つではありません。それぞれにあった正解があります。幸いにも受信ノードだけにmethodを設定すればよいです。donorがそれをサポートしていれば、joinerがリクエストしたmethodで転送します。

LOGICAL STATE SNAPSHOT

この方法ではメソッドはmysqldump、1つです。

Logical State Transfer Methodは以下のような利点を持ちます。

  • Liveなサーバ全て利用可能です。実際、初期化されたサーバすべてがLogical State Snapshotを受信できます。
  • 受信ノードとdonorノードが同じ設定を持っている必要性がありません。storage engine optionをupgradeすることができます(???)

例えば、antelopeからbarracudaファイルフォーマットにマイグレーションしてこのtransferメソッドを使うとき、パーティションから別のパーティションへ圧縮またはiblogを移動させればよいのです。

(訳注:これはストレージエンジンの設定変更が伴う同期でも、SQL文を通じて実行するから、実行可能だということでしょうね。)

Logical State Transferは以下の欠点があります。

  • 遅い
  • donorと加入ノード両方にrootで接続可能でないといけない
  • 受信するサーバはデータベースが壊れていない状態でないといけない(?)

mysqldump

mysqldumpの利点は動作しているサーバにstate snapshotが使えることだろう。これはサーバをすtなどアロンで起動させてクライアントコマンドラインからクラスタへの加入を指示できるということである。また、フォーマットの異なるデータベース間で使用することもできる。

mysqldumpは受信ノードがフル機能を持つデータベースであることを必要とする。そのためdonorノードと他のノード両方でroot権限が必要となる。

このtransferメソッドはかなりのデータベースで他のメソッドより遅くなってしまう。しかし、とても小さいデータベースであれば早いこともあるだろう。例えば、ログファイルより小さいデータベースであれば。

注意:警告:このメソッドは各ノードで実行されるmysqldumpのバージョンに非常に敏感である。それぞれインストールされたバージョンがクラスタ間で統一されてないかもしれない。State Snapshot Transferはあるノードと新しいノードで非互換があると失敗してしまう。

時々、mysqldumpが唯一の選択肢なこともある。例えば、MySQL 5.1から5.5へ、InnoDB pluginがupgradeするときである。

mysqldumpスクリプトは送信ノードでのみ動く。スクリプトの出力はMySQL clientを通じて加入ノードに流し込まれる。

mysqldumpがデータベースclientを通じて行われるから、設定はwarep_sst_methodパラメタの設定範囲を超えて、いくつかのステップが必要となる。設定方法は次を参照。

PHYSICAL STATE SNAPSHOT

Physical State Snapshotにはrsyncとxtrabackupの2つのバックエンドメソッドがある。

Physical State Transferメソッドは以下のような利点がある。

  • データをディスク上の物理的なデータコピーを行うので、データベースサーバに依存しない。
  • donorノードが以前参加していたときのデータを上書きするので、データベースが稼働状態にある必要がない(?)
  • 速い

Physical State Transferは以下のような欠点があります。

  • 加入ノードはdonorノードと同じストレージエンジンの設定を持つ必要があります。(ディレクトリ構成など)
  • ストレージエンジンが初期化されたサーバではtransferを受信できない(?)

これはノードがstate snapshotを要求するとき、データベースサーバは変更を適用するために再起動しなければならないことを意味します。(?) データベースサーバはストレージエンジンなしで認証を行うことができないので、state snapshot transferが完了するまでclientからのアクセスを受け入れられません。(?)

rsync

State Snapshot Transferで最も速いバックエンドメソッドがrsyncです。Ohysical Snapshot Transferの全ての利点と欠点を持ちます。転送している間、donorノードはブロックされますが、データベースの設定やルートアクセスを必要とせず、その設定も簡単です。

テラバイトスケールのデータベースを使っているとき、rsyncはxtrabackupの1.5から2倍速いでしょう。

rsyncは差分を転送するアルゴリズムを持つので、rsync-wan変更という特徴も持ちます。しかし、これはよりI/Oに強く依存するだけでなく、ネットワークスループットボトルネックになりえます。

注意:もっともよくあることとして、このメソッドはdonorとjoiningノードでのrsyncバージョン間の非互換に出くわすことがあります

rsyncスクリプトはdonorとjoiningノードの両方で動きます。joinerのほうでは、rsyncをサーバモードで開始し、donorからの接続を待ちます。donorでは、rsyncをクライアントモードで開始し、データディレクトリをjoiningノードに送信します。

(訳注:donorのほうからjoinerに送信するんですね、意外。)

xtrabackup

State Snapshot Transferでもっともポピュラーなバックエンドメソッドがxtrabackupです。Physical State Snapshotの利点と欠点を持ちますが、仮想的にdonorノードではnon-blockingとなります。

xtrabackupはMyISAMテーブルをコピーする短時間だけdonorノードをblockします。これらのテーブルが小さければ、ブロック時間はとても短くて済みます。しかし、これはスピードがコストとなります。xtrabackupはrsyncよりかなり遅くなります。

xtrabackupは短時間でデータ全体をコピーしますが、donorの性能を著しく劣化させます。

注意:このメソッドでは設定に気をつけましょう。xtrabackupは設定ファイルのオプションにいくつか設定が必要ですが、donorサーバにはrootアクセスが必要となります。

おわりに

xtrabackupを使ったことがないんですが、mysqldump同様、普段はdumpに使うんですかね?ノードをブロックにしない点がrsyncより勝っていますが、遅いようですね。

www.submit.ne.jp

リストアがかなり速いようですね。

それでは!

Galera Cluster Documentを読む Configuration編(1) NODE PROVISIONING

はじめに

Galera Documentationを読み解くシリーズ、Technical Description編が無事に終わり、次はConfiguration編です。

Galera Cluster Documentation — Galera Cluster Documentation

今回はNODE PROVISIONING。クラスタにノードを追加することですね。

Node Provisioning — Galera Cluster Documentation

NODE PROVISIONING

新しい状態になったとき、あsるいはノードが故障して、クラスタのPrimary Componentが変更されるとき、新規ノードあるいは故障ノードは現状のクラスタと同期しなければなりません。これより、新規ノードのプロビジョニングと故障ノードのリカバリは本質的に同じプロセスで、クラスタのPrimary Componentに参加します。

Galeraは初期化するノードIDをgrastate.txtファイルから読み取り、wsrep_data_dirパラメタによってディレクトリを見つけます。ノードが正常にシャットダウンされたとき毎回、Galeraはこのファイルに保存します。

Total Order Isolationモードでは、ノードがクラッシュした際にデータベースの状態は不明とし、初期化されたノードの状態は未定義になります。

00000000-0000-0000-0000-000000000000:-1

注意:通常のトランザクションが行われているとき、GTIDのシーケンスナンバーだけが未定義になります。(具体的には-1になります) UUIDは確実に残ります。この場合、Incremental State Transferによってノードがリカバリできます。

(訳注:Total Order Isolationとはなんでしょうか。リンク先の飛んでみると、書き込みがあったマスタノードにTransactionを実行する前に、同期しているすべてのノードで書き込み可能か判断してからコミットする仕組みのようですね)

HOW NODES JOIN THE CLUSTER

ノードがクラスタに参加するとき、自身のUUIDをPrimary Componentと比較します。UUIDが一致しない場合、クラスタからのstate transferをリクエストします。

state transferのdonorを決定するときに2つのオプションが利用できます。

  • Automatic: ノードがクラスタに加入しようとしたとき、group communicationレイヤーで、Primary COmponentの中で利用可能なdonorを選択します。
  • Manual: ノードがクラスタに加入しようとした時、wsrep_sst_donorパラメタを使ってdonorを決定します。Primary Componentの中で決定できなければ、state transferは失敗し、加入ノードは加入を放棄します。wsrep_sst_donorパラメタは、wsrep_node_nameパラメタと同じdonorの名前を使用します。

注意:state transferは重たい捜査です。これは加入するノードだけでなく、donorにとってもです。実際donorノードはクライアントのリクエストを受けられないでしょう。 結果的に、(もし可能なら)、donorを手動で選択したほうがいいでしょう。ネットワークの近さや、負荷分散の設定を考慮して選択すべきでしょう。

(訳注:なるべく早く終わるためにネットワーク的に遠いノードではなく近いノードを、また、なるべく同じロードバランサで振り分けられてる、更新量が近いノードを選ぶとstate transferははやく終わるでしょう)

state transferの最中は、加入ノードは他のノードから受け取るwrite-setをキャッシュします。state transferが終わると、現在のPrimary Componentに追いつくために、slaveから受け取るwrite-setを適用していきます。state snapshotがUUIDを知らせるので、snapshotが含むwrite-setと、捨てるwrite-setを決定するのは容易です。

追いつくフェーズでは、flow controlがslaveキューを短くすることを保証します。(これは、追いつくノードでのwrite-setを行う頻度がすなわち複製する頻度になり、上限となるからです)

STATE TRANSFERS

クラスタに加入するノードが行うstate transferには2種類あります。

  • State Snapshot Transfer(SST): donorが加入ノードに対してノード全体のスナップショットを転送する方法
  • Incremental State Transfer(IST): donorは加入しようとするノードに足りないTransactionのみを転送する方法

自動的にdonorが選ばれる場合、Galera clusterのバージョン3.6以降ではクラスタはstate transfer methodがもっとも利用可能であるという観点で決定されます。

  • ISTが安全に実行できるノードが存在しない場合、クラスタはstate snapshot transferを用いる
  • ISTが安全に実行できるノードが存在しない場合、donorにとってリモートノードよりローカルノードを好む
  • ISTが安全に実行できるローカルノードが存在しない場合、クラスタはリモートノードを選択する
  • ISTが安全に実行できるノードがlocalとremoteいくつか存在する場合、クラスタはseqnoがもっとも高いノードを選択する

(訳注:SSTでdonorノードが選択される論理について説明されています。local/remoteはネットワーク的な速さで選択しているのかどうか気になりますね。シーケンスナンバーが最も高い=1番更新されているノードが選ばれるのはそれはそうかなって感じです)

おわりに

クラスタのUUIDはなにを契機に変わるんでしょうか、Primary Componentが変わったとき?加入/離脱によって変わったとき?いずれにせよUUIDとseqnoでクラスタは状態を把握していて、その情報(grastate.txt)をもとにクラスタ加入をどのように(IST/SST)行うか決定されます。

クラッシュした場合UUIDはリセットされるのははじめて知りました。実機で見てみよう。

Galera Clusterの同期の仕組みを公式ドキュメントから読み解く(8) WEIGHTED QUORUM

はじめに

Technical Description、8回分のようやく最後です!

Technical Description — Galera Cluster Documentation

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

WEIGHTED QUORUM

単一ノードが故障することに加えて、ネットワーク障害によってクラスタが分断されることもあるでしょう。お互いに接続されているノード集合であるコンポートネントは、一方で他のコンポーネントとは異なる状態です。このような状況では、データベースの歴史的な相違を避けるために、ある1つのコンポーネントのみが操作を継続すべきです。このコンポーネントはPrimary Componentと呼ばれます。

通常の操作では、Primary Componentはクラスタそのものです。クラスタ分断が起きた時、Galera Clusterは特別なquorumアルゴリズムを用いて1つのコンポーネントをPrimary Componentとして選択します。これによってクラスタ内で1つ以上のPrimary Componentが存在しないことを保証します。

注意:ノードが独立することに加えて、quorumはgarbdと呼ばれる切り離されたプロセスを用いて計算します。

WEIGHTED QUORUM

現在のクラスタ内のノード数は現在のクラスタサイズを定義します。すべての利用可能なクラスタノードのリストを定義するconfiguration設定は存在しません。クラスタにノードが加入するたびにクラスタサイズは増加します。ノードがクラスタから離脱したときはクラスタサイズは現象します。クラスタサイズはquorumを達成するために必要な投票数を決定します。

Galera Clusterはノードの応答がなく、クラスタからの分断が疑われる時はいつでもquorumの投票が行われます。evs.suspect_timeoutパラメタを用いてレスポンスのタイムアウト時間を調整することができます。デフォルトは5秒です。

クラスタがquorum投票を行う時、切断前から接続されたノードの大多数が残っていた場合、それらは残ります。ネットワーク分断が起きると、切断の両側でアクティブなノードが残ります。コンポーネントはPrimary Componentとして独立して操作され続けますが、quorumに達せないコンポはnon-primary状態になってPrimary Componentと接続しようとします。

Quorumは過半数を必要とします、これは2つのクラスタで自動的にフェイルオーバーできないことを意味します。あるノードの故障がノードを自動的にnon-primary状態にし続けるからです。

偶数ノードを持つクラスタはsplit-brainを引き起こすリスクが存在します。もしネットワーク分断が起きた場合、その分断がノードをちょうど半分に分断してしまうので、どちらのcomponentもnon-primary状態になってしまいます。

自動的なフェイルオーバーを行うためには、少なくとも3つのノードが必要です。同様の理由から、これはインフラストラクチャの他のレベルの拡張されることを注意してください

  • 単一のスイッチクラスタは最低3ノード必要
  • スイッチにまたがるクラスタは最低3スイッチ必要
  • ネットワークをまたがるクラスタは最低3ネットワーク必要
  • データセンタをまたがるクラスタは最低3データセンタ必要

Split-brain Condition

お互いに自律的に動くデータベースクラスタ障害は、split-brainと呼ばれます。これが起きると、2つのデータベースノードで同じテーブルの同じレコードを更新することにより、データが回復不能になります。quorumベースのシステムの場合のように、Galera ClusterはPrimary Componentを選択する際にquorumアルゴリズムが失敗したときにsplit-brainが発生します。

例えば、メインスイッチが故障中し、バックアップスイッチを持たないクラスタを使っているとき、(split-brainは)発生します。または、2つのノードのクラスタであるノードが故障したときも発生します。

設計によって、split-brainは回避できます。分断されたクラスタが2つとも同じサイズになった場合、(明示的に設定しない限り) どちらの分断されたcomponentもPrimary Componentになりません。

偶数のノードでのリスクを最小限にするためには、常にあるコンポーネントがprimaryになるように分割しましょう。

この分割例では、障害によって正確にノードが半数になることは難しいでしょう。

QUORUM CALCULATION

Galera Clusterは重み付けquorumをサポートしています。各ノードに0から255の範囲で重みをつけることができます。

これは、新しいコンポーネントのノードの重みの合計が前のPrimary Componentの半数から正常に残ったノードを除いた数を超えた場合にquorumを保持されることを意味します。

ノードの重みをpc.weightパラメタで設定できます。デフォルトでは1であり、ノード数と同じになります。

注意:pc.weightパラメタは動的に変更できます。 Galera Clusterでは新しい重みは重みを運ぶメッセージ配信によって適用されます。瞬間的には、新しい重みを知らせるメカニズムはありませんが、結果的にはメッセージが配布されると発生します。

注意:警告:重みの変更メッセージが配布されたとき、一時的にグループ分断が起きると、重みを配信したすべての分割されたコンポーネントはnon-primaryになります。通常メッセージを配信する分割(パーティション)は状態遷移が配達された時に重みが適用され、quorumが仕切られるでしょう。 言い換えると、分断が発生した瞬間、重み変更メッセージが送信されるなら、一時的にクラスタのすべてがNon-Primaryになり得るということです。この状況を復旧するには、再マージを待つか、どのパーティションがもっとも進んでるかを調べ、bootstrappingによって新しくPrimary Componentとして起動します。

WEIGHTED QUORUM EXAMPLES

重み付けquorumがどのように動くか理解するために、いくつかの例を紹介しましょう。

Weighted Quorum for Three Nodes

3ノードでquprumの重みを設定するなら、以下のパターンを用います。

node1: pc.weight = 2
node2: pc.weight = 1
node3: pc.weight = 0

このパターンでは、node2とnode3が同時に殺されると、node1がPrimary Componentになります。node1を殺すとnode2とnode3はnon-primary componentsになります。(訳注:これってつまりnode1が常に死なないようにしないとまずいよね?なんでこんなことするんだろう)

Weighted Quorum for a Simple Master-Slave Scenario

単純なmaster-slaveのシナリオなら以下のパターンを使います。

node1: pc.weight = 1
node2: pc.weight = 0

このパターンでは、もしmasterが死んでも、node2はnon-primary componentとなります。しかし、node2が死んでも、node1はPrimary Componentであり続けます。network接続がnode間で失われても、node1はPrimary Componentであり続け、node2はnon-primary componentとなります。

Weighted Quorum for a Master and Multiple Slaves Scenario

複数のslaveを持つmaster-slave構成の場合は、以下のようになります。

node1: pc.weight = 1
node2: pc.weight = 0
node3: pc.weight = 0
...
noden: pc.weight = 0

このパターンでは、ノード1が死んでも、すべてのノードはnon-primaryになります。他のノードが死んでも、Prymary Componentは(node1に)保持されます。ネットワークが分断しても、node1が常にPrimary Componentであり続けます。

Weighted Quorum for a Primary and Secondary Site Scenario

primaryとsecondaryのサイトを持つ場合のquorumの重み付けは以下になります。

Primary Site:
  node1: pc.weight = 2
  node2: pc.weight = 2

Secondary Site:
  node3: pc.weight = 1
  node4: pc.weight = 1

このパターンでは、いくつかのノードはprimaryサイトに配置され、他のいくつかはsecondaryサイトに配置される。secondaryサイトがダウンまたはサイト間でネットワークが切断されたとき、primaryサイトのノードはPrimary Componentであり続けます。加えてnode1とnode2どちらかクラッシュした場合、残りのノードはnon-primary componentになります。

おわりに

重み付け、結構有用だなーって思いました。

単純に今は重み付けなしで、マルチマスタでやっていて、HAProxyでsingleマスタにしたりしています。

やーっとTechnical Descriptionが全部終わりました。必要な語彙はだいたい揃ったと思います。

しかし実際の運用となると、Technical Descriptionからも多く参照されているConfigurationの章も読まないとなーと思ったので、まだ続きます。。。

Galera Clusterの同期の仕組みを公式ドキュメントから読み解く(7) NODE FAILURE AND RECOVERY

はじめに

全8回、第7弾です。

Technical Description — Galera Cluster Documentation

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

NODE FAILURE AND RECOVERY

クラスタに接続できなくなったとき、各ノードでの操作は失敗します。これは様々な原因によって発生します。例えば、ハードウェア障害やソフトウェアのクラッシュ、ネットワークの切断やstate transferの失敗など。いずれもクラスタ内ノード間の通信を阻害は、ノード障害という概念の背後に存在します。ノード障害を理解することはリカバリを計画するのに役立つでしょう。

DETECTING SINGLE NODE FAILURES

ノード障害が起きた時の唯一のサインは他のノードから見てそのノードとの接続が失われることでしょう。ノードはクラスタのPrimary Componentとのメンバシップが失われた時、そのノードは故障したとみなされます。これは、Primary Componentから見て、長時間そのノードが確認されないノードは故障とみなす考え方からくるものです。障害が発生したノード自身から見ると、障害が起きていなかったとしても、Primary Conponentとの接続が切断されます。

ノードを監視するサードパーティのツールは存在しますが、-例えばping, Heartbeat, Pacemekerなど- それらはノードの故障見積もりが大きくズレているかもしれません。これらのツールはGalera Clusterのグループコミュニケーションに参加せず、Primary Componentを認識しません。

Galera Clusterノード状態を監視したいなら、wsrep_local_stateステータスを定期関しするか、Notification Commandを使ってください。

クラスタはノードから最後に受信したネットワークパケットより接続性を決定します。クラスタの確認間隔はevx.inactive_check_periodパラメタで設定可能です。確認中はクラスタが最後にネットワークパケットを受信した時間よりevs.keepalive_periodパラメタの値が大きければ、heartbeat beconを送信します。クラスタがevs.suspect_timeoutパラメタの値よりも長い間隔ネットワークパケットを受信しない場合、ノードは(故障が)疑わしいと宣言されます。Primary Componentのすべてのメンバはその疑いを確認すると、そのノードはアクティブでないと判断します、すなわち、故障です。

evs.inactive_timeout間隔より長い時間メッセージを受け取らない場合、ノードは同意に関係なく故障したと宣言されます。故障ノードはメンバシップがすべてのメンバーととれるまでは操作できない状態になります。(訳注:Non-Primary状態でしょう) メンバが稼働ノードとの同意に達することができないなら、クラスタ操作を行うにはネットワークが不安定すぎます。

ノード間の関係は、以下のオプションの値を満たす時に達成します。

evs.keepalive_period <=   evs.inactive_check_period
evs.inactive_check_period   <=   evs.suspect_timeout
evs.suspect_timeout <=   evs.inactive_timeout
evs.inactive_timeout    <=   evs.consensus_timeout

注意:メッセージやheatbeat beconの送信に失敗する、鈍感なノード、-例えば、とても重たいスワッピングが発生しているような- もまたはっきりと故障ノードとなるでしょう。これは他のクラスタに残ってるノードの操作を妨げます。このような望ましくない振る舞いを発見した場合、timeoutパラメタを増やしましょう。

CLUSTER AVAILABILITY VS. PARTITION TOLERANCE

CAP定理のうち、Galera Clusterはデータの安全と一貫性に重きを置いています。これはクラスタ可用性と対断耐性との間のトレードオフを意味します。WANのような不安定なネットワークを使用している場合、evs.suspect_timeoutとevx.inactive_timeoutの値が小さいと、誤った故障検知をしてしまいますが、一方で大きい値をとると、実際に故障した場合の可用性が担保できない時間が長くなってしまいます。

本質的にこれが意味することは、evs.suspect_timeoutパラメタはノード故障検知に必要な最小限の時間を指定するべきです。この間隔の間、クラスタは一貫性の制約のために使用できなくなります。

RECOVERING FROM SINGLE NODE FAILURE

あるノードがクラスタで故障した場合、他のノードは通常通りの操作を続けます。ノードがオンラインに復帰したときは、クラスタに戻る前に、自動的に他のノードとの同期が行われます。

1つのノードが故障してもデータ消失はないのです。

State Transfer Failure

単一ノードの故障はstate snapshot transfer(SST)の失敗でも発生します。この失敗は受信ノードを使用不能にし、state transferの失敗を検知した時受信ノードは(受信を)破棄します。

ノードがmysqldumpを使っている場合は、再始動は手動でのテーブルのリストアになるでしょう。rsyncSSTに使っている場合は、問題は起きません。データベースサーバを特に操作する必要はありません。

おわりに

クラスタ内での単一ノード障害の考え方と、障害を検知するパラメタの説明がされていました。言いたいことは「1つのノードが壊れてもクラスタとしては問題ないよ」になると思いました。

Galera Clusterの同期の仕組みを公式ドキュメントから読み解く(6) FLOW CONTROL

はじめに

全8回、第6弾です。

Technical Description — Galera Cluster Documentation

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

FLOW CONTROL

Galera ClusterはFlow Conrtolと呼ばれるフィードバックするするメカニズムを用いてレプリケーションプロセスを管理している。Flow Controlは必要に応じてレプリケーションを止めたり、再開させたりする。また、トランザクションを適用する際に、他のノードより遅れてしまうことを防ぎます。

HOW FLOW CONTROL WORKS

Galera Clusterはクラスタ上のすべてのノードでトランザクションの順番を保証することによって同期レプリケーションを達成しています。つまり、クラスタ間でのトランザクションの複製は非同期で行われると言えます。

ノードはWrite-setを受信し、グローバルな順番に並べ替えます。ノードがクラスタから受信したが、まだコミットされていないトランザクションは受信したqueueの中で保たれます。

受信したqueueがあるサイズに到達した場合、ノードはFlow Controlを発動します。ノードはレプリケーションを止め、受信したキューに対して処理をはじめます。管理可能なサイズより受信したキューが減ってくれば、またレプリケーションを再開します。

UNDERSTANDING NODE STATES

Galera Clusterは、ノードの状態に依存する、いくつかのFlow Controlの実装方式があります。これによって、仮想的な同期ではなく、一時的な同期と一貫性が保証されます。(訳注:as opposed to logicalが不明。。。)

主に4種類のFlow Controlがあります。

No Flow Control

これはノードがOPENかPRIMARY状態のときに作用します。

ノードはこの状態のとき、クラスタの一部分であることを考慮しません。これらのノードはあらゆるwrite-setをcache、実行、複製することが許されません。

Write-set caching

これはノードがJOINERかDONOR状態のときに作用します。

ノードはこの状態ではwrite-setを適用することはできず、あとで行う必要があります。同期を停止する意外に、ノードがクラスタと同期するための合理的な方法はありません。

同期の割合を制限することは、write-set cacheが設定されたサイズを超えないようにすることで可能になります。write-set cacheは以下のパラメタで制御できます。

(訳注:replication rateが何をいっているのかいまいちつかみとれず。write-setはgcacheにたまった未適用のトランザクションでしょう。JOINERまたはDONERのときは差分適用をしているので、新しい変更は受け入れず、あとで行うのでしょう。)

Catching Up

これはノードがJOINED状態のときに作用します。

この状態のノードはwrite-setを適用します。Flow Controlはクラスタに追いつこうとします。特に、write-set chacheは大きくならないことを保証します。これより、クラスタ間のレプリケーション率はこの状態のノードがwrite-setを適用する率に制限されます。write-setの適用はトランザクションを処理するより数倍速いので、この状態のノードはクラスタのパフォーマンスにほとんど影響を与えません。

(訳注:write-setがどんな状態かはわからないけど、トランザクションを処理するより数倍速いのはなぜだろう?結局write-setの中身からトランザクション(UPDATEやINSERT)を実行すると思ってた)

ノードがJOINED状態のときにクラスタ性能に影響を及ぼす状況は、ノードのバッファプールが空のときです。

注意:並行して適用することで高速化できます

(訳注:バッファプールはgcacheのことなのか、innodbのbuffer poolではないよね?自分自身のバッファプール=gcacheが空で、DONORのgcacheを全部適用するときの話?だとするとSSTを使いそうだし、やっぱりよくわからないな)

Cluster Sync

これはSYNCED状態のときに作用します。

ノードがこの状態に入った時、Flow Controlはslaveのqueueを最小限にしようとします。以下のパラメタを使ってノードの扱いを制御できます。

  • gcs.fc_limit: フロー制御が動作するポイントを決定します
  • gcs.fc_factor フロー制御が解除されるポイントを決定します

(訳注:パラメタを見るとlimitは16、factorは0.5がデフォルト値になっていて、write-setの数のようですね。)

CHANGES IN THE NODE STATE

ノードのState-MachineはGalera Clusteの異なるレイヤー上で異なる状態変更を扱います。以下は最上位レイヤーで起こるノードの状態変更です。

  1. Primary Componentに接続します

  2. ノードがstate transferリクエストに成功すると、write-setのcacheがはじまります

  3. ノードはState Snapshot Transferを受信します。すべてのクラスタのデータを持ち、cacheされたwrite-setを適用します。

ノードはFlow Controlにslave queueを結果的に減少させます。

  1. nodeはクラスタに追いつきます。slave queueは空になり、Flow Controlは空の状態を保ちます。

ノードがwsrep_readyを1にセットします。nodeはトランザクションを受け入れられる状態です。

  1. ノードはstate transferリクエストを受信します。Flow ControlはDONORします。ノードはwrite-setを適用できません。

  2. ノードはstate transferをJOINERノードに送信します。

(訳注:ここでいうslave queueがgcache内の差分のようですね。)

読みやすさのために、特定の遷移は上の図からは省かれている。次の点に注意してください。

  • 接続性:クラスタの設定変更イベントはPRIMARYかOPENで送信されます。例えば、ネットワーク分断によって、Primary Componentとの接続が失われた時、SYNCEDのノードはOPENにもm取ります。
  • 遷移の消失:joiningノードがstate transferが必要ない場合、ノードの状態はPRIMARYから直接JOINEDに変化します

注意:一緒に読むこと:Flow Controlに関するより詳細な情報はGalera Flow Control in Percona XtraDB Clusterを参照

おわりに

Flow Control、ノードのクラスタ加入に際の状態に応じて、write-setの適用(=FLOW)を制御するもののようですね。

状態ごとにFlow Controlの様子を整理しましょう。

  • OPEN/PRIMARY: No Flow Control, write-setのレプリケーションも複製もキャッシュもしない
  • JOINER/DONER: Write-set Caching, write-setの適用は行わない。具体的にはノード間でメッセージ交換をし、DONORを選択、ISTかSSTかどちらを適用するか決定するまでがこのフェーズでないかと思います。
  • JOINED: Caching Up, クラスタへ無事加入し、他のノードに追いつくためにwrite-setを適用している状態です。
  • SYNCED: Cluster Sync, クラスタの一員になり、slave queueと呼ばれる、他ノードとのwirte-set差分はなく、galeraによって配られるwrite-setを順次適用されている状態です。

Galera Clusterの同期の仕組みを公式ドキュメントから読み解く(5) STATE TRANSFERS

はじめに

全8回、第5弾です。ようやく半分超えました。

Technical Description — Galera Cluster Documentation

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

STATE TRANSFERS

クラスタ内の各ノード間でデータを複製するプロセスで、クラスタに同期することでノードを追加することはプロビジョニングとして知られています。Galera Clusterのプロビジョンには2種類の方法があります。

  • State Snapshot Transfers(SST): ノード全体のスナップショットが転送される
  • Incremental State Transsfers(IST) 失われたデータのみが転送される

STATE SNAPSHOT TRANSFER (SST)

SSTでは、あるノードから別のノードにフルデータを転送することでプロビジョニングされます。新しいノードがクラスタに加入するとき、新しいノードはクラスタにすでに存在しているノードのデータを同期することによってSSTを初期化します。

SSTを行うために、Galera Clusterでは2つの異なる概念的なアプローチを選択できます。

  • Logical: この方法はmysqldumpを用います。受信サーバは完全に初期化されていて、転送の前にコネクションを受け入れることが必要です。(訳注:?どういうことだろう?)

これはブロッキングな方法です。Donorノードは転送中はREAD-ONLYになります。State Snapshot Transferは"FLUSH TABLES WITH READ LOCK"をDonorノード上で行われます。

(訳注:テーブルをロックするからREAD ONLYになるんですね。InnoDBだとsingle transactionを使えばいいんでしょうけど)

mysqldumpは遅いSSTの方法です。クラスタ内に負荷を生じてしまいます。

  • Physical: この方法はrsyncrsync_wan, xtrabackupなどの実際のデータを直接コピーする方法です。転送の後にサーバの受信が初期化されます。(訳注:ここもLogicalと対比して前と後が違うようだ。クラスタ加入とみなされるのが前か後かってことのような気がしてきた)

この方法はmysqldumpよりは早い一方で、確かな制限があります。サーバのスタートアップでのみそれらが使えます。受信サーバはDonorとほとんど同じコンフィギュレーションが求められます。(例えば、どちらのサーバも同じinnodb_file_per_tableの値を使う必要があります。) (訳注:サーバのスタートアップ、が何をさすかわからないけれど、実データをコピーするので、実データの構造を決定しうるinnodb_file_per_tableを同じ値にしなければならないのは納得)

これらのメソッドの中のいくつか、xtrabackupはdonor上でノン・ブロッキングで実施可能です。それらはスクリプト可能なSSTインターフェースを通じてサポートされます。

注意:一緒に確認すること:各メソッドのSSTでの使われ方はState Snapshot Transfersを参照。(訳注:rsyncはblockingですね!)

confugrationファイルでどのSSTメソッドを使うかセットすることができます。

wsrep_sst_method=rsync_wan

INCREMENTAL STATE TRANSFER (IST)

ISTでは、データ全体を送る代わりに、クラスタはjoinerノードで欠けているトランザクションのみを送ります。

プロビジョニング方法は以下の条件でのみ利用可能です。

  • joiner nodeのstate UUIDがグループ内のものと同じであること
  • すべての欠けているwrite-setがdonerのwrite-set cacheに存在すること

(訳注:このdonerのwrite-set cacheがgcacheですね)

これらの条件が合致したとき、ノードは欠けたトランザクションのみを転送し、クラスタにjoinerが追いつくまで順番に(トランザクションが)再実行されます。

例えば、クラスタのノードが以下の値を持っていたとします。このノードは状態として以下を読み取ります。

5a76ef62-30ec-11e1-0800-dba504cf2aab:197222

それまで、現在のクラスタはこの状態を持っていたとします。

5a76ef62-30ec-11e1-0800-dba504cf2aab:201913

クラスタのdonorノードはjoinerノードからstate transferリクエストを受信します。そしてwrite-set cacheのシーケンスナンバーである197223を確認します。このシーケンスナンバーでがwrite-set cacheに存在しない場合、State Snapshot Transfer(SST)の準備をはじめます。シーケンスナンバーがwrite-set cacheに存在する場合、donorノードは197223から201913までのコミットをjoinerに送信します。

ISTの利点は劇的にノードのクラスタ加入が速い点です。加えて、donorはノンブロッキングで実行できます。

注意:ISTで最も重要なパラメタはdonorノードのgcache.sizeです。これはwrite-setをキャッシュするシステムメモリ上に割りあてる領域を制御します。このスペースが多ければ、weite-setを保存できる数も多くなります。write-setをより多く格納できれば、ISTにおけるシーケンスナンバーの差分もより大きくなります。 一方で、write-set cacheがデータベースのサイズより大きい場合、ISTはstate snapshotを送るよりは効果は薄くなるでしょう。

Write-set Cache(GCache)

Galera Clusterはwrite-set Cache(あるいはGCache)と呼ばれる特別なキャッシュにwrite-setを保存します。GCacheはwrite-setのためのメモリ割り当てです。主要な目的はRAM上のwrite-setのfootprint(訳注:???)を最小にすることです。Galera Clusterはディスクへのオフロードwrite-setストレージを通じてこれを改善します。(訳注:GCacheはメモリといいつつただのリングバッファ・ファイルだった気がするけれど。。。)

GCacheは3つのタイプのストレージを使います。

  • 永続的なメモリストア:OSのデフォルトのメモリ領域をwrite-setに割り当てます。これは余分なRAMがシステムにある場合に有益です。物理的なメモリサイズが上限になります。デフォルトではdisableです。

  • リングバッファ・ファイル: cache初期化中に、ディスクへ事前に割りあてます。これはメインのwrite-set格納場所として向けられます。デフォルトでは128MBです。

  • オンデマンド・ページストア:write-setは必要に応じて、実行時にメモリにマップされたページファイルを割り当てます。デフォルトでは128MBですが、より大きなwrite-setを格納するときはより大きくすることができます。ページサイズの上限は空いてるディスクスペースが上限です。デフォルトではGalera Clusterは使っていないページファイルを削除しますが、ページファイルサイズの合計を上限にすることもできます。すべての格納場所がdisableのとき、少なくともディスク上の1つのページファイルが残ります。

注意:一緒に見ること:write-set cacheの制御をするパラメタについてさらなる情報はGalera Parametersのgcache.*の欄をみてください。

Galera Clusterは上記順番でwrite-setを格納しようとします。まずはじめに、メモリ上の領域を使おうとします。もしwrite-setのために十分な領域がなかった場合、次にring-bufferファイルを使います。でwrite-setの量が利用可能なディスクサイズを超えない限りは、ページへの格納は常に成功します。

デフォルトでは、write-set cacheはプロセスのworking directoryのファイルに割り当てられます。write-sec cacheを別の場所に置きたい場合は、gcache.dirパラメタを使いましょう。

注意:すべてのキャッシュファイルがメモリに割り当てられた場合、write-setキャッシュプロセスは実際よりも多く割り当てられているように見えます

注意:もしgcache.recoverパラメタがyesにセットされている場合、起動時にgcacheを修復しようとします。ノードが他のノードからのISTの受信を続けるためです。noにセットすると、gcacheはノード起動時無効とされ、ノードはSSTでの受信をするでしょう。

(訳注:むしろこのパラメタ、yesじゃないとISTが動かないように見えたけど、本当?)

おわりに

Galera Clusterでもっとも重要だと思われるISTとSSTの2つのメソッドが紹介されました。差分トランザクションのみで他ノードに加入する方式が早く、ドナーノードのブロックもしないため非常に良い選択肢です。

この差分トランザクションを格納するのがgcacheで、実際はメモリ->リングバッファファイル->ページファイルという順番で使われるんですね。リングバッファファイルのみかと思ってました。このサイズが大きければ大きいほどISTでの加入になる確率があがりますが、データベースサイズ以上にする意味はないようです。

Galera Clusterの同期の仕組みを公式ドキュメントから読み解く(4) ISOLATION LEVELS

はじめに

第4弾です。引き続き以下のページを訳していきます。

Technical Description — Galera Cluster Documentation

take-she12.hatenablog.com

take-she12.hatenablog.com

take-she12.hatenablog.com

とはいえ、、、今回の題である分離レベル(Isolation Level)はこちらの記事で触れました。が、復習の気持ちでやろう!

take-she12.hatenablog.com

ISOLATION LEVELS(分離レベル)

データベースシステムでは、トランザクションが同時発生する家庭でお互いに"分離"します。分離レベルによってトランザクションがお互いにどのように影響するのかが決まります。

INTRA-NODE VS. INTER-NODE ISOLATION IN GALERA CLUSTER

Galera Clusterでのクライアントセッションのために定められる分離レベルの話をする前に、シングルノードとクラスタトランザクションの間の分離を区別することは重要です。個々のクラスタノードはMySQLまたはInnoDBによってサポートされている分離レベルを拡張することができる。しかし、クラスタ内のノード間での分離レベルはレプリケーションプロトコルに影響される。それは別のノードで行われるトランザクションがが同一ノードで行われるトランザクションと独立に分離することができないからだろう。

クラスタ全体でサポートされる分離レベルは、以下3つ。

  • READ-UNCOMMITTED
  • READ-COMMITTED
  • REPEATABLE-READ

(訳注:SERIALIZABLEは出てこないんですね)

異なるノードでトランザクションが行われるので、分離は"最初にコミットしたものが勝者"というルールによって強化される。これはすなわちノード固有の"例外的な更新を破棄する"ことを除外する。(MySQL/InnoDBはこの振る舞いをする) これは異なる出力がトランザクションに依存するが、(同じノードで出されたトランザクションが成功し、同じトランザクションが別のノードで失敗すること)、同様のケースでスタンドアロンMySQL/InnoDBでは分離レベルが強くなる。

(訳注:何が言いたいかイマイチよくわからない。。。クラスタ内でトランザクションが強豪した場合、あるノードのトランザクションが勝ち、あるノードのトランザクションが負けることはそりゃそうかなって思う)

SERIALIZABLEの分離レベルは同じノードで行われたトランザクションの間でのみ有効であるため、結果的に(Galera Clusterでは)回避する必要がある。

ノード間でのデータ一貫性は常にクライアントによって選ばれる分離レベルに関係なく保証されます。しかし、食らう案とのロジックはコンフィグレーションでサポートされていない分離レベルに依存している場合破綻します。

UNDERSTANDING ISOLATION LEVELS

注意:警告:master-slaveモードでgalera clusterを使うとき、MySQLがサポートする4レベルを使うことができます。しかし、multi-masterモードの場合、REPEATABLE-READレベルのみ使うことができます。

READ-UNCOMMITTED

このトランザクションはコミットされていない別のトランザクションによってデータが変更され得るものです。

言い換えると、トランザクションは、コミットをせずに変更をロールバックした別のトランザクションによって結果的に存在しないデータを読み取ることもあるでしょう。これは"ダーティリード"として知られています。(訳注:一時的にコミット前のInnoDBのログあるいはバッファプールにのったデータのことをダーティリードと呼びます。これはまだ実際のデータファイルに書き込みされておらず、本来読み込まれるべきではないものです。) 事実上、READ-UNCOMMITTEDはまったく分離されていません。

READ-COMMITTED

このレベルではダーティリードができません。コミットされていない変更はトランザクションがコミットされるまで別のトランザクションにとって見えません。

しかし、この分離レベルではSELECTのqueryはコミットされたデータのスナップショットを用いるので、SELECTのqueryが実行される前にデータがコミットされます。結果的に、SELECTのqueryは、同じトランザクションが同時に実行されるとき、異なる結果を返すことになります。これはnon-repeatable readと呼ばれます。

(訳注:コミットされていない結果を読み取ることがないので、ダーティリードは発生しないが、別のトランザクションによってUPDATEなどの変更が行われた場合は同一トランザクション内で別の結果が返ってくることがありえるので、ファントムリードやNon-Repeatable Readが起こる)

REPEATABLE-READ

このレベルではnon-repeatable readsは起きません。SELECT queryがトランザクションの間で最初に行われたときにスナップショットが取られます。

スナップショットはSELECT queryのトランザクション全体を通じて維持されます。それは常に同じ返答が得られることとなります。このレベルは別のトランザクションによってデータが変更されたことを知ることができません。コミットされている、いないにかかわらず。これにより読み取りは繰り返し可能なままです。

(訳注:読み取りに関してはトランザクション実施時にSnapshotを取得するので、一貫して同じ値が得られる。しかし別のトランザクションによってINSERTが実施された場合はファントムリードが発生する。)

SERIALIZABLE

このレベルはトランザクションでアクセスするすべてのレコードをロックします。リソースはトランザクションが行われているテーブルにレコードを追加できないようにロックします。

SERIALIZABLEはファントムリードで知られているものを回避できる。ファントムリードが起きたとき、トランザクションでは2つの独立したqueryが実行され、2つめのquetが1つめものと比べて別の答えを返すときに発生する。

おわりに

今回は分離レベルを扱いました。まず分離レベルの違いによって起きる3つの不都合「ダーティリード」「ノンリピータブルリード」「ファントムリード」を抑えた上で、4つのレベルで何が起きるか起きないかを整理することが重要です。

個人的にはREAD-COMMITTEDとREPEATABLE-READの違いが結構分かりづらかったのですが、前者は別のトランザクションのUPDATEによって別の値がSELECTで読み取られてしまう(Non-Repeatable Read)に対して、後者はINSERTによって最初存在しなかったレコードが見えてくるファントムリードが起きる、という風に理解しました。

が、特にGalera Clusterに特化した話ではなく、RDBMS一般の議論なのかなと思いました。Galera Clusterは、特にマルチマスタの場合REPATABLE-READとなるようですね。