かめあるき

プログラミング駄文。

システムデザインを勉強した【2周目/スケーラビリティ編】

前回

kame3xkame3.hatenablog.jp

前回よりもう少し細かく勉強しようと思ったら長くなりそうだったので、2周目はトピックで記事を分けました。

記事の流れはほぼsystem-design-primerの通りです。

まだ勉強中なので、ヤバそうな嘘があれば教えてください。

スケーラブルなシステムとは

スケーラブルなシステムをつくる上で、よく使われる設計原理や概念をまとめる。

ちなみにsystem-design-primerではここでCS75の動画を見ろと言っているが、10分くらい視聴して英語がわからないことがわかったので、飛ばしてググったり別の教材で補ったりした。

垂直スケーリングと水平スケーリング

  • 垂直スケーリング(Vertical Scaling)== スケールアップ(scale-up)
  • 水平スケーリング(Horizontal Scaling)== スケールアウト(scale-out)
  • 例えば手持ちのPCではパワー!が物足りないと思ったときで例えると、
    • よりスペックの高いPCに買い替えるのが垂直スケーリング(またはスケールアップ)。
    • もう一台増やして2台使うのが水平スケーリング(またはスケールアウト)。
  • 仕事が終わらなすぎる会社で例えると、
    • そもそもこの仕事をできる人が居ない!という話なら各社員の能力がもっと高ければいいとなる(垂直スケーリング・スケールアップ)。
    • どう考えても仕事が多すぎて人手が足りてない!という話なら、もっとたくさん人を雇おうとなる(水平スケーリング・スケールアウト)。
  • 実際の垂直スケーリング・スケールアップはCPUやメモリを追加で積み上げていくイメージなので、その雰囲気で名前を覚える。
  • 水平スケーリングは台数を増やして横並べにしていくイメージ(実際のサーバー室はラック使って縦方向にも並べている気がするがきにしない!)。
  • スケールアウトは、スケールアップのPC内部にいろいろ追加する方法に対して、PCの外で台数を増やしていって拡張するイメージ。
  • (名前とイメージが一般に想定されているものと一致しているかは確認できていない。)
  • 個人のPCならもっとスペックの高いPCを買って垂直スケーリングしよう!となるが、大規模システムでは、膨大なトラフィックを捌くために、サーバーを増やす水平スケーリングが一般的(もちろん状況に応じて適切な対策を講じる必要がある)。

垂直スケーリングのメリット/デメリット

メリット

  • サーバーの台数を増やさなくていいので、運用などの管理の手間が増えない。今まで通りの管理方法を変えなくて良い。
  • サーバーの台数を増やすより、サーバーのスペックを上げるほうが作業量が少ない(単純に作業対象のサーバーの台数が少ない)。

    デメリット

  • いいPCは費用が高い。
  • スペックの高さにも限界がある。「現代の最新技術をもってしても終わらない処理」とかがあると詰む。
  • サーバーを一度シャットダウンしたいとき、サーバーが1台しかないとその間システムにアクセスできない。

水平スケーリングのメリット/デメリット

メリット

  • 長期的に運用するならコスパが良い。
  • 役割ごとにサーバーを分けられる(アプリケーションとDBなど)ので、それぞれに適したスペックのサーバーを使ったり、対策を講じたりしやすい。
  • 複数のサーバーを同時に動かしていれば、一部のサーバーがダウンしてもシステムが動き続ける設計も可能。

デメリット

  • 初期投資の費用が高い。
  • 管理が複雑になる。
  • サーバーの台数を増やす作業量も多い。複数台で並列処理・分散処理をするためにシステム側のコード修正も必要になる。

クローン

  • スケーラブルなシステムは、複数のアプリケーションサーバーで構成されるグループ/クラスターに、ロードバランサーを介して通信する構成になっている。
  • ロードバランサーがリクエストを受け取り、負荷(ユーザーからのリクエスト)を適切なサーバーに分散させる。
  • レスポンスもロードバランサーを介してクライアントに返される。
  • クライアントのリクエストはどのサーバーで処理されても同じレスポンスを返されるし、リクエストがどのサーバーに送られるのか意識する必要がない。
  • 同じ処理を行うサーバーが複数あると、コードが変更されたときに全サーバーを更新する必要がある。
    • 自動デプロイツール(Capistranoなど)
    • イメージファイル(AMI - Amazon Machine Imageなど)

データベース

非正規化

  • 「正規化」の逆。
  • 正規化はデータベースのテーブルを分割してデータの重複を無くすこと。
  • 非正規化はデータベースのI/O(書き込み・読み込み)の速度を上げるために、あえてデータの重複を許すこと。
  • JOIN句を使うと速度が落ちるので、正規化してテーブルを増やさないようにする。

