Cassandra には Consistency Level という概念があり、
クライアント側で ALL や QUORUM, ONE などが指定できる。
Replication Factor が 3 の場合、
・ ALL だと 3 ノードすべてが起動していないと READ/WRITE ができない。
・ QUORUM だと過半数の 2 ノードが起動していないと READ/WRITE ができない。
・ ONE だと 1 ノード起動していれば READ/WRITE できる。
特に難しいことは無くすんなりとクリアしたいところだが、
この例をみてほしい。1回目 と 2回目で QUERY の結果が
異なってしまっている。一体何が起きたのだろうか?
cqlsh> CONSISTENCY ONE -- CONSISTENCY LEVEL を ONE に設定
Consistency level set to ONE.
cqlsh> select * from "key1"."tab1"; -- 1回目の READ
id | name
-----+-------
id1 | name1
id2 | name2
cqlsh> CONSISTENCY QUORUM -- CONSISTENCY LEVEL を QUORUM に設定
Consistency level set to QUORUM.
cqlsh> select * from "key1"."tab1"; -- 2回目の READ
id | name
-----+------------------------------
id1 | name1
id2 | name2
id3 | inserted while node3 is down -- id 3 が増えた???
当然だが、1回目と 2回目の間に別のセッションで Insert が起きたわけではなく、
違いは クライアント側 で設定した Consistency Level のみ。
この状況に至るまでの Cassandra の状況はこのようなもの。
-- node1 (UP) - node2 (UP) - node3 (UP) /// 全ノード起動
cqlsh> INSERT INTO "key1"."tab1" (id,name) VALUES('id1', 'name1');
cqlsh> INSERT INTO "key1"."tab1" (id,name) VALUES('id2', 'name2');
-- node1 (UP) - node2 (UP) - node3 (DOWN) /// node3 停止
cqlsh> INSERT INTO "key1"."tab1" (id,name) VALUES('id3', 'inserted while node3 is down');
-- node1 (UP) - node2 (UP) - node3 (UP) /// node3 起動
cqlsh> SELECT * FROM "key1"."tab1";
"id3" は node3 が停止している間に node1 と node2 に INSERT されたデータ。
その後 node3 が起動してきて ONE で READ しているので node3 からの結果が返ってきている。
2 回目の READ で QUORUM が指定されたことで Cassandra は node3 以外のノード(node1 か node2)も参照し "id3" のデータが返ってきている。
つまり Consistency Level で指定しているのは データを返すのに
何ノード参照するかということになる。
ONE で READ -> node1(もしくは node2 か node3) から結果を返す
QUORUM で READ -> node1 と node2(もしくはnode3) から結果を返す。
ALL で READ -> node1 と node2 と node3 から結果を返す。
ONE の場合、必ず node3 から結果が返るわけではなく node1 や node2 から結果が返った場合には "id3" のデータも当然返ってくる。
READ するたびに "id3" が返ってきたり、返ってこなかったり不思議な状況。
つまり「QUORUM で WRITE して QUORUM で READ すればいいだけでしょ。」で
すんなりクリアしたいところだが、いつかは起きるこんな障害のときにも正しく対応したい。
・ 6ノード構成、Replication Factor 3 の環境で H/W 障害が起きた!
・ しかも 2ノード同時! (というか 1ノード目は昨日の夜間に落ちてた!)
・ QUORUM で READ がエラーとなり本番環境がダウンです。
このような状況だと「復旧優先。残り4ノードあるんだから負荷にも耐えれるし早く QUORUM から ONE にして。」と言いたくなる。
しかし ONE で READ したノードが最新のデータを受け取っていなかった可能性があるので(上記例の "id3" のこと)古いデータが許されない環境では ONE には設定することができない。
ONE はあくまでも READ ができるかどうかだけを許しているだけで、データが最新ではない可能性があるのでシステム停止状態は脱出できても、次に鳴る電話は恐怖の「なんかデータおかしいんですけど?」なってしまう可能性がある。
( Cassandra にそもそもデータの一貫性とか求めていません。。多少古くたってシステムが稼働していることのほうが大切って方は ガンガン ONE で突き進むのみです。)
- 補足1: READ QUORUM でも古いデータが返ってくるパターン: その1
-- node1 (UP) - node2 (DOWN) - node3 (DOWN) /// node2 と node3 がダウン
Consistency Level ONE で INSERT
-- node1 (UP) - node2 (UP) - node3 (UP) /// node2 と node3 を復旧
Consistency Level QUORUM で READ /// 過半数である node2 と node3 から READ した場合には結果は返ってこない。
- 補足2: READ QUORUM でも古いデータが返ってくるパターン: その2
-- node1 (UP) - node2 (UP) - node3 (DOWN) /// node3 がダウン
Consistency Level QUORUM で INSERT
-- node2 (UP) でディスク障害
-- node1 (UP) - node2 (UP) - node3 (UP) /// node3 を復旧
Consistency Level QUORUM で READ /// node2 と node3 から READ した場合には 結果は返ってこない
-
補足3: この状況を防ぐために Cassandra では Hinted Handoff や Repair があります。ただし万全では無いのでデータ不整合が起きる時は起きます。
-
補足4: よくある勘違い。
誤:「QUORUMって多数決とるんでしょ。」
正:「QUORUM は過半数から READ する」