igorclauss.de open_in_new

Redis - Improving the caching algorithm by adding a visit counter

Introduction

We implemented a caching system for blog posts with Redis in my last article. In the end we had to confess there are further improvements we can implement. Now we will improve our caching algorithm by adding a mechanism to keep track on visits of cached articles.

Das Redis Logo

Prerequisites

We will continue our work we started in the last article. To follow this article we should have implemented the basics already.

Instructions - Implementing a visitor counter for blog posts

At some point we would like to know how successfull our blog posts are. One indicator for this can be the amount of traffic each post generates, in other words the amount of readers a post had. By adding a visits column to the posts table we can simply increment a counter on each request. For example in the Laravel framework this could look as simple like this:

public function showPost($id)
{
  $post = Post::findOrFail($id); 
 
  $post->visits++;
  $post->save();
 
  return view('blog-post')->withPost($post);
}

We fetch a post with the ID we get from the request, we increment the visits property on this post and save the post, before we return a view. That will result in one extra query to our database. The point is, when we implemented the redis caching algorithm we cut down the database usage, so no more increment will happen.

public function showPost($id)
{
  if ($post = Redis::get('post:' . $id)) {
    return view('blog-post')->withPost(unserialize($post));
  }
 
  $post = Post::findOrFail($id);
 
  // store data into redis for next 24 hours
  Redis::setex('post:' . $id, 60 * 60 * 24, serialize($post));
 
  return view('blog-post')->withPost($post);
}

Let's add a redis key for each post to keep track of the visits. Then we will increment the counter, when the next database query happens, by the value of this key. This technique uses one of Redis benefits, which consists of being able to do atomic operations on the stored data. Also the increment shows us how Redis stores typed data.

public function showPost($id)
{
  if ($post = Redis::get('post:' . $id)) {

    // here we increment the visit count in Redis
    Redis::incr('post:visits:' . $id);

    return view('blog-post')->withPost(unserialize($post));
  }

  $post = Post::findOrFail($id);
 
  if ($visit_count = Redis::get('post:visits:' . $id)) {
    $post->visits =  $post->visits + $visit_count + 1;
  } else {
    $post->visits++;
  }
 
  $post->save();

  // store data into Redis for next 24 hours
  Redis::setex('post:' . $id, 60 * 60 * 24, serialize($post));

  // reset Redis visit counter
  Redis::set('post:visits:' . $id, 0);

  return view('blog-post')->withPost($post);
}

The code above should be pretty self explaining. We check if there is a cached blog post. If there is, we increment the visit counter stored in Redis. If this key does not exist, Redis will create one with the value of 0. Then we return a view with data from the cache.

If there is no post in the cache, we will fetch the database. Then we increment the visits column on the posts table either by the count from redis or by 1. We store the fetched post in the cache and reset the visit counter in Redis. Lastly we return the view with the post model fetched from the database.

By saving the visits in Redis we sacrifice the realtime updates on the database for HTTP response speed. Otherwise we could do the write operation on the unserialized cached model, which would work equally, but would probably be slower.

Further improvements

I'm happy with our implementation of this redis cache & visit counter cache thingy. But I still wish to have full control over which posts to cache. Let's work on this on the next session.

Do you have any questions on my article? Is there anything I missed? What did you liked about the article? Please, tell me and leave a comment below.

Einen Kommentar posten

Kommentare zu diesem Beitrag