加入收藏 | 设为首页 | 会员中心 | 我要投稿 92站长网 (https://www.92zhanzhang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

C# .NET中的缓存达成

发布时间:2021-11-05 15:06:38 所属栏目:语言 来源:互联网
导读:软件开发中最常用的模式之一是缓存。这是一个简单但非常有效的概念,这个想法的核心是记录过程数据,重用操作结果。当执行繁重的操作时,我们会将结果保存在我们的缓存容器中。下次我们需要该结果时,我们将从缓存容器中拉出它,而不是再次执行繁重的操作。

软件开发中最常用的模式之一是缓存。这是一个简单但非常有效的概念,这个想法的核心是记录过程数据,重用操作结果。当执行繁重的操作时,我们会将结果保存在我们的缓存容器中。下次我们需要该结果时,我们将从缓存容器中拉出它,而不是再次执行繁重的操作。
 
例如,要获取一个人的头像,您可能需要访问数据库。我们不会每次都执行那次旅行,而是将 Avatar 保存在缓存中,每次需要时从内存中提取它。
 
缓存非常适用于不经常更改的数据。或者甚至更好,永远不会改变。不断变化的数据,比如当前机器的时间不应该被缓存,否则你会得到错误的结果。
 
进程内缓存、持久性进程内缓存和分布式缓存
有 3 种类型的缓存:
 
In-Memory Cache用于在单个进程中实现缓存。当进程终止时,缓存也随之终止。如果您在多台服务器上运行相同的进程,您将为每台服务器提供一个单独的缓存。
持久性进程内缓存是指在进程内存之外备份缓存。它可能在文件中,也可能在数据库中。这比较困难,但如果您的进程重新启动,缓存不会丢失。最适合在获取缓存项的情况下使用范围广泛,并且您的进程往往会重新启动很多。
分布式缓存是指您希望为多台机器共享缓存。通常,它将是多个服务器。使用分布式缓存,它存储在外部服务中。这意味着如果一台服务器保存了一个缓存项,其他服务器也可以使用它。像Redis[1]这样的服务非常适合这一点。
我们将只讨论进程内缓存。
 
早期做法
让我们用 C# 创建一个非常简单的缓存实现:
 
public class NaiveCache<TItem>
{
    Dictionary<object, TItem> _cache = new Dictionary<object, TItem>();
 
    public TItem GetOrCreate(object key, Func<TItem> createItem)
    {
        if (!_cache.ContainsKey(key))
        {
            _cache[key] = createItem();
        }
        return _cache[key];
    }
}
用法:
 
var _avatarCache = new NaiveCache<byte[]>();
// ...
var myAvatar = _avatarCache.GetOrCreate(userId, () => _database.GetAvatar(userId));
这个简单的代码解决了一个关键问题。要获取用户的头像,只有第一个请求才会真正执行到数据库的访问。然后将头像数据 ( byte[]) 保存在进程内存中。对头像的所有后续请求都将从内存中提取,从而节省时间和资源。
 
但是,正如编程中的大多数事情一样,没有什么是那么简单的。由于多种原因,上述解决方案并不好。一方面,这个实现不是线程安全的。从多个线程使用时可能会发生异常。除此之外,缓存的项目将永远留在内存中,这实际上非常糟糕。
 
这就是我们应该从缓存中删除项目的原因:
 
1.缓存会占用大量内存,最终导致内存不足异常和崩溃。
 
2.高内存消耗会导致GC 压力(又名内存压力)。在这种状态下,垃圾收集器的工作量超出其应有的水平,从而损害了性能。
 
3.如果数据发生变化,可能需要刷新缓存。我们的缓存基础设施应该支持这种能力。
 
为了处理这些问题,缓存框架具有驱逐策略(又名移除策略)。这些是根据某些逻辑从缓存中删除项目的规则。常见的驱逐政策有:
 
无论如何,绝对过期策略将在固定时间后从缓存中删除项目。
如果在固定的时间段内未访问某个项目,则滑动过期策略将从缓存中删除该项目。因此,如果我将过期时间设置为 1 分钟,只要我每 30 秒使用一次,该项目就会一直保留在缓存中。一旦我超过一分钟不使用它,该物品就会被驱逐。
大小限制策略将限制缓存内存大小。
现在我们知道我们需要什么,让我们继续寻找更好的解决方案。
 
更好的解决方案
作为一名博主,令我非常沮丧的是,微软已经创建了一个很棒的缓存实现。这剥夺了我自己创建类似实现的乐趣,但至少我写这篇博文的工作量减少了。
 
我将向您展示微软的解决方案,如何有效地使用它,然后在某些场景中如何改进它。
 
System.Runtime.Caching/MemoryCache 与 Microsoft.Extensions.Caching.Memory
Microsoft 有 2 个解决方案 2 个不同的 NuGet 包用于缓存。两者都很棒。根据 Microsoft 的建议[2],更喜欢使用,Microsoft.Extensions.Caching.Memory因为它与 Asp.NET Core 集成得更好。它可以很容易地注入[3]到 Asp .NET Core 的依赖注入机制中。
 
这是一个基本示例Microsoft.Extensions.Caching.Memory:
 
public class SimpleMemoryCache<TItem>
{
    private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
 
    public TItem GetOrCreate(object key, Func<TItem> createItem)
    {
        TItem cacheEntry;
        if (!_cache.TryGetValue(key, out cacheEntry))// Look for cache key.
        {
            // Key not in cache, so get data.
            cacheEntry = createItem();
 
            // Save data in cache.
            _cache.Set(key, cacheEntry);
        }
        return cacheEntry;
    }
}
用法:
 
var _avatarCache = new SimpleMemoryCache<byte[]>();
// ...
var myAvatar = _avatarCache.GetOrCreate(userId, () => _database.GetAvatar(userId));
这和我自己的非常相似NaiveCache,所以有什么改变?嗯,一方面,这是一个线程安全的实现。您可以一次从多个线程安全地调用它。
 
第二件事是MemoryCache允许我们之前谈到的所有驱逐政策。下面是一个例子:
 
具有驱逐策略的 IMemoryCache:
public class MemoryCacheWithPolicy<TItem>
{
    private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions()
    {
        SizeLimit = 1024
    });
 
    public TItem GetOrCreate(object key, Func<TItem> createItem)
    {
        TItem cacheEntry;
        if (!_cache.TryGetValue(key, out cacheEntry))// Look for cache key.
        {
            // Key not in cache, so get data.
            cacheEntry = createItem();
 
            var cacheEntryOptions = new MemoryCacheEntryOptions()
             .SetSize(1)//Size amount
             //Priority on removing when reaching size limit (memory pressure)
                .SetPriority(CacheItemPriority.High)
                // Keep in cache for this time, reset time if accessed.
                .SetSlidingExpiration(TimeSpan.FromSeconds(2))
                // Remove from cache after this time, regardless of sliding expiration
                .SetAbsoluteExpiration(TimeSpan.FromSeconds(10));
 
            // Save data in cache.
            _cache.Set(key, cacheEntry, cacheEntryOptions);
        }
        return cacheEntry;
    }
}
1.SizeLimit被添加到MemoryCacheOptions. 这为我们的缓存容器添加了基于大小的策略。大小没有单位。相反,我们需要在每个缓存条目上设置大小数量。在这种情况下,我们每次将金额设置为 1 SetSize(1)。这意味着缓存限制为 1024 个项目。
 
2.当我们达到大小限制时,应该删除哪个缓存项?您实际上可以使用.SetPriority(CacheItemPriority.High). 级别为Low、Normal、High和NeverRemove。
 
3.SetSlidingExpiration(TimeSpan.FromSeconds(2))添加了,它将滑动过期时间设置为 2 秒。这意味着如果一个项目在 2 秒内未被访问,它将被删除。
 
4.SetAbsoluteExpiration(TimeSpan.FromSeconds(10))添加了,将绝对过期时间设置为 10 秒。这意味着该项目将在 10 秒内被驱逐,如果它还没有。
 
除了示例中的选项之外,您还可以设置一个RegisterPostEvictionCallback委托,该委托将在项目被驱逐时调用。
 
这是一个非常全面的功能集。它让你想知道是否还有什么要添加的。实际上有几件事。

(编辑:92站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读