Directory
- Basic concepts of caching
- Caching principle
- Cache design
- A comparison between distributed cache Memcache and Redis
- Cache penetration, Cache breakdown, Cache avalanche Solution
- Data consistency
- Using the built-in MemoryCache
- Using Distributed Cache Redis
- Use Stackexchange.redis to encapsulate a Redishelper class yourself
- Reference
Basic concepts of caching
Cache is an important component in distributed system, which mainly solves the performance problem of hot data access in high concurrency and big data scenarios. Provides fast, high-performance data access.
Caching principle
- Writes data to a storage device that reads faster;
- Caches the data to the nearest location of the app;
- Caches the data to the nearest user location.
Cache design
- cache content hotspot data, static resources
- Cache Location CDN, reverse proxy, distributed cache server, native (memory, hard disk)
CDN
: Store HTML, CSS, JS and other static resources;
反向代理
: Static and dynamic separation, caching only the user's request for the passive resources;
分布式缓存
: Cache hotspot data in the database;
本地缓存
: Caches common data such as application dictionaries.
- expiration Policy fixed time, relative time
- synchronous mechanism real-time write, asynchronous flush
A comparison between distributed cache Memcache and Redis
- Data structure: Memcache only supports key value storage, Redis supports more data types, such as key value, hash, list, set, Zset;
- Multithreading: Memcache support multi-threading, Redis support single-threaded, CPU utilization memcache better than redis;
- Persistence: Memcache does not support persistence, and Redis supports persistence (both the snapshot and aof log persistence modes);
- Memory utilization: Memcache High, Redis low (in case of compression is higher than memcache). With simple key-value storage, memcached memory utilization is higher, and if Redis uses a hash structure to do key-value storage, its memory utilization will be higher than memcache due to its combined compression.
- Expiration policy: After Memcache expires, do not delete the cache, will cause the next time to fetch data data problems, Redis has a dedicated thread, clear cache data;
Cache penetration, Cache breakdown, Cache avalanche Solution
- Cache penetration
Cache penetration refers to querying a certain nonexistent data. Because the database is frequently requested, it creates access pressure on the database.
Workaround:
- Data that is null for the result is also cached, but the expiration time of setting it is very short, up to a maximum of five minutes.
There must be no key, using the bitmap filter, to build a large in-the-box, when queried through the bitmap filter.
The Bron filter (Bloom filter) was proposed by Bron in 1970. It is actually a very long binary vector and a series of random mapping functions. The Bron filter can be used to retrieve whether a > element is in a collection. Its advantage is that space efficiency and query time are far more than the general algorithm, the disadvantage is that there is a certain rate of error recognition and removal difficulties
If you want to determine whether an element is in a collection, it is common to think of saving all the elements and then identifying them by comparison. Data structures such as linked lists, trees, and so on are all such ideas. > But with the increase in the number of elements in the collection, we need more storage space and slower retrieval speed (O (n), O (Logn)). There is, however, a data structure called a hash table (also called the ha > sid, Hash tables) in the world. It can map an element to a point in a bit array (bit array) through a hash function. In this way, we just have to see if this point is 1 to know if it's in the collection. This is the basic idea of the Bron filter.
- Cache avalanche
Cache avalanche refers to the same expiration time when we set up the cache, causing the slow existence to fail at some point, and all requests are forwarded to the db,db instantaneous pressure avalanche.
Workaround:
- Control the number of threads that read the database write cache by adding locks or queues. For example, a key allows only one thread to query the data and write the cache, and other threads wait.
- Decentralized cache expiration time, such as when setting the expiration time to add a random number as far as possible to ensure that the cache will not be large area and failure.
- Cache breakdown
Cache breakdown refers to some keys that have an expiration time set, and if these keys may be accessed at a point in time that is too high and concurrently after expiration. The difference between this and the cache avalanche is that there is a key cache, and the former is a lot of keys.
Workaround:
- Use mutex to solve the problem, the popular description is that 10,000 users access, but only one user can get access to the database, when the user to re-create the cache, this time the rest of the visitors because they do not have access to the cache, waiting to go in situ.
Data consistency
Data inconsistency in several cases:
- Database has data, cache no data;
- The database has the data, the cache also has the data, the data is not equal;
- The database has no data and the data is cached.
At present, the most common data caching strategy is the cache aside Pattern, the update cache is the first to store the data in the database, after successful, and then let the cache invalidation.
The reason for this inconsistency under this policy is that only the update database succeeds, but deleting the cache fails.
Solution:
- Retry the deletion of the cache.
- Periodically update the cache in full volume.
- Set the cache expiration time reasonably.
Using the built-in MemoryCache
The ASP. NET Core supports many different caches. Includes memory cache, distributed cache (Redis and SQL Server). Github Open Source Address Libraries for in-memory caching and distributed caching.
IMemoryCache is to store the data in the memory of the Web server.
Call Addmemorycache in configureservices to inject a reference service through a dependency relationship.
public void ConfigureServices(IServiceCollection services){ services.AddMemoryCache(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);}
Creates an IMemoryCache object in the Controller class in the form of a constructor injection.
using Microsoft.Extensions.Caching.Memory;public class ValuesController : ControllerBase{ private IMemoryCache _cache; public ValuesController(IMemoryCache cache) { _cache = cache; }}
- Some common operations
-
Create cache
Set ()
DateTime cacheEntry1 = Datetime.now;var Cacheentryoptions = new Memorycacheentryoptions (). Setabsoluteexpiration (Timespan.fromseconds (3)); _cache. Set ("Cache1", CacheEntry1, cacheentryoptions);
getorcreate ()
getorcreateasync
var cacheentry = _cache. Getorcreate ("cache1", entry = {entry. Setabsoluteexpiration (Timespan.fromseconds (3)); return datetime.now;});
Get Cache
Get()
var cacheEntry = this._cache.Get<DateTime?>("cache1");
TryGetValue()
DateTime cacheEntry;if (!_cache.TryGetValue("cache1", out cacheEntry)){ // Key not in cache, so get data. cacheEntry = DateTime.Now; var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromSeconds(3)); _cache.Set("cache1", cacheEntry, cacheEntryOptions);}
Delete Cache
Remove()
_cache.Remove("cache1");
Other knowledge points
Icacheentry Members:
Key
Cache key
Value
Cached values
AbsoluteExpiration
Absolute expiration time, NULL if the condition is invalid
AbsoluteExpirationRelativeToNow
The absolute expiration time relative to the current time (using a timespan), NULL condition is invalid
SlidingExpiration
Sliding Expiration Time
ExpirationTokens
Provide cache expiration with a custom
PostEvictionCallbacks
Cache Invalidation Callback
Priority
Cache Item Priority (the order of absolute cleanup when the cache is full)
Size
Represents the size of the cached data, which is generally null in the memory cache
How the cache expires
- Absolute expiration (specify expiration at a fixed point in time)
- Sliding expiration (expired if not hit for a length of time, if hit is postponed)
- Expired tokens (custom expiration)
Using Distributed Cache Redis
- Nuget installation Microsoft.Extensions.Caching.Redis
- The
-
Configureservices method adds a service Adddistributedrediscache
public void configureservices ( Iservicecollection services) {services. Adddistributedrediscache (options = options. Configuration = "localhost"; Options. INSTANCENAME = "Instance1"; }); Services. Addmvc (). Setcompatibilityversion (compatibilityversion.version_2_1);}
- Common operations
Rediscache implements the Idistributedcache interface, which provides common add, retrieve, and delete operations.
Get
, GetAsync
using the string key and retrieving the cache entry as byte[] (if found in the cache)
Set
, SetAsync
Use the string key to add an item byte[] form to the cache
Refresh
, RefreshAsync
refreshes the item in the cache according to the key, and resets its adjustable expiration time-out value (if any)
Remove
, RemoveAsync
delete the cache entry by key
var now = DateTime.Now;var cacheValue = System.Text.Encoding.UTF8.GetBytes(now.ToString());var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(3));_cache.Set("cache1", cacheValue, options);_cache.Refresh("cache1");var value = _cache.Get("cache1");var nowString = System.Text.Encoding.UTF8.GetString(value);_cache.Remove("cache1");
Since the Rediscache inherited Idistributedcache interface does not provide some of the advanced features of redis such as hash, List, set, etc.
Use Stackexchange.redis to encapsulate a Redishelper class yourself
A simple Redishelper class based on Stackexchange.redis encapsulation:
Redishelper class
public class RedisHelper{ private readonly RedisOptions _options; private readonly Lazy<ConnectionMultiplexer> _connectionMultiplexer; public RedisHelper(IOptions<RedisOptions> optionsAccessor) { if (optionsAccessor == null) { throw new ArgumentNullException(nameof(optionsAccessor)); } _options = optionsAccessor.Value; _connectionMultiplexer = new Lazy<ConnectionMultiplexer>(CreateConnectionMultiplexer); } public IDatabase GetDatabase() { return _connectionMultiplexer.Value.GetDatabase(); } private ConnectionMultiplexer CreateConnectionMultiplexer() { if (_options.ConfigurationOptions != null) { return ConnectionMultiplexer.Connect(_options.ConfigurationOptions); } else { return ConnectionMultiplexer.Connect(_options.Configuration); } }}
Redisoptions Configuration Class
public class RedisOptions : IOptions<RedisOptions>{ /// <summary> /// The configuration used to connect to Redis. /// </summary> public string Configuration { get; set; } /// <summary> /// The configuration used to connect to Redis. /// This is preferred over Configuration. /// </summary> public ConfigurationOptions ConfigurationOptions { get; set; } /// <summary> /// The Redis instance name. /// </summary> public string InstanceName { get; set; } RedisOptions IOptions<RedisOptions>.Value { get { return this; } }}
Redishelperservicecollectionextensions extension Classes
public static class RedisHelperServiceCollectionExtensions{ public static IServiceCollection AddRedisHelper(this IServiceCollection services, Action<RedisOptions> setupAction) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (setupAction == null) { throw new ArgumentNullException(nameof(setupAction)); } services.AddOptions(); services.Configure(setupAction); services.AddSingleton<RedisHelper>(); return services; }}
Add a service reference inside the Configureservices
public void ConfigureServices(IServiceCollection services){ var redisOptions = Configuration.GetSection("RedisOptions").Get<RedisOptions>(); services.AddRedisHelper(options => { options.Configuration = redisOptions.Configuration; options.InstanceName = redisOptions.InstanceName; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);}
Use in Controller
public class ValuesController : ControllerBase{ private readonly RedisHelper _redisHelper; public ValuesController(RedisHelper redisHelper) { _redisHelper = redisHelper; } // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { _redisHelper.GetDatabase().StringSet("test_key_2", "test_value_2", TimeSpan.FromSeconds(60)); return new string[] { "value1", "value2" }; }}
Some open-source Redis extensions on the Web:
- StackExchange.Redis.Extensions
- Easycaching
Reference
- A detailed description of the caching technology
- The routines for caching updates
- Cache breakdown/Penetration/avalanche
- Cache in ASP.
- Embracing. NET Core series: MemoryCache cache expiration
- Implementing distributed caching using Redis