7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

システム設計の面接試験が良かったので紹介します

Posted at

はじめに

現在受託開発の会社でweb系のエンジニアをしている橋田至です

「システム設計の面接試験」という著書が非常に良かったので、概要や勉強になった点などをご紹介します。

私自身は面接対策というよりは、webに関するパフォーマンスやスケールするシステムのアーキテクチャについて学べたことが一番良かったなと感じています。

具体的には

  • 垂直スケーリングと水平スケーリング
  • ロードバランサ
  • DBレプリケーション
  • キャッシュ
  • CDN
  • ステートフルアーキテクチャとステートレスアーキテクチャ
  • メッセージキュー
  • レートリミッターとアルゴリズム
  • コンシステントハッシュ
  • キーバリューストア

などの解説、必要性を実際にさまざまなサービスを設計することを仮定して説明してくれています。

せっかくなので、ここでは上記の用語について学んだ内容をご紹介します。

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

垂直スケーリング(Vertical Scaling)

定義:

垂直スケーリングは、システムの性能を向上させるために、既存のサーバのCPU、RAM、ストレージなどのハードウェアリソースをアップグレードすることを指します。一般的には「アップサイジング」とも呼ばれます。

具体的な例:

例えば、4コアのCPUと16GBのRAMを持つサーバを、8コアのCPUと32GBのRAMにアップグレードする場合、これは垂直スケーリングの例です。

メリット:

  1. シンプル: 現存のシステム構成やアーキテクチャの変更が不要で、資源を追加するだけで性能を向上させることができます。
  2. 管理が容易: シングルサーバの管理となるため、複数のサーバを管理するよりは管理が容易です。

デメリット:

  1. 上限がある: ハードウェアの物理的な制限があるため、無限にリソースを増やすことはできません。
  2. ダウンタイム: ハードウェアのアップグレード時にサービスの中断が必要になることがある。

水平スケーリング(Horizontal Scaling)

定義:

水平スケーリングは、システムの性能を向上させるために、新しいサーバを追加することを指します。これにより、全体としてのシステムの処理能力やストレージ容量が増加します。

具体的な例:

例えば、1つのサーバを使用しているウェブアプリケーションを2台、3台と増やしてロードバランサーの背後に配置することで、トラフィックの増加に対応させる場合があります。

メリット:

  1. 柔軟性: 必要に応じてサーバを追加・削除することで、動的にシステムの容量を調整できます。
  2. 高可用性: 一つのサーバが故障しても、他のサーバが動作を続けることができ、サービスのダウンタイムを最小限に抑えることができます。

デメリット:

  1. 複雑性: 複数のサーバを同時に管理する必要があるため、システムの管理やトラブルシューティングが複雑になります。
  2. ソフトウェアの調整: 一部のアプリケーションやデータベースは、水平スケーリングに対応していない場合があり、調整や変更が必要になることがあります。

ロードバランサとは?

ロードバランサは、多数のリクエストやトラフィックをサーバー群に分散させる役割を持つネットワーク機器やソフトウェアです。これにより、サーバーの過負荷を防ぎ、システムの可用性や応答時間を向上させることができます。

具体的な例

  1. ハードウェアロードバランサ: 物理的な機器として設置されるもので、データセンターやオンプレミスの環境でよく使用されます。例: F5 BIG-IP, Cisco ACE など。
  2. ソフトウェアロードバランサ: ソフトウェアとして実装され、クラウドや仮想環境で動作するもの。例: HAProxy, Nginx, AWS Elastic Load Balancing (ELB) など。

メリット

  1. 可用性の向上: 一部のサーバーに障害が発生しても、他のサーバーがリクエストを処理することでサービスのダウンタイムを減少させる。
  2. スケーラビリティ: トラフィックの増加に応じてサーバーを追加することが容易。
  3. 負荷の均衡: すべてのサーバーが均等にリクエストを処理することで、特定のサーバーの過負荷を防ぐ。
  4. 保守性の向上: サーバーを更新・保守する際に、他のサーバーがリクエストを処理し続けることでサービスを継続的に提供可能。

デメリット

  1. コスト: ハードウェアやソフトウェアの導入・運用には費用がかかる。
  2. 複雑性の増加: システム構成が複雑になることで、トラブルシューティングや運用の難易度が上がる場合がある。
  3. 遅延: ロードバランサを通すことで、わずかながら応答時間に遅延が生じる可能性がある。

DBレプリケーションとは?

データベース(DB)レプリケーションとは、データベースの内容を一つ以上の場所にコピー・同期することを指します。これにより、データの可用性や冗長性が向上し、多くの場合、パフォーマンスの向上や災害復旧のための策として採用されます。

具体的な例

  1. マスタースレーブレプリケーション:

    • マスターデータベースが更新処理を行い、スレーブデータベースはその更新内容を受け取り同期します。
    • 例: MySQLのマスタースレーブレプリケーション
  2. マルチマスターレプリケーション:

    • 複数のデータベースが更新処理を行い、他のデータベースとその内容を同期します。
    • 例: MySQL Group ReplicationやGalera Cluster

メリット:

  1. 可用性の向上: 主要なデータベースがダウンしても、他のレプリカからデータを取得できる。
  2. 負荷分散: 読み取り専用のクエリをレプリカに振り分けることで、メインのデータベースの負荷を減少させる。
  3. データの冗長性: データの損失リスクを減少させる。
  4. 地理的分散: 異なる地域やデータセンターにデータを分散させることで、地域的な障害からのリカバリーが可能。

デメリット:

  1. 複雑性: レプリケーションの設定や維持には専門知識が必要。
  2. 遅延: データベース間の同期にはタイムラグが発生する可能性がある。
  3. データ不整合: マルチマスターレプリケーションの場合、同時に複数のデータベースが更新された際のコンフリクトのリスク。
  4. リソースのコスト: 追加のサーバーやストレージが必要。

キャッシュとは?

一般にデータアクセスの速度を向上させるために、一時的にデータを高速にアクセスできる場所に保存する技術や手法のことを指します。キャッシュの活用により、繰り返し同じデータにアクセスする際のパフォーマンスを大幅に向上させることが可能です。

具体的な例:

  1. ブラウザキャッシュ:ウェブページを閲覧する際、一度読み込んだ画像やスタイルシートなどのデータをローカルに保存しておき、次回そのページを訪れた際に再ダウンロードせずに高速に表示できるようにする。

  2. CPUキャッシュ:CPUがメインメモリからデータを取得する際の遅延を低減するために、最近アクセスしたデータや頻繁にアクセスされるデータを小容量だが高速なキャッシュメモリに保存しておく。

  3. データベースキャッシュ:データベースのクエリ結果を一時的に保存しておき、同じクエリが再度実行された際には保存しておいた結果を返すことで、クエリの実行時間を短縮する。

メリット:

  1. 速度向上:繰り返し同じデータやリソースにアクセスする際の応答時間を大幅に短縮できる。
  2. サーバーの負荷軽減:キャッシュされたデータのアクセスにより、サーバーやデータベースへのアクセス頻度が減少し、リソースの消費が減る。
  3. 帯域幅の節約:データの再ダウンロードが不要となるため、ネットワーク帯域を節約できる。

デメリット:

  1. データの古さ:キャッシュデータは最新のものではない可能性があるため、常に最新の情報が必要な場合には適していない。
  2. キャッシュの管理:キャッシュデータの有効期限やサイズ、更新のタイミングなどを適切に管理する必要がある。
  3. リソースの消費:キャッシュデータを保存するためのリソース(メモリやストレージ)が必要となる。

CDNとは?

CDN(Content Delivery Network)は、ウェブコンテンツを効率的かつ迅速にエンドユーザーに配信するためのネットワークシステムです。CDNは、複数のデータセンターやキャッシュサーバーを世界中の様々な場所に配置し、ユーザーのリクエストに応じて最も近いサーバーからコンテンツを提供します。

具体的な例

  1. 静的コンテンツの配信 : 画像、CSS、JavaScriptファイルなどの静的ファイルをキャッシュして迅速に配信します。
  2. 動画ストリーミング : グローバルな視聴者に向けて、遅延なく動画を配信するために使用されます。
  3. ソフトウェアのダウンロード : ソフトウェアやアップデートの配信を高速化します。

メリット

  1. 高速な配信 : ユーザーの近くにコンテンツをキャッシュするため、ウェブサイトの読み込み速度が向上します。
  2. 耐障害性の向上 : 単一のサーバーに依存せず、多くのサーバーからコンテンツを提供するため、一部のサーバーに障害が発生してもサービスの継続が可能です。
  3. コスト削減 : トラフィックのピークを分散させることで、オリジンサーバーへの負荷が減少し、インフラコストを削減できます。
  4. セキュリティの向上 : 一部のCDNは、DDoS攻撃などのセキュリティ脅威から保護する機能を提供しています。

デメリット

  1. キャッシュの管理 : 古いコンテンツがキャッシュされてしまうと、ユーザーに最新の情報が届かなくなる可能性があります。
  2. コスト : 大量のデータを配信する場合、CDNの使用料が高くなることがあります。
  3. 第三者サービスの依存 : CDNプロバイダーによるサービスの停止や変更が直接影響することがあります。

