缓存的基本概念

缓存是分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。

缓存原理

  • 将数据写入到读取速度更快的存储设备;
  • 将数据缓存到离应用最近的位置;
  • 将数据缓存到离用户最近的位置。

缓存设计

  • 缓存内容 热点数据,静态资源
  • 缓存位置 CDN,反向代理,分布式缓存服务器,本机(内存,硬盘)
    • CDN:存放HTML、CSS、JS等静态资源;
    • 反向代理:动静分离,只缓存用户请求的静态资源;
    • 分布式缓存:缓存数据库中的热点数据;
    • 本地缓存:缓存应用字典等常用数据。
  • 过期策略 固定时间,相对时间
  • 同步机制 实时写入,异步刷新

分布式缓存 Memcache 与 Redis 的比较

  • 数据结构:Memcache只支持key value存储方式,Redis支持更多的数据类型,比如Key value、hash、list、set、zset;
  • 多线程:Memcache支持多线程,Redis支持单线程;CPU利用方面Memcache优于Redis;
  • 持久化:Memcache不支持持久化,Redis支持持久化(快照和AOF日志两种持久化方式);
  • 内存利用率:Memcache高,Redis低(采用压缩的情况下比Memcache高)。使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcache。
  • 过期策略:Memcache过期后,不删除缓存,会导致下次取数据数据的问题,Redis有专门线程,清除缓存数据;

缓存穿透,缓存击穿,缓存雪崩解决方案

  • 缓存穿透

    缓存穿透是指查询一个一定不存在的数据。由于会频繁的请求数据库,对数据库造成访问压力。

    解决方法:

    • 对结果为空的数据也进行缓存,不过设置它的过期时间会很短,最长不超过五分钟。
    • 一定不存在的key,采用布隆过滤器,建立一个大的Bitmap中,查询时通过该bitmap过滤。

      布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一> 个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难

      如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. > 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈> 希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。

  • 缓存雪崩

    缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

    解决方法:

    • 通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
    • 分散缓存失效时间,比如在设置过期时间的时候增加一个随机数尽可能的保证缓存不会大面积的同时失效。
  • 缓存击穿

    缓存击穿是指对于一些设置了过期时间的key,如果这些key可能会在过期后的某个时间点被超高并发地访问。这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

    解决方法:

    • 使用互斥锁来解决问题,通俗的描述就是,一万个用户访问了,但是只有一个用户可以拿到访问数据库的权限,当这个用户拿到这个权限之后重新创建缓存,这个时候剩下的访问者因为没有拿到权限,就原地等待着去访问缓存。

数据一致性

数据不一致的几种情况:

  • 数据库有数据,缓存没有数据;
  • 数据库有数据,缓存也有数据,数据不相等;
  • 数据库没有数据,缓存有数据。

目前比较常用的数据缓存策略的是Cache Aside Pattern,更新缓存是先把数据存到数据库中,成功后,再让缓存失效。
这种策略下不一致产生的原因只有更新数据库成功,但是删除缓存失败。

解决方案:

  1. 对删除缓存进行重试.
  2. 定期全量更新缓存。
  3. 合理设置缓存过期时间。

使用内置 MemoryCache

ASP.NET Core 支持多种不同的缓存。包括内存缓存,分布式缓存(Redis 和 SQL Server)。Github 开源地址 Libraries for in-memory caching and distributed caching.

IMemoryCache是把数据存储在Web服务器的内存中。

  1. 在 ConfigureServices 中调用 AddMemoryCache 通过依赖关系注入引用服务。

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddMemoryCache(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
  2. 在控制器类中用构造器注入的方式创建 IMemoryCache 的对象。

    using Microsoft.Extensions.Caching.Memory;
    
    public class ValuesController : ControllerBase
    {
    private IMemoryCache _cache; public ValuesController(IMemoryCache cache)
    {
    _cache = cache;
    }
    }
  3. 一些常用操作

    • 创建缓存

      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()

      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);
      }
    • 删除缓存

      Remove()

      _cache.Remove("cache1");
  4. 其他知识点

    ICacheEntry成员:

    • Key 缓存key
    • Value 缓存值
    • AbsoluteExpiration 绝对过期时间,为null则条件无效
    • AbsoluteExpirationRelativeToNow 相对当前时间的绝对过期时间(使用TimeSpan),为null条件无效
    • SlidingExpiration 滑动过期时间
    • ExpirationTokens 提供用来自定义缓存过期
    • PostEvictionCallbacks 缓存失效回调
    • Priority 缓存项优先级(在缓存满载的时候绝对清除的顺序)
    • Size 代表缓存数据的大小,在内存缓存中一般为null

    缓存过期的方式

    • 绝对到期(指定在一个固定的时间点到期)
    • 滑动到期(在一个时间长度内没有被命中则过期,如果命中则顺延)
    • 到期Token(自定义过期)

