.NET性能优化,如何利用内存磁盘混合缓存提升效率?

摘要:我们回顾一下上一篇文章中的内容,有一个朋友问我这样一个问题: > 我的业务依赖一些数据,因为数据库访问慢,我把它放在Redis里面,不过还是太慢了,有什么其它的方案吗? 其实这个问题比较简单的是吧?Redis其实属于网
我们回顾一下上一篇文章中的内容,有一个朋友问我这样一个问题: 我的业务依赖一些数据,因为数据库访问慢,我把它放在Redis里面,不过还是太慢了,有什么其它的方案吗? 其实这个问题比较简单的是吧?Redis其实属于网络存储,我对照下面的这个表格,可以很容易的得出结论,既然网络存储的速度慢,那我们就可以使用内存RAM存储,把放Redis里面的数据给放内存里面就好了。 操作 速度 执行指令 1/1,000,000,000 秒 = 1 纳秒 从一级缓存读取数据 0.5 纳秒 分支预测失败 5 纳秒 从二级缓存读取数据 7 纳秒 使用Mutex加锁和解锁 25 纳秒 从主存(RAM内存)中读取数据 100 纳秒 在1Gbps速率的网络上发送2Kbyte的数据 20,000 纳秒 从内存中读取1MB的数据 250,000 纳秒 磁头移动到新的位置(代指机械硬盘) 8,000,000 纳秒 从磁盘中读取1MB的数据 20,000,000 纳秒 发送一个数据包从美国到欧洲然后回来 150 毫秒 = 150,000,000 纳秒 提出这个方案以后,接下来就遇到了另外一个问题: 但是数据比我应用的内存大,这怎么办呢? 在上篇文章中,我们提到了使用FASTER作为内存+磁盘混合缓存的方案,但是由于FASTER的API比较难使用,另外在纯内存场景中表现不如ConcurrentDictionary,所以最后得出的结论也是仅供参考。 经过一段时间的研究,笔者实现了一个基于微软FasterKv封装的进程内混合缓存库(内存+磁盘),它有着更加易用的API,接下来就和大家讨论讨论它。 FasterKvCache架构 这里需要简单的说一说FasterKvCache的架构,它核心使用的FasterKv,所以架构实际上和FasterKv一致,其原理比较复杂,所以笔者简化了原理图,大概就如下所示: FasterKv的热数据会在内存中,而全量的数据会持久化在磁盘中。这中间有一些缓存淘汰算法,所以大家看到这张图就能明白FasterKvCache适用和不适用哪些场景了。 如何使用它 笔者之前给EasyCaching提交了FasterKv的实现,但是由于有一些EasyCaching的高级功能在FasterKv上目前无法高性能的实现,所以单独创建了这个库,提供高性能和最基本的API实现;如果大家已经使用了EasyCaching,那么可以直接使用EasyCaching.FasterKv这个NuGet包。 如果使用需要FasterKvCache的话,只需要安装Nuget包,Nuget包不同的功能如下所示,其中序列化包可以只安装自己需要的即可。 软件包名 版本 备注 FasterKv.Cache.Core 1.0.0-rc1 缓存核心包,包含FasterKvCache主要的API FasterKv.Cache.MessagePack 1.0.0-rc1 基于MessagePack的磁盘序列化包,它具有着非常好的性能,但是需要注意它稍微有一点使用门槛,大家可以看它的文档。 FasterKv.Cache.SystemTextJson 1.0.0-rc1 基于System.Text.Json的磁盘序列化包,它是.NET平台上性能最好JSON序列化封装,但是比MessagePack差。不过它易用性非常好,无需对缓存实体进行单独配置。 使用 直接使用 我们可以直接通过new FasterKvCache(...)的方式使用它,目前它只支持基本的三种操作Get、Set、Delete。为了方便使用和性能的考虑,我们将FasterKvCache分为两种API风格,一种是通用对象风格,一种是泛型风格。 通用对象:直接使用new FasterKvCache(...)创建,可以存放任意类型的Value。它底层使用object类型存储,所以内存缓冲内访问值类型对象会有装箱和拆箱的开销。 泛型:需要使用new FasterKvCache<T>(...)创建,只能存放T类型的Value。它底层使用T类型存储,所以内存缓冲内不会有任何开销。 当然如果内存缓冲不够,对应的Value被淘汰到磁盘上,那么同样都会有读写磁盘、序列化和反序列化开销。
阅读全文