Cassandraの構造
提供: LunaBiblos
Software > DataBase > KeyValueストア > Cassandraの構造
目次 |
概要
Cassandraの構造を解説する。既にHBaseの構造の解説がある為、KVSとしてHBaseとの差異を元に説明を行う。
Cassandraとは
Cassandraは米Facebook社が開発したDatabaseの一種であり、HBaseがGoogle BigTableの完全なるクローンを目指しているのに対してCassandraは下記の様な2つの要素を組み合わせた「第二世代KeyValue型Store」のDatabaseである
- Data Model にGoogle BigTableのColumn指向型データ構造
- Data Partitioning にAmazon Dynamoの分散管理方式
実ファイル、Tableを構成する要素、Tableの構造はHBase/BigTableに近い形だが、Cluster構成はDynamoに似た分散ハッシュテーブルに近い構造をとる。この当たりの構造については後述する。
CassandraもやはりCluster環境で動作させる事が前提として作られたKVSであり、Nodeを追加する事でリニアに読込/書込、保存可能Data容量を追加する事が出来る。特に書込処理の方がScaleしやすい。 尚、現状のPCではCPUが最初にBottleneckに陥る可能性が高いという結果が出ている。またCassandraはData操作時のConsistencyの強度を指定して処理が行える事も特徴の一つである。
CAP原則から見たCassandra
CAP原則に基づいて考えると以下の通りに分類される
| DB名 | Consistency | Availability | Partition Tolerance |
|---|---|---|---|
| RDB | ○ | ○ | × |
| HBase | ○ | × | ○ |
| Cassandra | × | ○ | ○ |
Cassandraは可用性(Availability)と分断耐性(Partition Tolerance)に重点を置いたDatabaseである。一方でHBaseは一貫性/整合性(Consistency)と分断耐性(Partition Tolerance)に重点を置いたDatabaseである。
なお注意したいのは、分断耐性の意味である。これは「分散がしやすいか」ではない。
詳細はKVSとRDBの議論 - CAP原則を参照
HBase

HBaseはOLAP寄りのDBである。HBaseはDataのReplicationをHBase自身ではなく背後にあるHadoop ClusterによるHDFS内で行っている。
この為HBase上にDataは常に単一でしか存在しない為、Dataの一貫性/整合性は保たれている。
障害発生時
現状のHBaseでのMasterNodeの欠損はCluster全体の停止を意味する為、MasterNodeは別途冗長化しておく必要がある。現在のHBaseではMasterNodeのHotStandbyに対応しているのでこれを用いる事が出来る。
SlaveNodeが欠損した場合、欠損したNodeが提供していたDataは一時的に閲覧が不可能になってしまうが、MasterNodeが欠損を検知し次第生存する別SlaveNodeに対し、欠落したNodeが保持していたを割り当てる事で障害から復旧する。
障害時のClient側の動作は次の様になる。
ClientがData取得を試みたSlaveNodeが欠損していた場合、Clientはその旨をMasterNodeに通達する。通達をもらったMasterNodeの動作は上述の通りであり、Dataの復旧が完了するとMasterNodeは新たにDataを担当したSlaveNodeのAddressをClientに通知し、Clientはその情報を元にDataの再取得を試みる。
この為HBaseのClientを利用したApplicationからは、Nodeの欠損は意識されない。
Cassandra

