Let’s try an exercise. Try to remember any memories about your childhood. Now try to remember the same memory again after some time. You shall be able to remember it much quicker the second time. This happens because as soon as you recalled the memory the first time, your brain saved this information in your recent memory.
Caching works exactly the same way. Cachingenables us to store data in memory for quick access. When the data is needed again, applications can fetch the data from the cache instead of fetching it from the source. In addition, caching can also make data available when the data source is unavailable. Therefore, caching is one of the most popular performance optimization techniques which not only saves time but also increases the overall performance of an application or website.
There are two main areas for implementing caching mechanism
1. Application Caching
2. Website caching
Application cache is used to store and fetch web resources that are fetched from the web server in the first request. Getting resources from the cache is more efficient and faster than getting them from the source. This means it can be loaded instantly as no retrieving is needed and the resource will be ready to use.
In application caching, we can use the SQLDependency Class which establishes a relationship between an item stored in an ASP.NET application’s cache object and either a specific SQL Server database table or the results of a SQL query.
We can initialize dependency like:
SqlDependency = new SqlCacheDependency("DB_Name", "Column_name");
While using this class there is a possibility of
DatabaseNotEnabledForNotificationException
therefore, we need to implement try and catch blocks with the code to handle exceptions.
The CacheDependency class monitors the dependency relationships so that, when any of the dependencies change, the cached item associated with it will be automatically removed. We can implement this class as:
CacheDependency dep = new CacheDependency(fileName, dt);
cache.Insert("key", "value", dep);
if (dep.HasChanged)
{
Response.Write("The dependency has changed and the item will be removed");
}
2. Website caching means the resources from site sources are fetched in the first request and stored in the cache.
There are two main types of caching mechanisms
1. Client-side Caching
When a user visits a site for the first time, the browser stores items like CSS files and images for a specific time. Browser caching allows these saved items to be immediately available upon the next request, rather than placing a new request which needs to be sent back to the server. It will reduce the number of requests and results in faster web page loading time and improves the user experience. Browser caching is useful for reducing download time for static content like CSS, js, and images.
We can also use Local storage which enables us to store data locally in a browser. This data has no expiration date and will persist even after the browser window is closed. The following are some methods for using local storage:
- setItem() — Add key and value to local storage
- getItem() — Retrieve a value by the key
- removeItem() — Remove an item by key
- clear() — Clear all storage
For example, we can use local storage in Javascript like:
localStorage.setItem("key", value);
localStorage.removeItem(“key”);
localStorage.getItem(“key”)
localStorage.clear()
2. Server-Side Caching
This type of caching can serve many visitors from a centralized cache whenever a user visits the website. This helps us to reduce the load on the server and improves web page loading time for users. This type of cache acts on behalf of the web server and serves the users by providing them the required data without hitting app servers and database servers.
Caching is implemented through “Cache-Control” which is an HTTP header directive which is sent out in every instance of the HTTP response header. Although caching is helpful in many ways but, for security reasons, any sensitive data such as history and user account information should never be cached.
ASP.NET provides the following different types of caching :
Output Caching
Rendering a web page may involve some complex operations like accessing the database and initializing complex controls. Output caching allows us to bypass the round trip to the server, by caching data in the memory. It stores a copy of the completely rendered HTML page or part of the page which is sent to the client from the server. When the client requests for this page next time, instead of fetching the page from the server, the cached copy of the page is used to save time.
Syntax for implementation of Output Cache directive is:
<%@ OutputCache=" " Duration="10" VaryByParam="*" %>
The option VaryByParam helps you to specify the variables that would require a new cache entry. Other possible options include VaryByCustom, VaryByHeader and Location.
Data Caching
Data caching means caching data from a data source. When the cache expires fresh data is obtained from the data source and the cache is refilled.
The main aspect of data caching is caching the data source. These controls are derived from the abstract class DataSourceControl and have the following properties for implementing caching:
- CacheDuration – Sets the number of seconds for which caching will be done
- CacheExpirationPolicy – Defines the cache behavior when the cache has expired.
- EnableCaching – Specifies whether to cache data or not.
Object Caching
Object caching is to cache the objects on a page, such as data-bound controls. The cached data is stored in the server memory. It provides more flexibility than other caching techniques. The object can be of any type – a data type, a web control, a class or database object and any item can be added to the cache by assigning a key name like:
Cache["key"] = item;
REDIS Cache
Redis is an open-source NoSQL, high-speed memory data structure store. It is quick and it resides entirely in the memory with very small performance overhead. It is free for both commercial and non-commercial use under the BSD license.
It maintains data structures such as Sets, hashes, lists, strings with range queries, bitmaps, and geospatial indexes. It also has built-in support for replication, transactions, and also data persistence.
Redis allows the user to store a huge amount of data without the limitation of relational databases. Redis is written in ANSI C.
Redis is a reliable choice if your application requires to store and recover a huge amount of data and if the application is hosted on multiple servers.
The following are the benefits:
- High performance and application caching
- Simple Usage
- Very easy configuration with any application
- Supports lists of data types(hashes, lists, sets, etc)
- It provides Web UI for viewing web content
In order to install Redis, we need Cloud or Dedicated Server. The hardware requirements include 4 core CPU,16GB RAM, 1GBps Network Connection, and 2 storage devices.
Integration of Redis cache using C#.Net
Step 1: We need to download NuGet package StackExchange.Redis which will be our connector to Redis cache
public class MyRedisConnectorHelper
{
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 multiplexer = CreateMultiplexer();
public static ConnectionMultiplexer Connection
{
get
{
multiplexer.Value.PreserveAsyncOrder = false;
return multiplexer.Value;
}
}
private static Lazy CreateMultiplexer()
{
return new Lazy(() => ConnectionMultiplexer.Connect
(connectionString));
}
}
}
Step 2: We can use the Redis Cache Manager as:
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(4, TimeSpan.FromSeconds(3));
_retryPolicy = new RetryPolicy(retryStrategy);
}
public T Get(string key, ref bool hasValue)
{
var serializedItem = _retryPolicy.ExecuteAction(() => db.StringGet(key));
var item = JsonConvert.DeserializeObject(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));
}
}
Another caching option we can use is Memcached. It could be preferable when caching small and static data, such as HTML code snippets. Its internal memory management is more efficient in the simplest use cases because it consumes less memory for metadata. Strings are ideal for storing data that is only read, because strings require no processing. String is the only data type supported by Memcached.
Advantages of Redis over Memcached
1. Data Types Supported:
Memcached supports data records of the simple key-value structure. The data types supported by Redis include String, List, Hash, Set, and SortedSet.
2. Memory Management Scheme
The unique thing about Redis is that all of the data storage is not in memory. Redis may swap out values that are no longer in use. These values are then made persistent into the disk and are erased from the memory. This feature of Redis can maintain much more data than system memory capacity. Memcached has a high memory management efficiency but its major defect is that it causes space waste.
3. Support of data persistence
Redis supports memory data persistence, whereas Memcached does not support the data persistence operation.
Redis with Java application:
To use Redis with Java we need to download the jar named jedis.jar and include it into the classpath.
We also need Jedis which is a client library in Java for implementing a Redis data store. It is small, fast, and fully compatible with Redis 2.8.x and above.
Now we will see how we can use Redis in-memory data store and Jedis client library in a Java application :
Step 1: We need to add maven dependency for Jedis client library like:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
<type>jar</type>
</dependency>
Step 2: We can write a simple Java application to connect to a Redis server like:
package com.eduonix.redis.example
import redis.clients.jedis.Jedis; public class RedisJavaExample { public static void main(String[] args) { //Connect to Redis server using localhost Jedis jedis = new Jedis("localhost"); System.out.println("Connection is successful"); System.out.println("Getting response: " + jedis.ping()); } }
Step 3. Now we will store a value in Redis cache as:
package com.eduonix.redis.example
import redis.clients.jedis.Jedis;
public class RedisCacheExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
System.out.println("Connection successful");
jedis.set("Test", "Test data 1");
//Retrieve the value from the cache
System.out.println("Stored value: "+ jedis.get("Test"));
}
}
String is the most common value stored in Redis cache and it is stored as a key-value pair.
Conclusion:
We all know that performance is a key factor when it comes to websites for providing the best experience to end-users. Caching helps with the same by acting as a mediator between server and end-user by providing data on demand quickly. There are various caching mechanisms that can be used to speed up websites or applications. Although caching may seem like an afterthought for small applications, it is very crucial for complex applications.