1人だけに使われるシステムを作る場合は単一のサーバーにそのままリクエストしてレスポンスを返すだけで十分に動くことが多い。
しかし1億人に使われるシステムを作るにはそれだけでは十分ではなくなってくる。
この記事では1人に使われるシステムから1億人に使われるシステムをスケールさせる際に考えるべき点を書いていく。
注)人数はあくまで例で1と1億がわかりやすく差があると思っているのでそんな表現をしています。
1人に使われるシステム
フロントエンドとバックエンドサーバーとデータベースサーバーを構築することで1人(実際にはもっと人数がいてもいい)が利用する分には問題ないことが多い。
バックエンドサーバーとDBサーバーとにおける処理のおおよそのフローとしては2つ。
- バックエンドサーバーはクライアントからリクエストを受け取る
- バックエンドサーバーからDBサーバーにクエリを発行してクライアントにレスポンスを返す
ユーザー数が少なければこれで十分動き続ける。
1億人に使われるシステム
ユーザー数が増えるに連れて各サーバーの負荷が上がってくる。
サーバーのリソースが枯渇してしまうとレスポンスを返すことができなくてユーザーが困ってしまう。
こんな悲しい状態にならないために各サーバーを冗長構成にして負荷分散をすることを考えましょう。
バックエンドサーバーの冗長化
クライアントからリクエストが来た時に単一のサーバーで処理しきらずに複数の同一の処理をするサーバーに分散させることができる。
一般的にクライアントからのリクエストは直接バックエンドサーバーに向かわせずにロードバランサー(LB)に向かわせることが多い。
LBは複数のバックエンドサーバーへ振り分けて1台あたりの負荷を減らすことでリソースが枯渇することを回避させることが可能になる。
またLBを導入するとあるサーバーが障害を起こして止まってしまった場合には別のサーバーに処理を流すように設定することができ、サービスを使い続けられる状況を維持することができるようになる。
DBサーバーの冗長化
DBサーバーの冗長化も考えてみましょう。
ここでの考慮すべきポイントバックエンドサーバーと同様に冗長化すると保存されたデータが分散されることになります。
例えば3台のDBサーバーがあり、あるレコードを保存した時には3台のDBサーバーのうちの1台に保存されることになり、データ取得時にどのDBサーバーからデータを取得するかを考える必要があります。
(シャーディングという手法を使うことでデータを分散して保存することができますが今回は別の方法を紹介します)
これを解決方法はレプリケーションという手法を使うことができます。
レプリケーションは1台のDBサーバーの状態を他の複数のDBサーバーに同期することで冗長化を実現する手法です。
その1台をソース、他の複数台をレプリカと呼び、書き込み処理はソースに対して行い、読み込み処理はレプリカに対して行うことで負荷を分散させることができます。
この構成ではソースが障害を起こしてしまった場合にはレプリカをソースに昇格させることでサービスを継続させることができます。
1億人に快適に使われるシステム
ここまでで多くのユーザーが使えるシステムを作る手法を紹介してきた。
一方で遅延がなく快適に使われるかについては度外視してきたため、ここからは快適に使われるための設計を考えていく。
快適と言っているのはローディングスピナーがいつまでもぐるぐる回っていなく、あるイベントが発生したら速く反応してくれることを指している。
キャッシュの導入
キャッシュとはデータを一時的に保存しておくことでデータ取得時にオリジンへのアクセスする回数を減らし高速化する手法です。
システム構成に限らずプロセッサ、ディスク、メモリやCDNなど様々な箇所でキャッシュを導入することができます。
ことサーバーサイドのキャッシュについてはRedisやMemcachedなどのインメモリデータベースを使ってキャッシュを導入することができます。
動きとしては
- キャッシュにデータがあるか確認する
- キャッシュにデータがあればキャッシュからデータを取得して返す
- キャッシュにデータがなければオリジンからデータを取得してキャッシュに保存して返す
という流れになります。(これをRead Throughと呼びます。)
特に読み込み処理が多く、その読み込みが複雑で重い処理であればキャッシュを導入することで高速化することができます。
非同期処理の導入
読み込みだけでなく書き込みを高速化する手法もあります。
それが非同期処理で。非同期処理はリクエストを受け取った後にその処理をバックグラウンドで行う手法です。
あるリクエストが来た時にその処理を全て完了させてからレスポンスを返すのではなく、リクエストを受け取った後にキューイングをしてレスポンスを返し、リクエストに対して非同期的にワーカーで処理を行うようにすることで高速化することができます。
リクエストを受け取ったらサーバーはメッセージキューにリクエストを入れておき、ワーカーがそのキューからリクエストを取り出して処理を行うことで高速化することができます。
まとめ
システムをスケールさせて、かつ快適に使われる手法をまとめてみました。
負荷分散やキャッシュ、非同期処理などを導入することでユーザーが快適に使えるシステムを作ることができ、ユーザー思いの設計をできるようになりましょう。