CassandraはOLTP寄りのDatabaseである。CassandraはDataのReplicationをDBのData自体で行って居る。
Clusterに参加する全Nodeが同一の機能を有し管理Nodeを持たない為に単一障害点がない。その為Nodeが落ちたとしてもDB上のData欠損は発生しない。なおCassandraはどのNodeに対しても読み書きが可能である。
その為Cassandraは例えClusterがどれ程破綻しても、正常に稼働しているNodeが一つ以上あれば書込が行われ、応答が返されるという「Write never fail」を実現している。Clusterが正常に復旧した後はGossip Protocolを用いて全体の回復を行う事になる。
またClusterに新たなNodeを加える場合は、設定XML中のSEEDという設定項目に追加するNodeのIPと既存Clusterに参加済みのNodeのIPを記述すれば良い。なおこのSEEDの書き方を工夫する事で同一Cluster内に複数個のRingを作り出す事も出来る。
障害発生時
そもそもCassandraのClientはCassandra Cluster接続時にCluster全体のNode一覧表を取得している。このNode一覧情報を元に接続を試みる。
しかしこのNode一覧をどの様に使って接続を行うか、つまりApplication側の実装次第で障害発生時の動作が変わってくる。一般的なThriftを使った方式では自動で接続先に切り替えを行ってはくれないので、自分達でこの部分を実装する必要がある。
尚Network分断されてしまったCassandra Clusterはそれぞれ分断された状態の島でもそれぞれ機能し続ける事は出来る。Network分断復旧後、分断されて居たNode群はDataのMergeを開始し復旧を始める。
既存Nodeの除去と新規Nodeの追加
RingからNodeを削除する際に利用される「nodetool decommission」は、先ず先にRing上からそのNodeを取り除き、その後にそのNode上でのCassandraを停止する。(StorageService#decommission())
Nodeが欠落した後に新規にNodeを追加する際、欠落Nodeが保持していたTokenを指定する事での復旧も出来るが、運用の手間と負荷を考慮した場合、新たに新規Nodeを追加した方が良い。
新規にNodeが追加された際、Data更新頻度が高い場合、新規NodeへのData転送が追いつかない場合がある。HDFSの場合は高速にDataCopyが行われるが、Cassandraはゆっくり再配置される。
Cassandraは明示的な動的Rebalanceが苦手な為既存Dataの復元には時間が掛かる。Dataを全く保持しない新規Node追加による運用を行う場合が多い為、再配置にかかる負荷を予め計算しておく必要がある
図に依る比較
Cassandraの機能構造
保持Nodeの決定
- RandomPartitioner
- キーのMD5ハッシュを元にトークン生成。
- OrderingPreservedPartitioner
- CollatingOrderPreservingPartitioner
Replication先の決定
書き込まれたDataのReplicaを作る事でCassandraはNodeの欠損に備えている。しかしCassandraの初期設定ではReplicationの数が1、つまりCopyを全く作らない設定になっているのでClusterを構築する際は必ずここの値を変更する事。なおGoogleやHBaseに置いてReplicationの数は3が推奨値とされて居る。
更にCassandraではReplicaをどのNodeに割り当てるかというルールも設定する事が出来る。 DataのReplicationはKeyの範囲毎に自動で行われる。またReplicationの方法を下記の幾つかの方法から選択出来る
- RackUnaware・・・Nodeのring内で隣接するNodeを利用してReplicationを行う
- RackAware・・・IPを元に別Rackであると判定されたNodeにReplicationを行う方式
- DataCenterShared・・・IPを元に別DatacCenterであると判定されたNodeにReplicationを行う方式
Gossip Protocol
- Efficient Reconciliation and Flow Control
- 相当JavaClass:org.apache.cassandra.service.StorageService
Gossip ProtocolはCluster内Node同士が相互に通信する為の手法で、Amazon Dynamoでも利用されており当初から急激に増加する負荷に耐えうる様に設計されている。
CassandraではGossipを利用して常にNodeが相互に情報のやり取りを行っており、Node間のKey再配置、Node間の障害通知の情報交換を始め、新たにClusterに参加してきたNodeもこのGossipを利用して既存Cluster中のNodeから認識される事になる。
尚Gossipを利用してやり取りされる情報は全てKeyValue型のDataで管理されて居る。
Gossip経由でやり取りされるCassandra上のDataは、各Dataが持つTimestampの値を隣接Node間で比較し新しい情報を伝播させていく。これを繰り返す事で最終的には全Nodeのデータが更新される仕組みである。
このProtocolの利点は、発生するTrafficを押さえCluster内のThroughputを圧迫しない点にある。が同時にData更新の即効性がない為、結果整合性に頼る事となる。
Cassandraは上述の通り一貫性を一部犠牲にしているが、それを補う機能としてData操作時にTransctionの強度をConsistencyLevelという形で選択する事が出来る。ConsistencyLevelの強度は簡単に言えば「行った処理が何個のReplicaに伝播する迄、処理の受付を待たせるか」という視点である。最大強度にした場合、全Replicaが更新されるまで次の処理は待たせられる事になる。
ConsistencyLevel
CassandraがData操作時に扱えるConsistencyLevelは以下の通りである。下記で説明されている状態になるまで、処理が待つ事になる。 なお表中のNはReplication数を意味する。
| 強度名 | 処理 | 強度 | 説明 |
|---|---|---|---|
| ZERO | 書込 | 0 | Cluster参加中の何れかのNodeが要求を受け取った時点で終了 CommitLogの生成確認も行わない |
| ANY | 書込 | 0< x < 1 | Cluster参加中の何れかのNodeが要求を受け取りCommitLogとHHを行い、それらを確認し終了 |
| ONE | 書込/読込 | 1 | Data保持担当Nodeの内、一個つから要求処理の応答があった時点で終了 |
| QUARUM | 書込/読込 | N/2+1 | Data保持担当Nodeの内、最小過半数個のNodeから、要求処理の応答があった時点で終了 |
| ALL | 書込/読込 | N | Data保持担当Node全てから、要求処理の応答があった時点で終了 |
一貫性/整合性の確保は以下の式で保証される。
W:書込時Level R:読込時Level N:Replication数 W + R > N 書込時Level + 読込時Level > Replication数
Hinted Handoff
書込要求がなされた際、そのDataの保持担当Nodeが落ちていた場合に実行される処理。残る生存Nodeの何れかに、本来の保持担当Nodeの情報と書込内容を格納しておく。格納された情報はGossip protocolを経由して伝播され、落ちていた本来のData担当Nodeが復旧した際に、本来の処理が施される。これが失敗した場合ReadRepairが実行される
Hinted HandoffよりClusterに再度復帰にしたNodeは、差分Dataの復旧のみを行う事で正常なDataを提供出来る様になる。
整合性強度ANYを利用した場合は、書込要求を受け取ったNodeが最寄りのNodeで要求を転送し、転送先NodeでこのHinted Handoffが成功すれば、完了と見なし応答が返される。このANYの高速な応答実現がHinted Handoffの主目的である。
なお整合性強度ONE以上で書込処理を行った際にData保持担当Nodeが落ちていた場合でも、Hinted Handoffは実行されるが書込成功として数えられる事はない。つまりHinted Handoffは強度ANYの時は成功と見なされるが、強度ONE以上では成功と見なされる事はない。
課題点
RDBMSやHBaseではALTER文を使う事でDBの稼働中にTable構造の変更を行う事が出来るが、Cassandra 0.61の時点ではまだTable構造の動的変更に対応していない。次Verである0.70での対応が予定されている。
0.61以下のCassandraでTable構造の変更を行う場合はCassandraがもつ機能を使い下記の様に行う必要がある。
既存DataのJSON形式でのExport、XML書き換えによるColumn変更、JSONを編集し書き換え、JSON形式DataのImport
また上述の通りCassandraは一貫性/整合性を犠牲にしている点も考慮する事。
Data操作のAlgorithm
書込
共通処理
Clusterに属する何れかのNodeにClientからの要求が到達すると次の処理を行う。
- 書き込まれる値のKeyをMD5でHash値化した値
- Cassandra Clusterが持つHashTable
- 設定されたReplicationFactor数(以後nとする)
上記の情報に基づきn個のData保持担当Nodeを決定する
ConsistencyLevel.ZERO
- Data保持担当Node、全てに要求を転送し、それらの応答を待たずに終了
ConsistencyLevel.ANY
自己の最寄のNodeに対してHinted HandOffの依頼を投げる。最寄のNodeが落ちていた場合は自分自身がHintedHandOffを実行する
最寄りNode、或いは自分自身が行ったHinted HandOffの完了を確認し、終了
ConsistencyLevel.ONE以上
Data保持担当Node全てに書込依頼を投げる。
- ONEであれば、一個のNodeから書込完了報告があった時点で終了
- QUORAMの場合、最小過半数(n/2+1)個のNodeから完了報告があった時点で終了
- ALLの場合、全てのNodeからの完了報告があった時点で終了
CommitLog
CassandraはOSに対してのWrite命令発効で、CommitLogの作成と見なす。
Fsync等の処理が行われない限り、Disk上には保存されていない。
書込時のError
| 全Node数 | 3 (A,B,C) |
| Replication数 | 2 |
- 或るDataを書き込むが、そのDataはAとBに保存される
- Aは現在落ちている
上記条件下でDataの書込を行った場合、書込時のLevelに依って挙動が変化する
| ZERO | 成功 |
| ANY | 成功 |
| ONE | 成功 |
| QUORUM | UnavailableException |
| ALL | UnavailableException |
強度QUORUMでErrorに成った原因は、本来であれば最小過半数である2つのNodeから書込完了報告を受けなければならないのにそれが行えなかったからである。強度ALLで失敗している理由も同じで、本来書かれるべき2つのDataの作成に失敗したからである。
尚強度ANYで書込をおこなった場合、書込命令を投げたNodeから、最寄りのNode(今回で言えばC)に下記の二つが作成される
- CommitLog
- \data\system\HintedColumnFamily が作られる
読込
ConsistencyLevel.ONE
- Cluster中の何れかのNodeが、Clientから要求を受け付ける
- 読込対象のDataを持つNodeをHashTableから算出し、最も近くに居るNodeを算出
- 保持NodeにDataを要求する
- Dataを取得したら、そのDataをClientへ返す
- 設定XMLにおいて、「DoConsistencyChecksBoolean」の値がtrueとなっている場合、ReadRepairが実行される
ConsistencyLevel.QUORUM以上
- Cluster中の何れかのNodeが、Clientから要求を受け付ける
- 読込対象のDataを持つNodeをHashTableから算出し、最も近くに居るNodeを算出
- 読込対象のDataを持つNodeをHashTableから算出し、ReplicationされたDataを保持しているNodeを算出
- 一番近くにいるNodeにDataを要求し、それ以外のNodeにも要約を送る様に伝達する
- 指定されたConsistencyLevelの数と同じ数の応答がある迄待機する
- 要約が一致していれば、最寄りのNodeから取得したDataをClient側へと返す
- 要約が不一致の場合、実Dataを読み込みし直し、ReadRepairを行い、修正した後のDataを返す
- ReadRepairを実行しても値の不一致が見られる場合は「DigestMismatchException」を返す
- 強度QUORAM以上を実行した場合、ReadRepair処理が行われから応答がある為時間が掛かる。特にValueとして容量の大きな値を保持しているとその分遅く成ってしまう。
Read Repair
- 相当JavaClass:org.apache.cassandra.streaming
Cassandra Cluster全体で整合性一貫性を確保し強化する為の処理。読込処理実行の為に集められたDataの値(TimestampとChecksumの値)が一致しない場合、古いDataを保有するNodeに対してData更新要求を発行する(Gossip protocolを利用したQueue)。
不一致と成ったDataを全てのData保持担当Nodeから取得し、DataのMergeを実施、更にこの処理を行ったDataを各Data保持担当Nodeに送る事で最新のDataを保持させます。
この処理は設定ファイルを変更する事で、実施を止める事が出来る。実施を止める事で読込の速度向上を図れるが、同時に書き込み時に充分な整合性一貫性を保持して居なければ正しい値を読み取る事が出来なくなってしまう。
削除
削除処理の時間を短縮する為にCassandraでは削除命令が発せられたDataに対して、実際の削除処理を行う代わりに、「Tombstone(墓石)」Flagを立てます。このTombstoneFlagは他のReplicaDataにも伝播します。
TombstoneFlagを持つDataはMajorCompaction実行時に削除されます。
実Data保存
Dataの保存に関してはHBaseと同様に、最初にCommitLogへと書込内容を記述し、続いてMemory上のMemtableに保存される。
なおHBaseとCassandraの差異として、HBaseではMemory上のFileSizeに依ってHDD上のFileへの書き出しが行われたが、Cassandraでは一定時間使われていないDataから、HDD上のFileへと書き出される。
Memtableから書き出されたDataはSSTableと呼ばれるFileに格納されてHDD上に保存される。SSTableには以下の3種類がある。
| Data File | KeyでSortされたKeyとValueが格納されているFile |
| Index File | KeyとOffset値が格納されているFile |
| Filter File | BloomFilterが格納されて居るFile |
