系统相关
首页 > 系统相关> > 内存不够用还要速度快,终于找到可以基于 File 的 Cache 了

内存不够用还要速度快,终于找到可以基于 File 的 Cache 了

作者:互联网

一:背景

1. 讲故事

18年的时候在做纯内存项目的过程中遇到了这么一个问题,因为一些核心数据都是飘在内存中,所以内存空间对我们来说额外宝贵,但偏偏项目中有些数据需要缓存,比如说需要下钻的报表上的点,基于性能的考虑,不希望采用独立的缓存中间件,比如 redis, mongodb,毕竟再怎么滴还是要走网络io,但直接放在本机内存中也不现实,那有没有均衡于 native cache 和 cache server 之间的方案呢?对的,就是 disk cache,毕竟 磁盘IO 的读写要远大于网络IO,更何况配的是 SSD 呢。

二:寻找解决方案

1. 检索 github

有了 disk cache 这个大方向就可以去 github 上检索关键词,看看有没有类似的中间件,说实话,java的倒不少,比如著名的 guava,ehcache,不仅有cache的简单操作,还附带各种统计信息,刷新了对缓存认知的三观哈,尤其是 ehcache 太堆内,堆外,磁盘,分布式通通支持,用 C# 写的好不容易找到一个 disk cache 还不幸是收费的,气人哈,用 C# 调用 Java 肯定不现实了哈。

2. 使用sqlite作为 disk cache

既然开源社区没什么好的东西,看来只能自己封装一下了,像 ehcache 那种高阶的 diskcache 搞不定,用简单的 sqlite 作为本机的 diskcahe 还是可以的,接下来试试看。


   class DiskCache
   {
       private static readonly string dbFile = $@"{Environment.CurrentDirectory}\mysqlite1.db";
       private static readonly string connectionString = $@"Data Source={dbFile};Version=3";

       //过期数据监测:【一分钟来一次】
       private static Timer timer = new Timer((arg) =>
       {

       }, null, 1000, 1000 * 60);

       static DiskCache()
       {
           if (!File.Exists(dbFile))
           {
               var schema = @"CREATE TABLE Cache (
                                                 cachekey   VARCHAR (1000) PRIMARY KEY  NOT NULL,
                                                 cachevalue TEXT                        NOT NULL,
                                                 created    DATE                        NOT NULL,
                                                 expried    DATE                        NOT NULL
                                             );";

               using (SQLiteConnection connection = new SQLiteConnection(connectionString))
               {
                   connection.Execute(schema);
               }
           }
       }

       public static void Set<T>(string key, T value, int expiredMinutes)
       {
           using (SQLiteConnection connection = new SQLiteConnection(connectionString))
           {
               var sql = $"delete from Cache where cachekey =@key;" +
                         $"insert into Cache(cachekey,cachevalue,created,expried) values (@cachekey,@cachevalue,@created,@expried)";

               connection.Execute(sql, new
               {
                   key = key,
                   cachekey = key,
                   cachevalue = Newtonsoft.Json.JsonConvert.SerializeObject(value),
                   created = DateTime.Now,
                   expried = DateTime.Now.AddMinutes(expiredMinutes)
               });
           }
       }

       public static T Get<T>(string key)
       {
           using (SQLiteConnection connection = new SQLiteConnection(connectionString))
           {
               var sql = $"select cachevalue from Cache where cachekey=@cachekey and expried > @expried";

               var query = connection.QueryFirstOrDefault(sql, new { cachekey = key, expried = DateTime.Now });

               var json = JsonConvert.DeserializeObject<T>(query.cachevalue);

               return json;
           }
       }
   }

这里有二个注意点:

有了这些基础之后,原子化的缓存就实现好了,接下来试一下基本的 Get / Set 方法。

这个方案很好的节省了我宝贵的内存,同时速度又是 networkio 和 native 之间的一个平衡,算是个不错的解决办法吧。

三:aspnetcore 的 EasyCaching

EasyCaching 是园子里 @Catcher Wong 的作品 [https://www.cnblogs.com/catcher1994/p/10806607.html],点赞~~~ 看了下提供了很多种 provider,如下图:

我想后面肯定还会有更多的 provider 出现,如:leveldb,Cassandra,接下来看看这玩意怎么玩。

1. 安装使用

在 nuget 上 搜一下 EasyCaching.SQLite 安装即可,接下来就是使用文档:https://easycaching.readthedocs.io/en/latest/SQLite/#2-config-in-startup-class 如下图:

文档中是采用依赖注入的方式,而我的程序是 console 模式的后端服务,并没有 ServiceCollection,先模拟着试试看。


       static void Main(string[] args)
       {
           IServiceCollection services = new ServiceCollection();

           services.AddEasyCaching(option =>
           {
               option.UseSQLite(c =>
                {
                    c.DBConfig = new SQLiteDBOptions
                    {
                        FileName = "demo.db",
                        CacheMode = SqliteCacheMode.Default,
                        OpenMode = SqliteOpenMode.ReadWriteCreate,
                    };
                }, "m1");
           });

           IServiceProvider serviceProvider = services.BuildServiceProvider();

           var factory = serviceProvider.GetService<IEasyCachingProviderFactory>();

           var cache = factory.GetCachingProvider("m1");

           cache.Set("user", "hello world!", TimeSpan.FromSeconds(20));

           var info = cache.Get<string>("user");

           Console.WriteLine(info);
       }

接下来用 SQLiteStudio 打开 demo.db 看一下数据呈现,如下图:

图片

可以看到人家的框架比我的多了一个 name 字段,看样子是给 多个 cache 做隔离用的,不过这里貌似有三个需要优化的地方。

四:总结

可能很多人说都什么年代了还用 disk cache,这偏偏这万千世界啥需求都有,这几年开源项目越来越多,社区向好,值得点赞。


标签:cachekey,key,Cache,cache,expried,内存,File,var,new
来源: https://blog.51cto.com/15057829/2634642