news020.jpg

大規模サイトの舞台裏:大規模SNSのボトルネックとソリューション (1/4)

SNSは比較的データアクセスが多いアプリケーションであり、負荷対策が難しい部類に入る。本稿では、グリーCTOの藤本真樹氏が、GREEというSNSでの経験を基に、SNSの具体的な負荷軽減ソリューションを紹介する。


はじめに

 昨今では、地方自治体や企業内でもSNSを導入する動きが活発化しているようです。しかし、SNSは比較的データアクセスが多いアプリケーションであり、負荷対策が難しい部類に入ります。そこで本稿では、前回お届けした「大規模SNS実現のためのGREEのアプローチ」の内容からもう一歩踏み込んで、GREEというSNSでの経験を基に、SNSの具体的な負荷軽減ソリューションを紹介します。

SNSのボトルネック

 一定以上のユーザー数、データ量、そして機能を持つSNSでは、普通に構築していくと非常に悩ましいボトルネックが2つ顕在化してきます。1つは、「友達の新着」系の情報取得です。もう1つはアクセスコントロール、つまり「この情報は全体に公開、こちらは友達の友達まで」といった制限です。まずは、ボトルネックについて詳しく見てみましょう。

友達の新着情報

 友達の新着情報として、例えば「友達の新着日記」を考えてみます。ここでは仮にリスト1のように、友達情報、日記情報を持つテーブル(linkテーブルとblogテーブル)がそれぞれ存在するとします。


linkテーブル:
+-----------+---------+------+-----+---------+-------+
| Field     | Type    | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| user_id   | int(11) | YES |      | NULL    |       |
| friend_id | int(11) | YES |      | NULL    |       |
+-----------+---------+------+-----+---------+-------+
blogテーブル:
+----------------------+----------+------+-----+---------+-------+
| Field                | Type     | Null | Key | Default | Extra |
+----------------------+----------+------+-----+---------+-------+
| id                   | int(11)  |      |     | 0       |       |
| user_id              | int(11)  | YES  |     | NULL    |       |
| publication_datetime | datetime | YES  |     | NULL    |       |
| title                | text     | YES  |     | NULL    |       |
| content              | text     | YES  |     | NULL    |       |
+----------------------+----------+------+-----+---------+-------+

リスト1 SNSにおける友達情報/日記情報テーブルの例

 このとき、「user_id=1」の友達の日記を、新着順に取得するには次のようなコードになるでしょう。

SELECT * FROM blog, link WHERE link.user_id=1 AND blog.user_id=link.friend_id ORDER BY blog.publication_datetime


 上のコードでは、次のようにして内容を取得します。

  • blogテーブルのuser_idフィールドの値が自分の友達であるエントリのものを
  • publication_datetime(そのエントリが登録された日時)でソートする

大量データのソートでインデックスが有効活用されない

 さて、規模が大きくなってくるとどうなるか考えてみましょう。まず、linkテーブルとblogテーブルのデータベースサーバが分離されてしまうかもしれません。するとJOINができなくなるので、

SELECT friend_id FROM link WHERE user_id=1


としてから、以下のように2段階でJOIN相当の処理をしなければいけません(INの中には取得したfriend_idの値を並べます)。

SELECT title FROM blog WHERE user_id IN (……) ORDER BY publication_datetime LIMIT 0,5


 ここまでは別にボトルネックではないのですが、blogテーブルが肥大化してくると問題が顕在化します。例えば1日平均で10万件程度の日記が書かれるとすると、1年でおよそ3500万〜4000万レコードのテーブルとなります。すると、WHERE句とORDER BY句が並存した場合にインデックスが効果的に使われない*ため、パフォーマンスが劣化していきます。例えばuser_idとpublication_datetimeにマルチカラムインデックスが張られていても、

SELECT title FROM blog WHERE user_id IN (...)


で取得される件数(つまり自分の友達が書いた日記の総数)が増えれば増えるほど、ソートにコストが掛かります。試しにMySQL 4.0.xでEXPLAINしてみると以下のようになりました。


+-------+-------+---------------+---------+---------+------+------+-----------------------------+
| table | type  | possible_keys | key     | key_len | ref  | rows | Extra                       |
+-------+-------+---------------+---------+---------+------+------+-----------------------------+
| blog  | range | user_id       | user_id | 5       | NULL | 2    | Using where; Using filesort |
+-------+-------+---------------+---------+---------+------+------+-----------------------------+

