View on GitHub

Today I Learned

Software Engineering Blog

Designing Data-Intensive Applications - Part 2. Distributed Data

第2部: 分散データ

データを分散したい理由

高負荷に対応するためのスケーリング方法

この部はスケールアウトでデータを分散した場合に生じる問題についてのお話

Chapter 5. Replicaiton

リーダーベースレプリケーション

writeはリーダー、readは任意のレプリカ

アクティブ/パッシブ、マスタースレーブレプリケーションとも呼ばれる

リーダーベースレプリケーションの動作

  1. レプリカの1つからリーダーを指定。リーダーはクライアントから受け取ったリクエストをローカルストレージに書き込む
  2. リーダーはデータをそれ以外のフォロワーに送り、受け取ったフォロワーはローカルストレージに書き込む
  3. クライアントはリーダーかフォロワーにreadリクエストを送る。writeリクエストはリーダーのみ

同期、非同期

レプリケーションは同期的に行われるか、非同期的に行われるか

同期的に行われる場合、フォロワーのデータは常に最新になり一貫性が保証されるが、フォロワーの障害時に書き込みができなくなる。

順同期型(1つのフォロワーを同期型にして、残りを非同期型にする)がおすすめ

ノード障害

フォロワーがダウンした場合。ダウン前からの全てのデータ変更をリーダーにキャッチアップする

リーダーのときは複雑。リーダーがダウンした場合、あるフォロワーをリーダーに昇格させて、クライアントの書き込み先を新しいリーダーに変更するプロセスが必要

タイムアウトによる障害の確認 -> コントローラによるリーダー選出 -> 新しいリーダーのための設定変更

このフェイルオーバーには いくつか問題もある

レプリケーションラグ

リーダーの書き込みとフォロワーへの反映のラグ

レプリケーションラグを解決するための一貫性モデル

read-after-write一貫性

ユーザは自分自身が投入したデータが必ず反映されていることを保証する

ユーザ自身が変更した情報はリーダーからreadする、自身の最新の書き込みタイムスタンプを記録しておく、などの方法

モノトニックな読み取り

ある時点でのデータをユーザが一度見たら、それ以前の時点のデータを見ることがない

ユーザは常に読み取りを同じレプリカから行う、などの方法

一貫性のあるプレフィックス読み取り

ユーザは適切な順序で保持したデータを見ることができる

順序を保持したい情報は同じパーティションに書き込む、などの方法

マルチリーダーレプリケーション

writeは複数あるリーダーのどれか、readは任意のレプリカにリクエスト

マスターマスター、アクティブアクティブレプリケーションとも呼ばれる

マルチデータセンターで利用するときなどに検討する

最大の問題は、書き込みで衝突しうること

衝突の回避方法として、LWW(last write wins, 最後に書いた方を優先する), 値のマージ、衝突回避のためのアプリケーションコードを用意する、などがある

リーダーレスレプリケーション

writeもreadも複数ノードに並列リクエスト

w + r < n である限り最新のデータを取得できる(=クオラム)

n: レプリカ、w: 書き込み成功ノード、r: 読み込み成功ノード

Chapter 6. Partitioning

パーティショニングはスケーラビリティを向上させるため

ノードはパーティションのリーダーであると同時に他のパーティションのフォロワーになる

一部のパーティションが他のパーティションに比べて多くのデータを持っている状態をスキュー、不均等な高負荷が集中しているパーティションはホットスポットと呼ばれる

ホットスポットを防ぐ方法は、ノードに対してレコードをランダムに割り当てる

キーの範囲によるパーティショニング

連続的なキーの範囲を各パーティションに割り当てる。日付の範囲など

単純なタイムスタンプだと特定のパーティションに集中して、他のパーティションは遊んでいる状態になってしまう。これを回避するためには日付以外のキーを用いる

ハッシュパーティショニング

多くの分散データストアはキーに対するパーティションを決定する際に、ハッシュ関数を使う

ハッシュを使っても完全にホットスポットが発生しなくなるわけではない。例えばTwitterのIDのハッシュがキーになっている場合。こういった場合は、キーにランダムな数値を加えるなど、アプリケーション側での対応も必要。

ローカルインデックス

ドキュメントによってパーティショニングされたインデックス

セカンダリインデックスをプライマリキーおよび値と同じパーティションに保存する

書き込み時に更新するパーティションが1つで済むが、読み込みには全てのパーティションへクエリを送って結果を結合しなければならない(スキャッタ、ギャザー)

グローバルインデックス

語によってパーティショニングされたインデックス

セカンダリインデックスはインデックスが張られた値を使って独立にパーティショニングされる

書き込み時に複数のパーティションを更新する必要がある。読み取り時は単一のパーティションだけで処理できる

リクエストのルーティング

ZooKeeperはルーティングのためのメタデータとなるノードとパーティションのマッピングを管理している

Chapter 7. Transactions

Chapter 8. The Trouble with Distributed Systems

Chapter 9. Consistency and Consensus