关于c#:内存不够用还要速度快终于找到可以基于-File-的-Cache-了

32次阅读

共计 3263 个字符,预计需要花费 9 分钟才能阅读完成。

一:背景

1. 讲故事

18 年的时候在做纯内存我的项目的过程中遇到了这么一个问题,因为一些外围数据都是飘在内存中,所以内存空间对咱们来说额定贵重,但偏偏我的项目中有些数据须要缓存,比如说须要下钻的报表上的点,基于性能的思考,不心愿采纳独立的缓存中间件,比方 redis, mongodb,毕竟再怎么滴还是要走网络 io,但间接放在本机内存中也不事实,那有没有平衡于 native cachecache 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;
            }
        }
    }

这里有二个留神点:

  • 因为是做缓存,所以数据库和表的创立都要通过程序自动化,数据库是否存在判断 file 文件是否存在即可。
  • 过期数据的问题,因为我有 expried 字段,这一点能够学习 GC 思维,应用 Timer 在后盾定期清理。

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

这个计划很好的节俭了我贵重的内存,同时速度又是 networkio 和 native 之间的一个均衡,算是个不错的解决办法吧。

三:aspnetcore 的 EasyCaching

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

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

1. 装置应用

在 nuget 上 搜一下 EasyCaching.SQLite 装置即可,接下来就是应用文档:https://easycaching.readthedo… 如下图:

文档中是采纳依赖注入的形式,而我的程序是 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 做隔离用的,不过这里貌似有三个须要优化的中央。

  • 并不是每一个程序都要应用 依赖注入 的形式,提供更便捷的形式初始化就更好了。
  • 看了下源码,并没有找到能够定期删除过期数据的业务逻辑。
  • 倡议提供一些 cache 的统计信息,如命中次数,某一个 key 最初命中工夫等等时候统计图。

四:总结

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

正文完
 0