ASP.NET Core 中的緩衝

來源:互聯網
上載者:User

目錄

  • 緩衝的基本概念
    • 緩衝原理
    • 緩衝設計
    • 分布式緩衝 Memcache 與 Redis 的比較
    • 緩衝穿透,緩衝擊穿,緩衝雪崩解決方案
    • 資料一致性
  • 使用內建 MemoryCache
  • 使用分布式緩衝 Redis
  • 使用 Stackexchange.Redis 自己封裝一個 RedisHelper 類
  • 參考
緩衝的基本概念

緩衝是分布式系統中的重要組件,主要解決高並發,巨量資料情境下,熱點資料訪問的效能問題。提供高效能的資料快速存取。

緩衝原理
  • 將資料寫入到讀取速度更快的存放裝置;
  • 將資料緩衝到離應用最近的位置;
  • 將資料緩衝到離使用者最近的位置。
緩衝設計
  • 緩衝內容 熱點資料,靜態資源
  • 緩衝位置 CDN,反向 Proxy,分布式快取服務器,本機(記憶體,硬碟)
    • CDN:存放HTML、CSS、JS等靜態資源;
    • 反向 Proxy:動靜分離,只緩衝使用者請求的靜態資源;
    • 分布式緩衝:快取資料庫中的熱點資料;
    • 本機快取:緩衝應用字典等常用資料。
  • 到期策略 固定時間,相對時間
  • 同步機制 即時寫入,非同步重新整理
分布式緩衝 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擴充:

  • StackExchange.Redis.Extensions
  • EasyCaching
參考
  • 緩衝技術的詳解
  • 緩衝更新的套路
  • 緩衝擊穿/穿透/雪崩
  • Cache in ASP.NET Core
  • 擁抱.NET Core系列:MemoryCache 緩衝到期
  • 使用Redis實現分布式緩衝

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.