読者です 読者をやめる 読者になる 読者になる

UZABASE Tech Blog

株式会社ユーザベースの技術チームブログです。 主に週次の持ち回りLTやセミナー・イベント情報について書きます。

荒ぶるRedisとNewsPicks

f:id:uzabase:20141217183314j:plain


NewsPicks の開発を担当している杉浦です。

NewsPicksはおかげさまでユーザ数が20万を突破しました。
サービスが順調に成長するということは大変にうれしいことなのですが、エンジニアとしては負荷との戦いになったりします。我々も例に漏れず日々、負荷との戦いを強いられています。


NewsPicksの機能面の特長として次の2つがあります。
・フォローしているユーザのPickが自分のタイムラインに集約される
・各カテゴリで話題になった記事を閲覧できる

これらの機能を高速に処理・実現するためにRedisを採用しているのですが、
ユーザ数の増加による負荷増加によって問題が発生するようになりました。

本記事では、
・ユーザ数が増える中でRedisにどのような問題が発生したか
ソースコードを読みながら問題の原因を考える
・そして、どのような対応を行っているか

を共有したいと思います。

どのような問題が発生したか

主に2つの問題が発生しました。

ピーク時のレスポンスタイムが遅くなる

NewsPicksは朝の8時にその日の注目ニュースをPush通知で送信します。その直後が最大のピークタイムになり、平常時の10倍程度のリクエストを処理する必要があります。ピークタイムに画面表示のレスポンスタイムが悪化するという問題が発生しました。

夜間バッチ実行時にslaveとのレプリケーションが切れる

夜間にRedisに保持している古いタイムラインデータを削除するというバッチが実行されるのですが、バッチによる大量のデータ更新が発生しレプリケーションタイムアウト値を越えてしまい、レプリケーションが切れるという障害が発生しました。


ソースコードを読みながら問題の原因を考える

なぜこのような問題が発生するようになったのでしょうか。
原因を知るにはRedisのアーキテクチャを理解する必要があります。そして、ソフトウェアのアーキテクチャを理解する近道はいつの時代もソースコードを読むことです。ということで、redis-2.8.17をダウンロード して読んでみました。

 

f:id:uzabase:20141217183324p:plain


ファイル数が少なくシンプルなソフトウェアということがわかります。
理解をしやすくするために、おおまかな機能ごとに整理します。

f:id:uzabase:20141217183355p:plain



メインプログラムの redis.c から ae.c を読み進めると、RedisはI/O戦略としてイベントループモデルを採用していることがわかります。

f:id:uzabase:20141217183525p:plain

イベントの発火時に各コマンドハンドラーが呼ばれて処理が進められています。

f:id:uzabase:20141217183601p:plain


イベントループの詳細については TheC10kProblem を参照してください。
2006年の古い記事ですがよくまとまっていて、今読んでも本質的な部分は変わっていません。

イベントループモデルのメリットは以下が挙げられます。
  • 処理ごとにスレッド/プロセスを用意しないので使用メモリ量を抑えられる
  • スレッド/プロセス切り替えのコンテキストスイッチのオーバヘッドが少ない
  • ノンブロッキングI/Oを合わせて採用することで、I/O待ち時間に他の処理が行われる
逆に、デメリットは以下のようなものがあります。
  • ループの中に遅い処理が入ると、後続の処理が遅れる
  • (Redisの場合は) 1スレッドでループ処理を行うので、マルチコアのサーバの場合に全てのCPUリソースを使い切らない

Redisはデータを全てメモリ中に持つため、高速に処理を行いますが、
それでもRedisの利用度が上がってくると、Redisの能力を越える多量の読み書きが実行される時がやってきます。
そして、Redisはイベントループモデルの特性から、閾値を越えると一気に処理遅延が発生します。

NewsPicksが遭遇した問題はまさにこの事象で、あるタイミングを境に突然問題が発生するようになりました。
システムのアーキテクチャを考える際に負荷に合わせてタイミング良くスケールアウトしていくことができる仕組みを作ることが大切です。
なお、Nginx、Node.jsも同様にイベントループを採用しているので、同じ考え方を適用できると思います。

NewsPicksではどのような対応を行っているか

現在、データのシャーディングと、垂直・水平に処理リクエストを分散をするように対応を進めています。
タイムラインデータについては、一定のユーザ数毎にデータ保存するサーバを分離しています。一般的にユーザパーティショニングによる垂直分散と呼ばれる手法です。
ランキングデータについては読み取りリクエストをslaveに分散させるといった水平分散の対応を行っています。


NewsPicksでは一緒に負荷と戦ってくれるエンジニアを募集しています!
サービスの成長を肌で感じられるやりがいのある仕事です(笑
興味をお持ちいただいた方はWantedlyなどからご連絡ください!