NoSQL

  • SQLを使うデータベースであるRDBMS(リレーショナルデータベース)「「「じゃないほうの」」」データベース全般のこと。
  • 種類は色々ある。
  • 概してRDBMSより構造がシンプルで、I/Oが早くなることが多い。

ほかのいろんな方法

  • フェデレーション
  • シャーディング
  • SQLチューニング

キャッシュ

  • ここでは特に、「インメモリキャッシュ」のことを指す。
  • JSONPythonで言うところの辞書のように、Key-Valueの形式でデータをメモリに保持する。
  • メモリにあるデータは高速に取得できる。
  • アプリケーションとデータストレージの間に置いておいて、アプリケーションは欲しいデータがキャッシュにあればキャッシュから、なければデータストレージから取得することで全体的なアクセス速度を向上させることができる。
  • データをキャッシュする方法
    • データベースクエリのキャッシュ
      • Key → クエリをハッシュ化したもの
      • Value → クエリ実行結果のデータセット
      • デメリット:有効期限
        • あまり使われないような複雑なクエリをキャッシュする場合、いつ削除するか?
        • 複数のキャッシュに存在するとあるデータが変更された場合、対象のすべてのキャッシュを削除する必要がある。
    • オブジェクトのキャッシュ
      • 著者推奨。
      • データをオブジェクトとして扱う。
      • オブジェクトが生成されたらそのままキャッシュに保存する。
      • そのデータが変更されたらそのまま削除できる。
      • 非同期処理が可能。
  • どんなデータをキャッシュするか?
    • ユーザーセッション
    • ブログ記事
    • アクティビティストリーム
      • SNS上などでのユーザーの活動をリスト化して時系列で保持しているもの)
    • ユーザーとフレンドの関係性
  • 代表的なキャッシュ

非同期処理

  • リクエストを受けた直後になるはやで、以外のタイミングで処理してレスポンスすること。
  • 非同期の方法
    • その1
      • パン屋さんで例えると、「夜にパンを焼いて、朝に売る」形式。
      • 予め処理をしておいて、その結果を利用することで迅速なレスポンスを実現する。
      • 動的コンテンツを静的コンテンツに変換する場合などに適用される。
    • その2
      • パン屋さんで例えると、「注文を受けて、翌日以降に受け取ってもらう」形式。
      • リクエストをキューに送り、行列のように後ろに並ぶ。順番が来たら処理を行い、レスポンスを返す。
  • 非同期処理に関係する代表的なキュー
    • RabbitMQ

トレードオフ

パフォーマンス vs スケーラビリティ

  • パフォーマンス == 性能
  • スケーラビリティ == 拡張性
  • リソースが追加するごとにパフォーマンスが上がるシステムはスケーラブルである
  • (↑これはトレードオフなのか??)

レイテンシー vs スループット

可用性 vs 一貫性

