こんにちは。LINE Plus CorporationのContents Service Engineering組織でバックエンド開発を担当しているKang Hyun Yangと、Byungchan Leeです。私たちが所属しているContents Server EngineeringではTech Groupという組織を運営しています。Tech Groupでは、さまざまな組織が直面する共通の課題に取り組み、特に大量のトラフィックを効率的に処理する方法を模索してきました。その模索の中で、キャッシュプロセスの高度化を行い、その結果、「req-shield」という内部ライブラリを開発しました。
この記事では、req-shieldを開発した背景と詳細機能を詳しく紹介したいと思います。
一般的にアプリケーションがキャッシュを利用する方法
通常、アプリケーションは以下のようにキャッシュを利用します。
クライアントがリクエストを行うと、そのリクエストに対応するコンテンツがキャッシュにあれば、クライアントはすぐにレスポンスを受け取ります。もしキャッシュに対応するコンテンツがない場合は、バックエンドのデータスト ア(DB、サーバー、ファイルなど)からデータを取得し、キャッシュに保存します(上の図では便宜上「MySQL」と表現されている)。
このような仕組みで、仮にN個のクライアントのリクエストが入っているとしてみましょう。このとき、キャッシュにN個のリクエストに対応するコンテンツがなければ、N個のリクエストはすべてバックエンドデータストアにデータをリクエストし、N個のリクエストがすべて同じ値を求めていたとしても、N回のリクエストがそのまま行われます。さらに、特に何の措置も取らなければ、バックエンドデータストアから受け取ったデータをキャッシュにN回記録する作業も行われます。これはバックエンドストアとキャッシュの両方に負荷を引き起こす原因となり、「Thundering Herd問題」と呼ばれます。
req-shieldの開発は、その「Thundering Herd問題」を解決する方法を模索することから始まりました。
req-shieldのアイデア
この問題を解決するカギは、クライアントからバックエンドデータストアへのリクエストをどのように減らすかです。私たちはキャッシュにヒットするかどうかによって、2つのアイデアを適用できると考えました。
リクエストされたデータがキャッシュに存在しない場合
リクエストされたデータがキャッシュに存在せず、バックエンドストアから取得してキャッシュにロードする必要が ある場合、アプリケーション側でローカルまたはグローバルロック(lock)を使って特定のリクエストだけをバックエンドストアに送ります。他のリクエストは、ロックを取得したリクエストがキャッシュにデータを作成するまで待てば、問題を解決できます。
リクエストされたデータがキャッシュに存在する場合
リクエストされたデータがキャッシュに存在し、クライアントがすぐにレスポンスを受け取る場合にも、将来のための予防措置を取ることができます。例えば、照会しようとするキャッシュのTTL(time to live)が10秒で、クライアントがそのキャッシュを照会した時点でTTLが2~3秒程度残っていれば、キャッシュが期限切れになる前にキャッシュのTTLを更新することです。これにより、一定のトラフィックが入るという前提で、キャッシュが期限切れにならず、まるでキャッシュが継続的に存在しているような効果が期待できます。
上記を時間グラフで比較すると以下のようになります。