実例をもとに、もう少し具体的に考えてみよう。 第1章では、Twitterっぽいメッセージングサービスを実装するとしたら…ということを考えた。 読み込み操作の中でいちばん多いのが、いわゆる「ホームタイムライン」の表示リクエストだろう。 自分がフォローしているすべてのユーザーの最近のツイート(と、ユーザー名やアイコン画像)を表示するものだ(図1-17参照)。
そんな場合に使うSQLクエリを図1-18で見たが、あんなのを毎回実行するのでは速度が遅くなってしまうという結論だった。 事前にホームタイムラインの内容を算出して取得しておけば、ユーザーからのリクエストにすぐに答えられるようになる。 なんとなくマテリアライズドビューっぽく聞こえないだろうか?
Twitter並みの規模でマテリアライズドビューを作れるデータベースは存在しない。 でも、ストリームプロセッシングツールを使えば、マテリアライズドタイムラインを実装できる*1。 その概要を図5-20に示す*2。
図5-20. ストリームプロセッシングツールを用いたTwitterタイムラインの実装
まずはすべてのデータソースをイベントストリームとして扱えるようにしなければいけない。そのためには、第3章でとりあげたCDCを用いるか、あるいは第2章で考えたようにイベントを直接ログに書き出せばいい。今回の例では、これら三つのデータソースからのイベントストリームを利用する。
これらのストリームをKafkaに入れれば、マテリアライズドビューを作れる。 Kafka StreamsあるいはSamzaを使って、ストリームプロセッシングジョブを書けばいい。 たとえば、あるツイートが何回リツイートされたかを数えるジョブを書けば、マテリアライズドビュー"retweet count"を作れる。
ストリームをjoinすることもできる。 tweetsとuser profilesをjoinした結果は、一連のツイートに非正規化されたプロファイル情報(ユーザー名やプロファイル画像など)がぶらさがったストリームになる。 誰かがプロファイルを更新したときにそれをどこまで反映させるか(変更後のツイートにだけ繁栄させるのか直近の100ツイートにだけ反映させるのか、あるいは過去にさかのぼってすべてのツイートに反映させるのかなど)は、ストリームプロセッサの実装しだいでいかようにでもできる (まあ過去にさかのぼってすべて更新するのは非効率的だろうけど、そのへんはなんとでもなるでしょう)。
次に、ツイートとフォロワーをjoinしてみよう。 フォロー/アンフォロー イベントをとりまとめれば、あるユーザーXをフォローしているユーザーのリストを作ることができる。 Xが何かをツイートしたらそのリストをスキャンして、それぞれのホームタイムラインに新しいツイートを配送すればいい (Twitterではこれを"fan-out"*3と呼んでいる)。
「ホーム」タイムラインはいわばメイルボックスのようなもの。次にログインしたときにユーザーが見るべきすべてのツイートが、そこに含まれている。 ここでは、図1-18のSQLに相当するマテリアライズドビューを効率的に作った。 注目すべきは、SQLのふたつのjoinが図5-20におけるストリームのjoinに対応していること。 ストリームプロセッシングシステムは、いわばクエリを継続的に実行し続けているようなもの。