LaravelでDBへのアクセスを減らすためにRedisキャッシュを使う



Laravel使いの人はredisを使っていますか?

Redisはメモリーにデータを乗せる事が出来るKey-Valueストアです。

Socket.ioと連携したり、セッション管理、QueueクラスでJob登録に使うのはよく見かけますが、
ブログやニュースサイト等の静的に近いサイトにも使い所があります。

Laravel Redis

ブログ等の記事を取得する方法としては、
-> データベースから記事を取得
-> 表示
レポジトリーパターンを使うと以下のようなコードが多いと思います。
( * コントローラー上でレポジトリクラスを呼び出す想定 )

// app/Repositories/ArticleRepository.php
use app/Model/Article;

class ArticleRepository
{
  public function getPost($id)
  {
    return Article::where('id', $id)
              ->where('active', 1)
              ->where('published_at', '<', date("Y-m-d H:i:s"))
              ->get();
  }

}

以上の様にすると、記事ページにアクセスされる度、
データベースにアクセスすることになります。

ブログ等は一度投稿すると、頻繁にアップデートしないので、
Redisを使って記事データーをメモリに乗せてデータベースへの負担を減らしてみましょう。

// app/Repositories/ArticleRepository.php
// redisのインストール、predisパッケージ、config/database.php内で設定が必要です
use app/Model/Article;

class ArticleRepository
{
  /**
  * @param $id *記事のid
  */
  public function getPost($id)
  {
    return json_decode(\Redis::get("{$id}-article"), true)
        ?: value(function() use ($id) {

          $result = Article::where('id', $id)
                      ->where('active', 1)
                      ->where('published_at', '<', date("Y-m-d H:i:s"))
                      ->toJSON();
          // Redisに 記事id-articleという名で登録
          // exp. 1-article, 103-article ... etc
          // redis内でpost等の要素を作成することも出来ます
          \Redis::set("{$id}-article", $result);

          // 30分間のキャッシュをセット
          \Redis::expire("{$id}-article", 1800); // 30 min

          // 登録した記事データを配列に変換して返す
          return json_decode(Redis::get("{$id}-article"), true);
      });
  }

}


結果を取得するだけなのにコードが少し長く感じるかもしれませんね。

上のコードがしていることは、、

  1. Redisから"{$id}-article"とマッチしたkeyのデーターを取得
  2. もしデータがRedis上にkeyが無ければDBから結果を取得
  3. Redisにデータを登録 *{$id}-article  **JSON型に変換しています
  4. Redisのexpireを使ってキャッシュ時間を指定 *サンプルでは30分間
  5. 1と同じ  (*Redisから"{$id}-article"というkeyのデーターを取得)


上記の例の様にすると一分間に何万回のアクセスが来ようが
メモリ上のRedisからデータを取得しているので、

データベースへのアクセスが30分に1回で済むことになります。

少し変更を加えていますが、このサイトで動いているコードは下記の様な感じです。
( *Lumen上で動いており、Query-Builder、native-redisライブラリを使っています [hhvmでは標準でredisライブラリが搭載] )

class ArticleRepository
{

  public function getArticle($id)
  {
      return json_decode($this->redis->get("{$id}-article"), true)
          ?: value(function () use ($id) {

              $result = \DB::table($this->table['article'])
                  ->select(
                      'id',
                      'category_id', 'category_name', 'category_icon',
                      'tag_id_1', 'tag_name_1',
                      'tag_id_2', 'tag_name_2',
                      'tag_id_3', 'tag_name_3',
                      'page',
                      'title', 'keywords', 'description',
                      'content', 'content_2', 'content_3',
                      'image', 'twitter_hash',
                      'opinion',
                      'published_at'
                  )
                  ->where(function($query) use ($id) {
                    $query->whereNull('deleted_at')
                          ->whereNotNull('published_at')
                          ->where('active', 1)
                          ->where('published_at', '<', date("Y-m-d H:i:s"))
                          ->where('id', $id);

                  })->get();

              $this->redis->set("{$id}-article",
                  json_encode($result, JSON_UNESCAPED_UNICODE));

              $this->redis->expire("{$id}-article", CACHE_TIME);

              return json_decode($this->redis->get("{$id}-article"), true);
          });

  }

}


今回はブログの記事を例にして説明しましたが、メニュー等を動的に生成する場合にも使えるので、
「データベースへのアクセスを減らしたい」とお考えの人はゼヒ×2参考にしてください!

 

この記事のカテゴリ
プログラミング

この記事のタグ