ステートフルアーキテクチャとステートレスアーキテクチャ

ステートレス (Stateless) アーキテクチャ

ステートレスとは、システムがユーザーやセッションの状態を保持しないことを指します。各リクエストは、それ自体が全ての必要情報を持っており、過去のリクエストやセッションの状態に依存しません。

例: HTTPプロトコルは、その性質上ステートレスです。各HTTPリクエストは、それに対するレスポンスを生成するための全ての情報を含んでいる必要があります。

メリット:

  1. スケーラビリティ: 状態を持たないため、新しいインスタンスやサーバを追加するのが容易です。
  2. 予測可能性: 各リクエストが独立しているため、他のリクエストの影響を受けずに動作します。
  3. 簡素化: セッション管理の必要がないため、設計や管理が簡単です。

デメリット:

  1. データの繰り返し: 同じデータを何度も送信する必要がある場合があり、効率が低下することがあります。
  2. パフォーマンスの問題: 必要な情報を毎回リクエストで送信するため、通信量が増加する可能性があります。

ステートフル (Stateful) アーキテクチャ

ステートフルとは、システムがユーザーやセッションの状態を保持することを指します。これにより、以前のリクエストの結果やデータを次のリクエストで利用できます。

例:

オンラインのショッピングカートは、ユーザーが商品を追加する度にその状態を更新し、ユーザーが次にアクセスした時もその状態を保持しています。

メリット:

  1. 効率: 一度取得したデータや状態を再利用できるため、リソースの使用が最小限になります。
  2. ユーザーエクスペリエンス: ユーザーの過去のアクションに基づいてカスタマイズされた体験を提供できます。
  3. 計算の簡素化: 過去のデータに基づいて新しい計算や操作を行うことができます。

デメリット:

  1. スケーラビリティの問題: 状態を持つことで、システムの成長に合わせて状態の管理や同期が難しくなることがあります。
  2. 復旧の難しさ: システムがクラッシュすると、状態の復旧が難しい場合があります。
  3. リソースの使用: 状態を維持するための追加のリソース(メモリやストレージ)が必要です。

この2つのアーキテクチャの選択は、アプリケーションの要件や目的によって変わります。状態を持たせることで効率的に動作するアプリケーションもあれば、ステートレスでシンプルに保つことが適切なケースもあります。適切なアーキテクチャを選択することで、アプリケーションのパフォーマンスや管理性を最適化することができます。


メッセージキューとは

メッセージキューは、プログラム間でデータを非同期に伝達するための中間的なデータ構造であり、メッセージ指向ミドルウェアの一部としてしばしば使用されます。これにより、システムやサービス間での通信を効果的に管理でき、耐障害性やスケーラビリティを向上させることができます。

具体的な例

  • RabbitMQ:高度にカスタマイズ可能なオープンソースのメッセージブローカー。
  • Apache Kafka:ストリーム処理プラットフォームであり、高スループットでメッセージを処理することができる。
  • Amazon SQS:AWSのフルマネージドなメッセージキューサービス。

メリット

  1. 非同期処理:システム間の通信を非同期に行うことで、一つのプロセスが待機している間に他のプロセスを実行することができ、全体のスループットが向上します。
  2. 耐障害性:メッセージキューが中間に介在することで、一部のコンポーネントがダウンしてもメッセージはキューに保存され、復旧後に処理が再開されます。
  3. スケーラビリティ:必要に応じて、キューの消費者や生産者の数を増減させることが簡単になります。
  4. 分散システムの統合:異なるシステムやサービス間でのデータのやり取りをシームレスに行うことができます。

デメリット

  1. 遅延:システム間の通信が非同期であるため、即時のレスポンスが必要な場合は適していないことがあります。
  2. 管理の複雑さ:メッセージキューの導入や運用、監視が必要となり、システム全体の複雑さが増します。
  3. 順序性の問題:一部のメッセージキューシステムでは、メッセージの順序性を保証するのが難しい場合があります。

レートリミッターとは?

レートリミッターは、システムやサービスにおいて、リソースへのアクセス頻度やリクエストの数を制限するための仕組みです。これは、過度なトラフィックやDDoS攻撃からシステムを守るため、また公平なリソース利用を保証するために使われます。

