ニコニコ生放送に見る Redis 活用ノウハウ

第3回 RedisによるWebアプリケーション開発(1)

この記事を読むのに必要な時間:およそ 4 分

前回までに,Redisの概要と,コマンドラインクライアントによる基本的なコマンド操作を見てきました。今回からは,Redisのデータ型を使ったアプリケーション開発について,簡単なサンプルコードと,ニコニコ生放送での事例を交えて紹介していきます。

クライアントライブラリを用いた開発

WebアプリケーションからRedisにアクセスするには,言語ごとのクライアントライブラリを導入することになります。公式サイトにライブラリがリストアップされていますので,各々の環境に合ったものを探してください。

ニコニコ生放送では,PHPによる開発の場合にはphpredisを,Javaで実装している検索サーバーなどではJedisJRedisを使っています。

今回から掲載するサンプルコードでもこれらのライブラリを使っていきますので,他のライブラリを導入される方はそれぞれ対応するAPIに置き換えてご覧ください。

LIST型の構造

RedisのLIST型は,文字列要素のリストを保持することができます。一般的なプログラミング言語が備えている配列やリストといったデータ構造と同様に,要素の追加・削除,範囲を指定した取得,ソートなどの操作がコマンドとして用意されています。

LIST型の内部構造は連結リストであるため,リストの先頭と末尾への操作は高速ですが,インデックスを指定したアクセスは,その位置に比例した処理時間がかかります。したがって,LIST型を使ったアプリケーションロジックで最大限の性能を得るには,アクセスが先頭と末尾に対して行われるよう考慮しておく必要があります。

Redisのコマンドリファレンスには各コマンドの時間計算量が記載されていますので,これを参考に最適なコマンドを選択してください。

LIST型が有効なケース

前述の特徴から,開発においてLIST型がマッチするのは次のようなケースだと考えられます。

  • 要素の追加が高頻度に発生する
  • 要素が時系列(追加順)に表示される
  • 最近追加された要素にアクセスが集中する

例えば,Twitterのタイムラインや,ソーシャルメディアでのメッセージ受信箱,ユーザーのアクティビティログのような,タイムセンシティブな情報を格納するのに向いているといえます。

LIST型に向かないケース

連結リストであるため,先頭と末尾以外の更新が頻発する場合に不利になります。このため,リストに格納するのは主キー(等の不変情報)だけにしておき,その他の付帯情報(タイトルやデスクリプション)は,別途,RedisのハッシュやMySQLに格納しておくことをおすすめします。それらをリストに含めてしまうと,更新が発生した際にリストから該当の情報を探し出さなくてはならず,更新コストが高くつくからです。

ニコニコ生放送での利用例

ユーザー生放送一覧ページ

ニコニコ生放送システムでのLIST型の応用として代表的なのが,ユーザー生放送一覧ページです。ユーザーによる放送はピークタイムで分間数百番組が作成されるため,なるべく高頻度で内容を更新する必要があります。Redis導入以前は,バッチ処理にてMySQLから数千番組のリストを構築し,ページ数分に分割してmemcachedに載せるという手法を取っていました。現在はリスト全体をRedisのLIST型に載せることでバッチ処理の高速化と更新頻度の向上を図っています。

図1 ニコニコ生放送のユーザー生放送一覧(赤枠囲み部)

図1 ニコニコ生放送のユーザー生放送一覧(赤枠囲み部)

リアルタイムログ機能

リアルタイムログ機能は,フロントウェブサーバー群で発生したイベント情報(エラーやシステムアラート,デバッグメッセージ)のメッセージをRedisのリストに集約し,管理用ウェブサーバーの画面で一元的かつリアルタイムに監視する機能です。この種のイベント情報は集中的かつ大量に発生する可能性があるため,高頻度の追加に耐えうるLIST型の特性を生かしたものとなっています。

図2 リアルタイムログ機能の構成

図2 リアルタイムログ機能の構成

リストの構築とページネーションの実装

あるコンテンツのページネーション機能つきの一覧ページを構築するコードを例として,RedisのLIST型の操作について説明します。

リストの構築

要素追加はLPUSH/RPUSHコマンドによって行います。LPUSHはリストの先頭に,RPUSHは末尾に要素を追加します。

図3 LPUSHコマンドとRPUSHコマンド

図3 LPUSHコマンドとRPUSHコマンド

次のコードは,引数としてコンテンツIDをひとつだけ受け取り,リストの先端に追加します(分かりやすさのため,エラーハンドリング等は省略しています)。

<?php
 function contents_list_push($id) {
   $redis = new Redis();
   $redis->connect('localhost', 6379);
   $redis->lpush('contents_list', $id);
   $redis->close();
 }

複数の要素を一度に追加する場合には,パイプラインを使うことで,リクエストとレスポンスの往復にかかる時間を削減できます。

<?php
 
 function contents_list_push_array($idarray) {
   $redis = new Redis();
   $redis->connect('localhost', 6379);
 
   $pipe = $redis->multi(Redis::PIPELINE);
   foreach ($idarray as $id) {
     $pipe->lpush('contents_list', $id);
   }
   $pipe->exec();
   $redis->close();
 }

phpredisでは,multiメソッドの引数でRedis::PIPELINEを指定することでパイプラインを作成できます。無指定の場合,MULTI/EXECの動作になります。

著者プロフィール

小野侑一(おのゆういち)

株式会社ドワンゴ ニコニコ生放送システムリーダー。

ニコニコ生放送での開発経験を通して,ウェブをリアルタイム化する技術の重要性に着目。その成果としてリアルタイムタグ検索やRedisを活用したリアルタイム視聴者集計システムを開発。現在は全文検索のリアルタイム化に注目している。

Twitter@synk

コメント

コメントの記入