はじめに
株式会社ドワンゴで「ニコニコ生放送 」の開発を担当している、小野と申します。本特集では、いわゆる「NoSQL」の一種であるRedisの概要と基本操作、そしてWebアプリケーションでの応用例までを、ニコニコ生放送での事例を交えながら紹介していきます。
Redisとは?
Redis は、Salvatore Sanfilippo氏によって2009年に公開されたインメモリベースのキー・バリュー・ストアです。2010年3月にはVMWareが同氏を雇入れ、同社の支援のもと、コントリビューターのPieter Noordhuis氏と共にフルタイムで開発が進められています。
本記事執筆時点での最新stableはバージョン2.0となり、新しいデータ型の追加やトランザクションのサポート、VM(仮想メモリ)の実装などが追加されました。
また、メモリ効率の向上やスループットの改善が図られたバージョン2.2が間もなくstableとなる模様です。
気になる採用事例ですが、公式サイトによれば、次の有名サイトでも利用されているとのことです。
他にも、国内外のソーシャルゲームのデータストレージとして採用されている事例を耳にします。そしてもちろん、ニコニコ生放送ではフロントエンド・バックエンド問わず大いに活用しています。
Redisの特徴
インメモリベース
すべてのデータがメモリ上に保持されるため、更新・参照が非常に高速です。同じくインメモリの高速性を応用したものにmemcachedがありますが、両者の想定する用途は大きく異なっています。
memcachedが主にデータベースクエリ等に対するキャッシュストレージとしての用途からGETやSET等のシンプルな操作に特化しているのに対し、Redisはそれ自体がデータストアとして利用可能であるように設計されており、更新参照用、管理用のコマンドが100種類以上用意されています。
また、memcachedがLRU(Least Recently Used)順に古いデータを自動削除するのに対し、Redisではコマンドで明示的に削除するか、timeoutを設定しない限りは削除されません。この点からも、あくまでデータストアとしての用途が想定されていることが明らかです[1] 。
永続化
インメモリベースのデータストアを導入する際の懸念として、クラッシュした際にデータが消えてしまうという問題があります。
Redisは永続化の機構を実装しており、インメモリの高速性を持ちながら、同時にディスクベース・データストアの永続性をも備えているのが特徴です。
Redisの永続化機構は、定期的に(または更新回数などの条件により)メモリ上のデータセットのスナップショットをファイル(.rdb)にダンプし、再起動時にはこのスナップショットの内容をメモリに読み込むことにより、前回ダンプした状態までデータセットを復元します。
このスナップショット方式の永続化では、最後にダンプしてからクラッシュするまでの更新内容は消えてしまいます。アプリケーションの要件によってはこれが許容できない場合もあるでしょう。この場合は、Append Only Fileを有効にすることで、クラッシュ時のデータ喪失を最小限にすることができます。このモードでは、すべての更新コマンドがAppend Only Fileに随時追記され、再起動時にこれらのコマンドを再実行することにより、データを復元します。
なお、誤解のないように強調したいのは、「 永続化」といっても、RAM容量より多くのデータを格納できるわけではないということです。あくまで最大容量はRAM容量の制約を受ける点に注意してください。この制約を超えるには、後述する「RedisVM」を使う必要があります。
データ構造
公式サイトの紹介文にもあるように、Redisは「data structure server(データ構造サーバー) 」と呼ばれます。その名のとおり、複数の文字列要素からなるデータ構造をバリューとして持つことができ、通常の文字列型のバリューと同じようにキーで参照することができます。
最新の2.0で実装されているデータ構造は、次のとおりです。
LIST(連結リスト)
SET(重複のない集合)
SORTED SET(ソート済みのSET)
HASH(連想配列)
このような通常のプログラミングで用いるようなお馴染みのデータ構造をそのままRedis上で表現できるうえ、要素の追加削除、ソートなどの定番の操作もコマンドとして用意されています。
これらのデータ構造と豊富なコマンドを組み合わせることにより、単純なキー・バリュー・ストアでは手間のかかる(あるいは全く不可能な)処理を容易に実装できるのです。
クライアントライブラリ
Redisの備える豊富なコマンドを使いこなすには、クライアントライブラリのサポートが不可欠です。既にRuby, PHP, Java, C++を含む主要な言語でクライアントライブラリが開発されています。お使いの言語のクライアントは公式サイト で確認ください。
どの言語のライブラリでも、大抵はRedisのコマンド名をそのまま関数名やメソッド名にしたものであるため、容易に習得できるはずです。
レプリケーション
複数のRedisインスタンスによるマスタースレーブ・レプリケーションを構成できるため、巨大なリストのソートや数万要素の集合演算などの重いコマンドを実行する場合、スレーブとなるインスタンスを追加することによって参照負荷を分散できます。
または、マスターに障害が起きた際のホットスタンバイとしてスレーブを利用することも考えられます。
RedisVM(バーチャルメモリ)
一部のデータをスワップファイルに書きだすことによって、実際のRAM容量よりも多くのデータを扱えるようにするオプション機能です。
この機能はバージョン2.0で正式にリリースされましたが、その後、動作が不安定になるなどの報告がされているため、導入を検討される際には十分に検証を行ってください。代替として「diskstore」という新しいストレージモデルの開発がはじまっており、Salvatore Sanfilippo氏のメッセージ によると、こちらはバージョン2.4でリリースされる予定のようです。
ニコニコ生放送での利用
ニコニコ生放送システムの特徴
ニコニコ生放送 は、リアルタイムの映像配信と、「 ニコニコ動画 」特有のコメントシステムを組み合わせたサービスです。
ニコニコ動画との特性上の大きな違いは、リアルタイム性が高く求められることにあります。フロントエンドは一般的なWebアプリケーションの構成ですが、バックエンドでは、映像配信サーバーをはじめ、座席管理サーバー、コメントサーバー、ボットサーバーなど、様々なシステムがリアルタイムに連動しており、あらゆるデータが刻一刻と変化しています。
特に、話題性の高い公式の番組では、開場の瞬間に数万人のユーザが殺到することもあり、視聴データの更新が一瞬に集中するため、極端な高頻度参照・高頻度更新になります(開発チームではこれを「突入負荷」と呼んでいます) 。
Redisの導入
様々な最適化を積み重ねた結果、番組タイトルや説明文などの更新頻度の低い情報や、ポイント関連の情報などは従来通りMySQLで管理し、視聴者数のカウントや最新番組一覧などの高頻度で更新される情報はインメモリベースのmemcachedに格納するという構成に落ち着きました。
しかし、この構成にも限界があったのです。memcachedはシンプルなキー・バリューであるが故、巨大な構造化データ(例えば、特定の番組を観た数万人のユーザIDのリスト)を扱うことが難しく、実装できる機能が制限されていました。
また、データ消失の恐れがあるため、視聴集計などの重要なデータを扱う場合には、別途MySQLやファイルにバックアップする機構を用意しなければならない点も問題でした。
これらの課題をクリアしつつ、しかもmemcached並に高速なソリューションを探していた折、まさに「インメモリ」「 永続化」「 データ構造サーバー」の特徴を持つRedisの存在を知り、迷わず導入を決めました。
利用例
2010年の6月の導入以来、システムの至るところに使われていますが、ここではその一端をご紹介します(開発中も含みます) 。
フロントエンド
視聴者数カウンタ
番組タグリスト
番組タグの使用頻度順リスト
カテゴリ別の最新番組リスト
サイトリニューアル時のA/Bテスト集計
バックエンド
セグメント別ユニーク視聴者数集計
番組間の視聴ユーザIDの集合演算による分析システム
リアルタイムイベントログの集約
サブシステム間の更新通知キュー
分散バッチ処理の中間データの集約、ソート機構
図1 ユーザー番組一覧ページにおけるRedis使用箇所(赤枠囲み部)
まとめ
今回は、Redisの概要からニコニコ生放送での導入経緯までを駆け足でご紹介しました。memcachedと比較されることが多いRedisですが、大まかにその違いと利点がお分かりいただけたのではないでしょうか。次回は、Redisのインストールから基本操作までをご紹介します。