How is a .NET Distributed Cache Superior to Key Value Store?How is a .NET Distributed Cache Superior to Key Value Store?
ASP.NET web applications, .NET web services applications, and other .NET server applications need to handle extreme transaction loads without slowing down. And, although their application tier scales linearly, the data storage and database tier does not scale and therefore becomes a bottleneck. As a result, the entire application cannot scale.
Originally, simple in-memory distributed key-value stores like Memcached and later Redis were introduced on Unix/Linux platforms to help resolve this scalability problem. They quickly became quite popular mainly because they provided linear scalability just like the application tiers and removed the database bottleneck.
Limitations in Key Value Stores
But, despite their popularity, these solutions were very simple and basic in nature and didn’t really solve many problems facing real life applications. Some of the areas where these solutions were very weak included:
- Lack of high availability
- Lack of intelligent ways of keeping the cache fresh
- Lack of SQL searching
- Lack of server-side caching code (e.g. Read-through)
For example, high availability was so poor in Memcached that third parties started developing high availability “fix ins” for it. But, the underlying architecture was just not designed for high availability and therefore these solutions remained quite limited in nature.
Redis had the same high availability issues but later re-architected the product to incorporate some high availability features like data replication and some failover support.
But, the other areas are still big holes in all key-value store products like Memcached and Redis. This is where distributed cache solutions came to the rescue.
.NET Distributed Cache as 2nd Generation Key Value Store
A .NET distributed cache like NCache on the other hand was designed from day one to address all the above mentioned limitations and more. So, in essence, NCache is a 2nd Generation to the original key value stores like Memcached and Redis. NCache is a popular 10-year old distributed cache for .NET.
Dynamic Cache Cluster & Data Replication
A distributed cache like NCache has a self-healing dynamic cache cluster that pools all the CPU and memory resources from all cache servers in the cluster. At the same time, NCache provides a variety of caching topologies with different data distribution and replication strategies. This allows NCache to linearly scale without compromising on high availability. And, even if a cache server goes down, no data loss occurs, the cache cluster continues running, and all the applications using the cache also continue without any interruptions.
Keeping Cache Fresh
Another area where a distributed cache like NCache shines is keeping the data fresh and always consistent with the database. NCache does this through a variety of features including expirations, event driving SqlDependency, polling based DbDependency, and support for CLR procedures for relational databases. Expirations work just like key value stores but SqlDependency and DbDependency allow NCache to synchronize the cache with any changes in the database for the related data. And, CLR stored procedures allow you to directly update the cache from your SQL Server database when the corresponding data changes.
All of this means that even if a third party application changes data in the database, NCache immediately updates itself accordingly. And, the benefit of all this is that you can now cache almost all your application data instead of only caching read-only data. And this provides huge and real performance and scalability gains.
Searching Cache with SQL
So, when you’re able to cache almost all your data due to “keep cache fresh” features, you run into the issues of not being able to find data easily if the only mechanism is key-value. But, if you could search data based on attributes, then a distributed cache like NCache becomes as easy to search as a database. NCache provides you SQL and LINQ searching for this purpose.
In addition to SQL searching based on object attributes, you can assign group/subgroup, tags, and named tags to cached items and later include them in your SQL searching. Below is an example of SQL searching in C#. For example;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using Alachisoft.NCache.Runtime;
using Alachisoft.NCache.Runtime.Exceptions;
using Alachisoft.NCache.Web.Caching;
public void SearchDataUsingSQL()
{
//...
Cache cache = NCache.InitializeCache("myparitionreplica");
string query = "SELECT this.Category, "
+ "MAX(Prod.Product.ProductID) "
+ "WHERE this.Category = ? "
+ "GROUP BY this.Category";
Hashtable values = new Hashtable();
values.Add("Category", 4);
ICacheReader reader = cache.ExecuteReader(query, values);
if (reader.FieldCount > 0)
{
while (reader.Read())
{
//you can get value through the field name...
object category = reader.GetOrdinal("Category");
//perform operations
}
}
reader.Close();
return data;
}
|
Server-Side Code
Finally, there is server-side code like Read-through, Write-through, Custom Dependency, and Cache-Loader that is very useful for you. This code is developed by you but is called by the distributed cache and runs in the cache cluster. With the help of this code, you can simplify your applications and move a lot of commonly used code to the caching tier.
For example, NCache calls your Read-through handler when your application asks for some data that is not in the cache and the application tells NCache to call Read-through in that case. Similarly, you can combine Read-through with expirations and database synchronizations to auto-reload the cached item instead of removing it from the cache.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using Alachisoft.NCache.Runtime.Caching;
using Alachisoft.NCache.Runtime.DatasourceProviders;
using Alachisoft.NCache.Runtime.Dependencies;
public class SampleReadThruProvider : IReadThruProvider
{
public void Init(IDictionary parameters, string cacheId)
{
// Create SQL connection and other initializations at the server side
}
//Responsible for loading an item from the external data source.
public void LoadFromSource(string key, out ProviderCacheItem cacheItem)
{
//where LoadFromDataSource is the dummy method to load data from data source.
object value = LoadFromDataSource(key);
//Attach SQL dependency to your object
string query = "SELECT ProductID FROM dbo.Products WHERE ProductID = 1001";
cacheItem = new ProviderCacheItem(value);
cacheItem.Dependency = new SqlCacheDependency(connectionString, query);
//Set expirations
cacheItem.SlidingExpiration = new TimeSpan(0, 5, 0);
//Indicates whether item should be reloaded on expiration if
//ReadThru provider is specified.
cacheItem.ResyncItemOnExpiration = true;
}
public void Dispose() { //... }
}
|
Write-through works in the same fashion as Read-through but for updates. It updates your database when your application updates the cache. And, if you prefer, Write-behind updates the database asynchronously even though the cache gets updated synchronously. Finally, Cache Loader is called when the cache is started so you can pre-load it with the desired data.
Conclusion
As you can see, NCache, an open source .NET distributed cache, offers a lot more power to your cache than a simple Redis key-value store or Memcached.