うわー、これは単純な質問であり、可能な答えの膨大な配列です。質問のより明確な部分は、データベースと直接またはWebサービスを介して接続する方がスケーラブルであるかどうかを尋ねます。その答えは簡単です。データベースに直接クエリを実行します。Webサービスを通過すると、ファイアウォールの背後で動作するコード(全体的に)にはまったく不要な待ち時間が追加されます。たとえば、Webサービスでは、リクエストを受信し、それをデシリアライズし、DBにクエリし、レスポンスをシリアライズして返すためのコンポーネントが必要です。したがって、コードがすべてファイアウォールの背後で動作している場合は、トラブルを回避し、DBに直接クエリを実行してください。
ただし、Webサイトをスケーラブルにすることは、最初に提起した問題をはるかに超えています。ここで接していても許してくれますが、あなたが特にFacebookについて言及していることを考えると、それは役に立つと思いました。
Brad Fitzpatrick(LiveJournalの創設者であり、現在Googleにいる)によって構築された作品とツールを読むことをお勧めします。Six Apartで彼と仕事をしたとき、彼から学んだことと、LiveJournalのアーキテクチャが非常にスケーラブルであることをここにいくつか紹介します。
広いデータベーステーブルではなく、狭いデータベーステーブルを使用します。これが魅力的だったのは、このアーキテクチャの動機を知ることでした。これは、簡単かつ迅速にシステムを作成することでした。アップグレードされました。幅の広いテーブル、または各フィールドまたはプロパティがテーブル内の列であるテーブルを使用する場合、データベーススキーマをアップグレードするとき、たとえば新しい列を追加するとき、システムはスキーマの実行中にテーブルをロックする必要があります変更が実装されます。大規模に運用する場合、これはデータベーススキーマを単純に変更するだけで大規模なデータベースが停止する可能性があることを意味します。それは明らかに悪い。一方、狭いテーブルは、オブジェクトに関連付けられた個々のプロパティをデータベース内の単一の行として単純に格納します。したがって、データベースに新しい列を追加する場合に必要なことは、ロックされていない操作であるレコードをテーブルに挿入することだけです。OK、それは少しの背景です。このモデルがLiveJournalのような作業システムで実際にどのように変換されるかを見てみましょう。
ユーザーのブログに最新の10個のジャーナルエントリをロードし、各ジャーナルエントリに10個のプロパティがあるとします。従来の幅の広いテーブルレイアウトでは、各プロパティはテーブルの列に関連付けられていました。その後、ユーザーは必要なすべてのデータを取得するためにテーブルを1回クエリします。クエリは10行を返し、各行には必要なすべてのデータが含まれます(SELECT * FROMエントリORDER BY date LIMIT 10など)。ただし、狭いテーブルレイアウトでは、状況は少し異なります。この例では、実際には2つのテーブルがあります。最初のテーブル(テーブルA)は、エントリのID、作成者のID、エントリの日付などで検索する単純な基準を格納します。 (テーブルB)は、エントリに関連付けられたすべてのプロパティを保存します。この2番目のテーブルには、entry_id、key、およびvalueの3つの列があります。テーブルAのすべての行に対して、テーブルBには10行(各プロパティに1行)があります。したがって、最新の10個のエントリを取得して表示するには、11個のクエリが必要です。最初のクエリはエントリIDのリストを提供し、次の10個のクエリは最初のクエリで返された各エントリに関連付けられたプロパティを取得します。
「聖なるモリー!」「地球上でどのようにスケーラブルにできるのですか?!」まったく直感に反する権利ですか?最初のシナリオではデータベースクエリが1つだけでしたが、2番目の「よりスケーラブルな」ソリューションでは11のデータベースクエリがあります。それは意味がありません。その質問に対する答えは、次の箇条書きに完全に依存しています。
memcacheを自由に使用します。気づいていない場合、memcacheは分散型のステートレスで低レイテンシのネットワークベースのキャッシュシステムです。Facebook、Google、Yahoo、そして地球上のあらゆる人気のあるスケーラブルなWebサイトで使用されています。狭いテーブルデータベース設計に固有のデータベースオーバーヘッドを相殺するために、Brad Fitzpatrickによって部分的に発明されました。上記の#1で説明したのと同じ例を見てみましょうが、今回はmemcacheを紹介しましょう。
ユーザーが最初にページにアクセスし、キャッシュに何もないときに始めましょう。まず、ページに表示する10エントリのIDを返すテーブルAをクエリすることから始めます。これらの各エントリについて、データベースにクエリを実行して、そのエントリに関連付けられているプロパティを取得し、それらのプロパティを使用して、コードがインターフェイスできるオブジェクト(オブジェクトなど)を構成します。次に、memcacheにそのオブジェクト(またはそのオブジェクトのシリアル化された形式)を隠します。
誰かが同じページを2回目にロードするとき、同じ方法で開始します。表示するエントリIDのリストをテーブルAに照会します。エントリごとに、最初にmemcacheにアクセスして、「キャッシュにエントリ#Xがありますか?」と言います。はいの場合、memcacheはエントリオブジェクトを返します。そうでない場合は、データベースを再度クエリしてそのプロパティを取得し、オブジェクトを構成して、memcacheに格納する必要があります。ほとんどの場合、誰かが同じページに2回目にアクセスすると、データベースクエリは1つだけで、他のすべてのデータはmemcacheから直接取得されます。
実際には、LiveJournalのほとんどで起こったのは、システムのデータのほとんど、特に揮発性の低いデータがmemcacheにキャッシュされ、ナローテーブルスキーマをサポートするために必要なデータベースへの余分なクエリがほとんど完全に相殺されたことです。
この設計により、すべての友達に関連付けられた投稿のリストをストリームまたは「壁」にまとめることに関連する問題をはるかに簡単に解決できました。
次に、データベースのパーティション分割を検討します。上記のモデルは、さらに別の問題を浮上させます。つまり、狭いテーブルは非常に大きく/長くなる傾向があります。また、これらのテーブルの行が多いほど、他の管理タスクが難しくなります。これを相殺するには、何らかの方法でテーブルをパーティション分割してテーブルのサイズを管理し、ユーザーのクラスターが1つのデータベースで処理され、別のユーザーのクラスターが別のデータベースで処理されるようにすることをお勧めします。これにより、データベースの負荷が分散され、クエリの効率が維持されます。
最後に、素晴らしいインデックスが必要です。クエリの速度は、データベースのテーブルのインデックス作成の程度に大きく依存します。インデックスが何であるかを議論するのにあまり時間を費やしませんが、それは干し草の山から針を見つけるのをより効率的にする巨大なカードカタログシステムによく似ていると言うことを除いて。mysqlを使用する場合は、スロークエリログをオンにして、実行に長い時間がかかるクエリを監視することをお勧めします。クエリがレーダー上に表示されたら(遅いなどの理由で)、高速化するためにテーブルに追加する必要があるインデックスを見つけます。
「この素晴らしい背景のすべてに感謝しますが、聖なる悪徳、それは私が書かなければならない多くのコードです。」
必ずしも。memcacheとのインターフェイスを非常に簡単にする多くのライブラリが作成されています。さらに他のライブラリは、上記のプロセス全体を体系化しています。PerlのData :: ObjectDriverはまさにそのようなライブラリです。他の言語については、独自の調査を行う必要があります。
この回答がお役に立てば幸いです。私が頻繁に見つけたのは、システムのスケーラビリティがコードにますます低下し、健全なデータストレージと管理戦略/技術設計にますます低下することです。