レートリミッターの主なアルゴリズム

  1. トークンバケット (Token Bucket)

    • 動作: 一定間隔でトークンがバケットに追加される。リクエストが来るたびにトークンが消費される。
    • メリット: 短時間のバーストトラフィックに対応可能。
    • デメリット: 長時間の高トラフィックには効果が低い。
  2. リーキーバケット (Leaky Bucket)

    • 動作: リクエストはバケットに溜まり、一定間隔で排出される。
    • メリット: 一定のレートでの処理が可能。
    • デメリット: 短期的なバーストトラフィックには柔軟に対応できない。
  3. 固定窓 (Fixed Window)

    • 動作: 固定時間窓でのリクエスト数をカウントする。
    • メリット: 実装が簡単。
    • デメリット: 窓の境界付近でのリクエストが2倍のトラフィックとして扱われる可能性がある。
  4. スライディング窓 (Sliding Window)

    • 動作: 任意の時点での前N秒間のリクエスト数をカウントする。
    • メリット: 窓の境界問題を緩和。
    • デメリット: 固定窓よりも計算コストが高い。

コンシステントハッシングとは?

コンシステントハッシングは、データ分散技術の一つで、主に分散キャッシュや分散ストレージなどでのデータの配置や再配置を効率的に行うためのアルゴリズムです。これは、大量のリクエストやデータを均等に分散するためのツールとして考えられています。

具体的な例

例えば、3つのサーバー(A, B, C)があり、それぞれのサーバーにデータを保存する場合を考えます。通常のハッシュ関数を使用すると、データのキーに基づいてデータが各サーバーに均等に分散されることは期待できます。しかし、サーバーBがダウンした場合、Bに割り当てられていたデータは再ハッシュされ、AやCに再分配される必要があります。これにより、多くのデータが移動する可能性があり、システムの負荷が大きくなる恐れがあります。

コンシステントハッシングでは、キー空間を円環上にマッピングし、各サーバーもこの円環上に配置します。データのキーをハッシュ関数で円環上の位置にマッピングし、その位置から時計回りに最初に出会うサーバーにデータを保存します。

サーバーBがダウンした場合、Bの位置の次に配置されているサーバー(この例ではC)がBのデータを引き継ぐだけです。これにより、必要なデータの移動量が大幅に減少します。

メリット

  1. スケーラビリティ: 新しいサーバーの追加や既存のサーバーの削除が、最小限のデータ移動で行える。
  2. 均等性: データは全てのサーバーにほぼ均等に分散される。
  3. 耐障害性: サーバーがダウンしても、そのサーバーのデータは隣のサーバーが効率的に引き継ぐことができる。

デメリット

  1. 実装の複雑さ: 通常のハッシュマッピングに比べ、コンシステントハッシングは少し複雑です。
  2. 最適なバランスの難しさ: 実際のシステムで完全な均等性を実現するためには、仮想ノードの導入などの追加的なテクニックが必要となる場合があります。

キーバリューストアとは?

データを「キー」と「バリュー」のペアとして保存するデータベースの一形態です。この形式のデータベースは、単純ながらも高速なデータアクセスを提供します。

具体的な例

例として、以下のようなキーバリューペアを考えましょう。

"12345": "山田太郎"
"67890": "佐藤花子"
"11223": "鈴木一郎"

この場合、各キー(例:12345)はユニークな識別子として機能し、それに関連づけられたバリュー(例:"山田太郎")はデータ本体となります。

メリット

  1. 高速な読み書き: キーを使って直接データにアクセスできるため、データの読み書きが高速です。
  2. スケーラビリティ: 多くのキーバリューストアは、分散型の設計を採用しているため、大量のデータを効率的に扱うことができます。
  3. シンプルさ: スキーマレスであり、テーブルや関係を気にすることなくデータを保存できるため、開発が容易です。
  4. 柔軟性: バリューとしてさまざまなデータタイプ(文字列、バイナリデータ、JSONオブジェクトなど)を保存できます。

デメリット

  1. クエリの制約: 従来のリレーショナルデータベースのような複雑なクエリや結合機能は、基本的にはサポートされていません。
  2. データの冗長性: 同じバリューを異なるキーで保存する必要がある場合、データの冗長性が生じる可能性があります。
  3. トランザクションの制約: 全てのキーバリューストアがトランザクションをサポートしているわけではないため、一貫性や原子性を求める場合は他のデータベースの選択が必要となることがあります。

まとめ

スケールするサービスを開発するためにはさまざまな知識が必要になってきます。
著書では図を用いてわかりやすく具体例とともに解説してくれているので、興味を持ったら是非とも購入することをお勧めします。

もし参考になったらいいね、Twitterのフォローもお願いします!

毎日技術や個人開発、仕事に関してのツイートをしています

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?