WHERE句とORDER BY句が並存した場合のインデックス使用状況

 「Using where」とあるように、user_idによる検索にはインデックスが利用されますが、その後のソートは「Using filesort」とあるように、ソートでインデックスが利用されません。これは、user_idカラムとpublication_datetimeカラムの複合インデックスを作成することである程度改善できます*が、データ量が数千万件以上に肥大化した場合には、やはり性能が劣化します。

blogテーブルの分割とSNSの相性

図1 図1 ユーザーの「ホーム」ページ

 また、今度はさらにblogテーブルを、ユーザーIDなどをキーにして分割する必要が出たとします*。この場合、「友達AのデータはblogテーブルAに、友達BのデータはblogテーブルBに」といったように、自分の友達のデータがテーブルや、はたまたデータベースごとに分散してしまうことになります。

 このとき、普通にクエリを発行して問題を解決しようとすると、次のような手順になります。

  1. blogテーブルAに対して友達の新着日記を取得するクエリを発行する
  2. blogテーブルBに対して1と同じようにクエリを発行。テーブルN個に対して同様にクエリを発行する
  3. 得られた結果をアプリケーション側でマージ、ソートする

 先頭からN件だとまだ簡単ですが、200件目から5件、などという条件でデータを取得しようとすると、考えただけで面倒な処理が発生します。

 また、GREEではログイン後の初期画面(いわゆる『ホーム』。図1)で、友達の新着情報を各種(日記、フォト、レビューなど……)表示させていますが、ホームはサイト内でもかなりアクセスの多いページで、毎回前記のような動的ページ生成を行うと、非常にコストが高くなります。

 この問題に対処するため、単純にページキャッシュなども利用できますが、

  • 表示されるデータがユーザーごとに異なるため、キャッシュのヒット率が非常に低い
  • データの性質上即時性が求められるため、あまりキャッシュしたくない

という問題があり、キャッシュの利用も避けたいところです。

このページで出てきた専門用語

WHERE句とORDER BY句が並存した場合にインデックスが効果的に使われない

この現象は、特にMySQLを使っている場合に顕著。

複合インデックスを作成することである程度改善できます

特にMySQL以外のデータベースを使っている場合に効果が高い。

ユーザーIDなどをキーにして分割する必要が出たとします

インデックスを正しく利用したり、クラスタリングシステムの利用で問題を解決できることもあるが、やはり大量のレコード数が存在する場合には、MySQLを利用している場合に限らず、テーブルの分割が必要になるようだ。


       1|2|3|4 次のページへ

Copyright© 2010 ITmedia, Inc. All Rights Reserved.




キャリアアップ



エンタープライズ・ピックアップ

news004.jpg 世界で勝つ 強い日本企業のつくり方:利用契約の検討――グローバルクラウドで失敗しないために(前編)
2010年以降、クラウドサービスの利用がさらに加速する。サービスを利用する企業はプロバイダーのデータセンターに預けた自社情報を保護するために、法的な要素を理解しておかなければならない。企業が注意を払うべき法的な検討事項を整理する。

news001.jpg IT投資の新方程式:「Twitter使ってます」――現役MS社員が“社員力”を語る(前編)
マイクロソフトが掲げるプロモーションメッセージ「社員にチカラを。ITで企業力を。(以下、BIEB)」からは、ITで社員の生産性を向上することが業績の拡大につながる、といったニュアンスを感じる。そこで気になるのが「じゃあ、マイクロソフトの社員自身はどうなのよ?」ということ。3人の現役MS社員により実態が明らかになる……?

news010.jpg 産業構造を変えるか:「住宅クラウド」の衝撃
住宅都市工学研究所が進める「住宅クラウド」は、クラウドが企業のIT領域にとどまらず、ビジネスのやり方自体を変える可能性を示している。

news010.jpg オルタナティブな生き方 栗原進さん:ネットでリアルを楽しくしたい
SE出身の企業広報マンでありながら、趣味は落語で憧れの人はインディ・ジョーンズとアナログ全開の栗原さんに、ブログを書く理由やネットからはじまるコミュニケーションについて伺った。

news001.jpg 最強最速アルゴリズマー養成講座:トップクラスだけが知る「このアルゴリズムがすごい」――「探索」基礎最速マスター
プログラミングにおける重要な概念である「探索」を最速でマスターするために、今回は少し応用となる探索手法などを紹介しながら、その実践力を育成します。問題をグラフとして表現し、効率よく探索する方法をぜひ日常に生かしてみましょう。