使用分布式缓存 Redis

  1. Nuget 安装 Microsoft.Extensions.Caching.Redis

  2. ConfigureServices 方法里面添加服务 AddDistributedRedisCache

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddDistributedRedisCache(options => {
    options.Configuration = "localhost";
    options.InstanceName = "Instance1";
    }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
  3. 常用操作

    RedisCache 实现了 IDistributedCache 接口,提供了常用的添加、检索和删除操作。

    • GetGetAsync 采用字符串键并以byte[]形式检索缓存项(如果在缓存中找到)
    • SetSetAsync 使用字符串键向缓存添加项byte[]形式
    • RefreshRefreshAsync 根据键刷新缓存中的项,并重置其可调过期超时值(如果有)
    • RemoveRemoveAsync 根据键删除缓存项
    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");

    由于自带的 RedisCache 继承 IDistributedCache 接口并没有提供 Redis的一些高级特性比如Hash, List, Set等。

使用 Stackexchange.Redis 自己封装一个 RedisHelper 类

基于Stackexchange.Redis封装一个简单RedisHelper类:

RedisHelper 类

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 配置类

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 扩展类

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;
}
}

在 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);
}

在 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" };
}
}

网上一些开源的Redis扩展:

参考

ASP.NET Core 中的缓存的更多相关文章

  1. ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

    .NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...

  2. ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存

    分享 最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了. 背景 在项目开发的 ...

  3. C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入

    C# 嵌入dll   在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...

  4. ASP.NET Core中使用Cache缓存

    ASP.NET Core中使用Cache缓存 缓存介绍: 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性. 缓存对不经常更改的数据效果最佳. 缓存生成的数据副本的返回速度可以比从原始 ...

  5. ASP.NET Core中的Http缓存

    ASP.NET Core中的Http缓存 Http响应缓存可减少客户端或代理对web服务器发出的请求数.响应缓存还减少了web服务器生成响应所需的工作量.响应缓存由Http请求中的header控制. ...

  6. ASP.NET Core教程:ASP.NET Core中使用Redis缓存

    参考网址:https://www.cnblogs.com/dotnet261010/p/12033624.html 一.前言 我们这里以StackExchange.Redis为例,讲解如何在ASP.N ...

  7. 在ASP.NET Core中使用百度在线编辑器UEditor

    在ASP.NET Core中使用百度在线编辑器UEditor 0x00 起因 最近需要一个在线编辑器,之前听人说过百度的UEditor不错,去官网下了一个.不过服务端只有ASP.NET版的,如果是为了 ...

  8. ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态

    原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...

  9. ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】

    本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...

随机推荐

  1. mdadm详细使用手册

    1. 文档信息 当前版本 1.2 创建人 朱荣泽 创建时间 2011.01.07 修改历史 版本号 时间 内容 1.0 2011.01.07 创建<mdadm详细使用手册>1.0文档 1. ...

  2. Send or receive files via Xshell

    1. install lrzsz $ sudo apt-get install lrzsz 2. If you want to send file from your pc to pi, just d ...

  3. 3D空间中射线与轴向包围盒AABB的交叉检测算法【转】

    引言 在上一节中,我讲述了如何实现射线与三角形的交叉检测算法.但是,我们应该知道,在游戏开发中,一个模型有很多的三角形构成,如果要对所有的物体,所有的三角形进行这种检测,就算现在的计算机运算能力,也是 ...

  4. [ASP.NET]uploadify简单使用讲解

    背景:在用户控件中使用html 的file控件或者ASP.NET的FileUpLoad控件都无法获取到文件,于是想到听说过的uploadify uploadify官网:www.uploadify.co ...

  5. ASP.NET在请求中检测到包含潜在危险的数据,因为它可能包括 HTML标记或脚本

    背景:程序迁移到新的服务器上,在程序进行修改操作时,提示包含危险数据.然而在旧服务器上却没有问题,我猜想的可能是,新服务器IIS安装的ASP.NET版本框架高于以前的IIS上的版本框架,导致web.c ...

  6. ubuntu Mono+Jexus 部署到 ASP.NET MVC 5

    之前搞了很多次都是卡在了razor那个异常哪里,今天心血来潮就在试一试,一试竟然成功了,激动的我赶紧记录下历程.废话不说,走起... ubuntu 16.04 安装mono(最新版 5.14.0) 官 ...

  7. Newtonsoft.Json日常用法

    原文链接:https://www.cnblogs.com/ZengJiaLin/p/9578794.html

  8. Day 11 作业题

    1.整理装饰器的形成过程,背诵装饰器的固定格式 固定格式 def wrapper(func): def inner(*args, **kwargs): #执行函数前进行的操作 ret = func(* ...

  9. Delphi - 让Delphi10.2在Windows下开发的图形界面程序运行在64位Linux中!

    FmxLinux官网:https://fmxlinux.com/ 参考: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Linux_Applica ...

  10. (2)Oracle基础--表空间

    · 表空间概述 <1> 理解表空间 ① 表空间与数据库的关系: 表空间是数据库的逻辑存储空间,可以理解为在数据库中开辟的一块空间,用于存放数据库的对象. 一个数据库可以由多个表空间构成.O ...