CAP理論

  • 分散システムにおいては、下記の3つのうち2つまでしか保証できない。
  • (2つは極論すぎ!そうともかぎらないよ!という記事を見つけた、未読。https://www.publickey1.jp/blog/13/capcap32.html
  • 一部のサーバーの障害のせいでシステム全体が使えないというのは困るので、分断耐性は必ず保証する
  • 創るシステムの要件に従って、CPかAPの適切な方を選ぶ。
  • (分散システムに限らなければCAも存在するし採用される。例:RDBMS

Consistency - 一貫性

  • 必ず最新の情報か、エラーを返す。
  • データの整合性を保持できること。

Availability - 可用性

  • 必ずレスポンスを返すが、最新だとは限らない。
  • いつでも使えること。

Partition Tolerance - 分断耐性

  • ネットワーク障害などで一部のサーバーとの通信が途絶えてもシステムが機能する。

CP

  • ○一貫性と分断耐性
  • ✕可用性
  • システムはいつでも機能していてレスポンスは常に正しい情報だが、必ずレスポンスがもらえるとは限らない(エラーになることがある)。
  • 銀行口座でAさんからBさんに1000円振り込むとき、
    • ①Aさんの口座から1000円減らして、②Bさんの口座に1000円足す。
    • ①か②のどちらか一方が失敗すると、銀行全体のお金の帳尻が合わなくなるので、①と②の両方をなかったコトにする == エラーになる。

AP

  • ○可用性と分断耐性
  • ✕一貫性
  • システムはいつでも機能していて必ずレスポンスが返ってくるが、レスポンスの情報は最新のものだとは限らない。
  • 情報がちょっと古くても、いつでも使えるようになっていること。

一貫性パターン

  • 分散システムなので、一部のシステムがダウンしてもシステムが機能する(= 分断耐性)ように、データの複製を用意し、同期しておく必要がある。
  • どうやってメインのデータの変更を複製に反映するか。
  • データの整合性と同期の速度がトレードオフになる。

弱い一貫性

  • 整合性は低いが同期は早い。
  • 更新した後、最新の情報が取得できたりできなかったりする。
  • 正確さよりもリアルタイム性が重視される場合に適している。
  • ボイスチャットなどのように、音声が途切れることが合っても、なるべく遅延が少ないほうがよい場合がある。

結果整合性

  • 同期にそこそこ時間がかかるが整合性は高い。
  • 更新した後、すこし遅延があるものの必ず変更が読み取れるようになる。
  • メールなどのように、間違いなく相手が読み取れる必要があって、多くのリクエストを捌く必要があるもの(ボイスチャットと違って、メールが届かないこともあるというのは困る)。

強い一貫性

  • 同期の速度より完璧な整合性。
  • 更新した後、どのデータベースからも必ず同じデータが読み取れる。
  • 銀行口座のように、AさんからもBさんからも振込が行われたことが確認できないといけない場合。
  • 一部のシステムがダウンしたために古いデータしか参照できず、先月の給与振込がなかったことになったら困る。

可用性パターン

  • 可用性を保証する方法は以下の2つ
    • フェイルオーバー(サーバーの可用性向上)
    • レプリケーション(データベースの可用性向上)

フェイルオーバー

  • サーバーを普段メインで動作しているものと、待機しているものに分ける。
  • メインのシステムやサーバーの一部で障害が発生したとき、待機しているシステムに自動で切り替える仕組みのこと。
  • 待機しているシステムが普段から利用されて動いているか・いないかで2種類のパターンがある。
    • アクティブ・パッシブ フェイルオーバー(普段は動いていない)
    • アクティブ・アクティブ フェイルオーバー(普段も動いている)
  • デメリット:
    • 複数のサーバーで構成されているので複雑さが増す。
    • サーバー間で更新を同期する前に障害等が発生すると、データの欠損が起こる可能性がある
アクティブ・パッシブ
  • 待機しているシステムは普段動いていない。
  • メインのサーバー(マスター)から「ちゃんと働いてます!」という周期信号が待機しているシステム(スレーブ)に送られる。
  • この信号が途絶えたら、なんらかの障害が起きたと考えて待機しているサーバーがサービスを再開する。
  • マスター・スレーブ」とも呼ばれる。
  • 待機しているシステムが常に起動している状態を「ホットスタンバイ」といい、切り替えが早い。
  • 待機しているシステムが切り替え時に起動されるものを「コールドスタンバイ」といい、切り替えは遅いがリソースは節約できる。
アクティブ・アクティブ
  • 複数のサーバーで同じ役割を担っていて、普段は仕事を分け合って負荷分散している。
  • サーバーのどれかがダウンしたら残りのサーバーでトラフィックを捌く。
  • 複数のサーバーで構成されている == それぞれに別々のパブリックIPアドレスが割り振られているので、DNSやクライアントとサーバーを中継するシステムはこれらの情報を把握している必要がある。
  • 全部のサーバーが普段から動いているので、「マスター・マスター」とも呼ばれる。

レプリケーション

  • Replication、複製。
  • フェイルオーバーのデータベースバージョン。
  • データベースを複製したレプリカを用意しておいて、データベースの障害に備える。
  • レプリカのデータベースに普段から書き込みがあるか・ないかで2種類の方法がある。
  • デメリット:
    • データベース間で書き込みの複製を行う前に障害等が発生すると、データの欠損が起こる場合がある。
    • 書き込みが多いと、複製に掛かる時間が増えて、読み込みが遅くなる。
    • データベースのレプリカの数が多いと、複製する数が増える→複製に掛かる時間が増える。
    • データベースへの書き込みは並列処理できても、複製は並列処理できないシステムがある。
    • 複数のデータベースで構成されているので、複雑さが増す。

感想

  • スケーラブルなシステムをつくるより具体的な手段がちょっとわかってきてよかった。
  • 「あっこれ覚えることがいっぱいあるやつだ」となってる。
  • system-design-primerにもAnkiあるけど、もっと簡単なレベルからやる自分用Ankiが必要そう…。
  • 応用情報までと被ってる内容もあるけど、応用情報はもう少し範囲が狭くて細かい用語が出た記憶、面接対策のシステムデザインはもう少し幅が広くて、適しているケースを考えたり具体的なツール名とかがよく出てくる感じ。
  • クラウドエンジニアじゃないからクラウドの勉強はまだいいや!と思ってたけどガンガン出てきそうな予感がしてチョト勉強してる…。

次の目標

  • 1周目でさらっと流した具体的なツール(DNSとかロードバランサーとか)をきちんと勉強したい。
  • できれば次の次で具体的な設計問題に取り掛かりたい…!