BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Twitter、進化するアーキテクチャ

Twitter、進化するアーキテクチャ

原文(投稿日:2009/6/26)へのリンク

Twitterサービスチームの主任エンジニアであり、主に最適化とスケーラビリティを担当しているEvan Weaver氏が、QCon London 2009においてTwitterのアーキテクチャ、とりわけ過去一年にわたって行ってきたウェブサイトの最適化について語った。

Twitterで使われているツールの多くはオープンソースである。そのスタックは、フロントサイドにRails、中間のビジネス層にC、Scala、Java、データストアとしてMySQLを利用してつくられている。すべてがRAM上に保持されており、データベースは単なるバックアップである。Railsのフロントエンドはレンダリング、複合キャッシュ、DBクエリ、同期的挿入を扱う。このフロントエンドは、MySQLクライアント、Memcachedクライアント、JSONクライアントなどの、多くはCで書かれたいくつかのクライアントサービスを主につなぐ役目も果たしている。

ミドルウェアとしては、Memcached、ページキャッシュとしてのVarnish、Scalaで書かれたMQであるKestrelを利用している。さらに同じくScalaで書かれた、多数のつぶやき(tweets)をトラッキングしようと考えているクライアントが使うCometサーバを利用するための作業が進行中である。

Twitterは“メッセージングプラットフォームではなくコンテンツマネジメントプラットフォーム”として始まっており、そのため、まとめて読むことを基礎にした初期モデルから、すべてのユーザが最新のつぶやきで常に更新される必要がある現在のメッセージングモデルに変更するために、多くの最適化が必要とされた。変更は、キャッシュ、MQ、Memcachedクライアントという3つの領域で行われた。

キャッシュ

各つぶやきは平均126ユーザにトラッキングされている。そのため、明らかにキャッシングが必要である。当初の設定では、APIだけがユーザからつぶやきが届くたびに無効化されるページキャッシュを持ち、残りのアプリケーションはキャッシュレスであった。

image

アーキテクチャの最初の変更は、64ビット整数で直列化されたtweet IDの配列を含むライトスルーキャッシュであるベクターキャッシュ(Vector Cache)をつくることだった。このキャッシュは99%のヒット率を持っている。

2番目の変更は、ユーザとつぶやきのデータベースレコードを含む別のライトスルーキャッシュである列キャッシュ(Row Cache)を加えることだった。このキャッシュは95%のヒット率を持ち、Cache Moneyと呼ばれるNick Kallen氏のRailsプラグインを利用している。Nick氏はTwitterのシステムアーキテクトである。

3番目の変更は、つぶやきの直列化バージョンを含むリードスルーキャッシュである断片キャッシュ(Fragment Cache)を導入することであった。ここで、つぶやきの直列化バージョンとは、APIクライアントを通してアクセスされ、JSON、XML、あるいはAtomでパッケージングされているものである。このキャッシュも同じく95%のヒット率を持つ。断片キャッシュは「直接ベクターを消費し、もし直列化された断片がその時点でキャッシュされていたら、誰かが見ようとして要求したつぶやきの実際の列データをロードしない。そうやって、データベースアクセスを避けて、多くの時間を節約する」とEvan氏は述べた。

さらにもう1つの変更は、ページキャッシュとは分離されたキャッシュプールをつくることだった。Evan氏によれば、ページキャッシュプールは直接的な無効化ではなく、世代型キー手法(generational key scheme)を用いている。その理由は、クライアントは

HTTPのif-modified-sinceを送ることができ、リクエストの中で好きなときにタイムスタンプを押すことができます。また、私たちはその列をスライスして、クライアントが見たいつぶやきだけをクライアントに提供する必要があります。しかし、クライアントが利用した可能性のあるキーをすべてトラッキングしたくはないのです。この世代型手法には大きな問題がありました。なぜなら、すべての無効なキーを削除することができなかったからです。つぶやきの数にしたがってユーザが受け取る各ページが追加されるたびに、キャッシュ内の有効なデータを押し出してしまいます。その結果、私たちのキャッシュは5時間の実効時間しか持つことができませんでした。これらのページキャッシュのすべてがずっと流れ続けているからです。

ページキャッシュがプールに移動されることで、キャッシュミスは50%削減された。

以下の図が現在Twitterによって使われているキャッシュ手法の図である。

image

Twitterのトラフィックの80%がAPI経由であるため、そこには2つのレベルのキャッシュが追加されている。それぞれのキャッシュは、その前の層から来るリクエストの最大95%を提供する。全体のキャッシュの変更は、計20から30の最適化によって達成され、それによってもたらされたものは

10倍のキャパシティの改善でした。もっとできたかもしれませんが、私たちはその時点で別のボトルネックにぶち当たったのです…。私たちの戦略は、まずリードスルーキャッシュを追加し、それが正しいものを無効化することを確かめ、その後で、ライトスルーキャッシュに移し、新しいtweet IDが届くたびごとにキャッシュを壊すのではなく、修復してオンラインにすることです。

メッセージキュー

各ユーザには平均して126のフォロワーがいるので、各つぶやきに対するキューには126のメッセージが存在するということになる。そうでなくても、トラフィックがピークに達する時というのがある。それはオバマ大統領の就任式のときなどで、そのときには、毎秒数百のつぶやき、言い換えるとキューの中に数万のメッセージがあるという状態になり、そのトラフィック量はその当時の通常のトラフィックの3倍の量であった。MQはピークを見つけて、それを時間をかけて分散するようにし、その結果として、さらなる多くのハードウェアの追加をしなくてよいようにする。TwitterのMQはシンプルである。Memcachedプロトコルをベースとしており、ジョブの順序づけはなく、サーバ間の共有状態もなく、すべてはRAMに保持され、トランザクション処理である。

MQの最初の実装はRubyで書かれたStarlingを利用していたが、特に世代型ではないRubyのガベージコレクションのためにうまくスケールしなかった。その結果、ガベージコレクタがその作業を終えるまでの間キュー処理全体がある時点で止まってしまうことが理由でMQがクラッシュした。そこで、MQをより成熟したJVMのガベージコレクションを利用するScalaに移植することが決定された。現在のMQはたった1200行のコードからなり、3つのサーバ上で稼動している。

Memcachedクライアント

Memcachedクライアントの最適化は、クラスターのロードの最適化を目的としたものだ。現在のクライアントが使っているものは、libmemcachedであり、Twitterがその最も重要なユーザであり、コードベースへの貢献者となっている。そのlibmemcachedをもとにして、一年以上かけて断片キャッシュの最適化を行うことにより、1秒間に処理するページリクエストが50倍に増加した。

image

リクエストに局所性がないため、リクエストを処理するもっとも高速な方法は、データを必要なときに各サーバで再処理することではなく、データを事前処理しネットワークRAM上にストアすることである。この手法はWeb2.0のサイトの多くで利用されているもので、それらのサイトはほぼ完全にメモリ上で稼動している。次のステップは、Evan氏によれば、「1年間読み取りのスケーリングを行って、その後、書き込みのスケーリングを行う。それから、複数コロケーションの問題に取り組む」とのことだ。

QConでのプレゼンテーションのスライドがEvan氏のサイトで公開されている

この記事に星をつける

おすすめ度
スタイル

BT