26
Sep
2018

Redis Cache Implementation using .NET

Introduction to Redis

Redis is an open source (BSD licensed), high-speed, NoSQL, in-memory data structure stores. It’s quick, and it operates entirely in the memory with negligible performance overhead when presenting and corresponding data. Redis is free for both commercial and non-commercial use under the BSD license.

It maintains data structures such as strings, hashes, lists, Sets, sorted sets with range queries, bitmaps, hyper logs and geospatial indexes with radius queries. It also presents built-in support for replication and transactions and has unique support for data persistence.

Redis is a data structure which runs in memory and could be communicated to the server. So when you are looking for fast in-memory data storage system. Redis allows the user to store a vast amount of data without the limitation of relational database unlike MongoDB, MySQL etc. Redis is written in ASCI C.

Redis Cache Implementation using .Net

Why should I use it?

Redis is a genuine and reliable choice if your application requires to store and recover a huge amount of data and when the application is hosted on multiple servers. Redis Cache is very simple to set-up and has relatively much less performance overhead when collecting and regaining data.

The following are listed the benefits of using Redis:

  1. Very easy configuration with any application
  2. Simple Usage
  3. High performance and application caching
  4. Supports lists of data types(hashes, lists, sets etc)
  5. Redis provides Web UI for viewing web content

Steps for Implementation:

 
Step 1:

First it needs to setup Redis server on our premises or we can use Azure or some other services who are providing Redis server. Redis can run on a Linux environment, too.

Step 2:

In the ASP.Net application development, we need to add StackExchange.Redis dll from Nuget package that will be our connector to Redis cache.

using System;
using Newtonsoft.Json;
using StackExchange.Redis;
 
namespace Service.Caching
{
  static class Redis
  {
    static string redisHost = "127.0.0.1";
    static int redisPort = 6397;
    static string redisPassword = "admin123";
    static string redisConnection = string.Format("{0}:{1},password={2}",redisHost, 
    redisPort, redisPassword);
    static string connectionString;
    connectionString= redisConnection + ,syncTimeout=30000,connectTimeout=30000,ssl=False,
    abortConnect=False, allowAdmin=true";        
 
    static Lazy<ConnectionMultiplexer> multiplexer = CreateMultiplexer();
 
    public static ConnectionMultiplexer Connection
    {
      get
      {
        multiplexer.Value.PreserveAsyncOrder = false;
        return multiplexer.Value;
      }
    }
 
    private static Lazy<ConnectionMultiplexer> CreateMultiplexer()
    {
      return new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect
      (connectionString));
    }
 
  }
}

 Step 3:

Create a connection with Redis through the code. The central object in StackExchange.Redis is the ConnectionMultiplexer class in the StackExchange. This is the object that hides away the details of multiple servers. Because the ConnectionMultiplexer does a lot, it is designed to be shared and reused between callers.

The connection to the Redis Cache is managed by the ConnectionMultiplexer class. This class should be shared and reused throughout your client application and does not need to be created on a per operation basis.

In the preceding code snippet, abortConnect is set to false, which means that the call succeeds even if a connection to the Azure Redis Cache is not established.

  • AllowAdmin is set to true as this connection can do some administrator level execution like FlushDB.
  • syncTimeout is set to allow for synchronous operations
  • connectTimeout is set for connect operations

Step 4 :

Get/Set/Delete methods. Redis sets are an unordered collection used for storing strings. One of the great benefits of Redis Sets is that the operation of add, remove, and testing for the existence of items is of constant time of O(1) regardless of the number of items in the Set. An important thing about Sets is when adding the same element multiple times, it will only result in a set having a single copy of the item.

In a way, one does not have to check if an item exists or not before adding. The amount of members you can store in a Set is more than 4 billion, thus one can store quite a few things in them.

Redis GET command is used to get the value stored in the specified key. If the key does not exist, then nil is returned. If the returned value is not a string, an error is returned. For deleting multiple keys we are using FLUSHDB for better performance.

Redis FLUSHDB deletes all the keys of the currently selected DB. This command never fails.

We require around 1k read/write/delete operations in Redis per second. To overcome the timeout exception we used RetryPolicy using RedisCacheTransientErrorDetectionStrategy which helped us to solve timeout issues.

public class RedisCacheManager : ICacheManager, IDisposable
{
   private static IDatabase db;
   private readonly RetryPolicy _retryPolicy;
 
   public RedisCacheManager(ILogger logger)
   {
      db = Redis.Connection.GetDatabase();
      var retryStrategy = new FixedInterval(3,TimeSpan.FromSeconds(2));
      _retryPolicy = new RetryPolicy<RedisCacheTransientErrorDetectionStrategy>(retryStrategy);
   }
 
   public T Get<T>(string key, ref bool hasValue)
   {
      var serializedItem = _retryPolicy.ExecuteAction(() => db.StringGet(key));
      var item = JsonConvert.DeserializeObject<T>(serializedItem);
      if (item == null)
         return default(T);
      return item;
   }
 
   public void Set(string key, object data, int cacheTime)
   {
      var expiresIn = TimeSpan.FromMinutes(cacheTime);
      var serializedItem = JsonConvert.SerializeObject(data);
      _retryPolicy.ExecuteAction(() => db.StringSet(key,serializedItem,expiresIn));
   }
}

Challenges Faced:

  • We have a large number of keys generated in Redis. We have also a large number of operations every second so, we need to perform around 1000 Read/Write/Remove operation.
  • We were facing an issue in connection as all requests require to connect Redis. We used Lazy connection using Connection Multiplexer which helped to have multiple connections.
  • We require to remove keys by a pattern, for example, all keys which starting with particular pattern needs to be deleted together while performing CRUD of product. Redis has some limitation on scanning the keys by a pattern which was creating timeout issues. To overcome this issue for .Net Development services, we distributed keys in different Redis database and while removing by the pattern we use FLUSHDB operation.
  • To perform more than 1000 operation every second, we used Retry Policy which has solved timeout exceptions.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *