• 一个基于STSdb和fastJson的磁盘/内存缓存


    一个基于STSdb和fastJson的磁盘/内存缓存

    需求

    业务系统用的是数据库,数据量大,部分只读或相对稳定业务查询复杂,每次页面加载都要花耗不少时间(不讨论异步),觉得可以做一下高速缓存,譬如用nosql那种key/value快速存取结果

    目的

    这里不是要做一个大家都适用的磁盘/内存缓存库,这个做法,部分是展示STSdb的用法,部分是提供一个简单易用的解决方案。

    磁盘/内存

    为什么不用memcached或者AppFabric Cache这样的现成解决方案呢?因为业务要缓存的内存或大或小,小的几KB,大的几MB,如果用户一多,势必对内存有过度的需求。所以选择做一个基于磁盘的。

    当然,这个解决方案是支持内存缓存的。构造的时候传递空字符串便可。

    STSdb是什么

    再来说明一下STSdb是什么:STSdb是C#写的开源嵌入式数据库和虚拟文件系统,支持实时索引,性能是同类产品的几倍到几十倍,访问官方网站

    我之前介绍过:STSdb,最强纯C#开源NoSQL和虚拟文件系统 和 STSdb,最强纯C#开源NoSQL和虚拟文件系统 4.0 RC2 支持C/S架构 ,大家可以先看看。

    实现

    存取

    因为是基于磁盘,所以需要使用到高效的Key/Value存取方案,碰巧我们有STSdb :)

    序列化

    因为要求简便快速,用的是fastJson

    代码

    代码比较简单,花了2个小时写的,很多情况没考虑,譬如磁盘空间不足、过期空间回收等,这些留给大家做家庭作业吧。另外,为了发布方便,STSdb和fastJson的代码都合并到一个项目里。

    CahceEngine.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using STSdb4.Database;
    using fastJSON;
    using System.IO;
     
    namespace Com.SuperCache.Engine
    {
        public class CacheEngine
        {
            private const string KeyExpiration = "Expiration";
            private string dataPath;
            private static IStorageEngine memoryInstance = null;
            private bool isMemory = false;
     
            static CacheEngine()
            {
                memoryInstance = STSdb.FromMemory();
            }
     
            public CacheEngine(string DataPath)
            {
                dataPath = DataPath;
                if (!dataPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
                    dataPath += Path.DirectorySeparatorChar;
     
                isMemory = string.IsNullOrEmpty(DataPath);
            }
     
            public void Add<K>(string Category, K Key, object Data)
            {
                Add(Category, Key, Data, null);
            }
     
            private IStorageEngine Engine
            {
                get
                {
                    if (isMemory)
                        return memoryInstance;
                    else
                        return STSdb.FromFile(GetFile(false), GetFile(true));
                }
            }
     
            public void Add<K>(string Category, K Key, object Data, DateTime? ExpirationDate)
            {
                var engine = Engine;
                var table = engine.OpenXIndex<K, string>(Category);
                var result = JSON.Instance.ToJSON(Data);
                table[Key] = result;
                table.Flush();
                //add cache expiration time
                var expiration = engine.OpenXIndex<K, DateTime>(KeyExpiration);
                //default 30 mins before expiration
                var expirationDate = ExpirationDate == null || ExpirationDate <= DateTime.Now ? DateTime.Now.AddMinutes(30) : (DateTime)ExpirationDate;
                expiration[Key] = expirationDate;
                expiration.Flush();
     
                engine.Commit();
     
                if (!isMemory)
                    engine.Dispose();
            }
     
            private string GetFile(bool IsData)
            {
                if (!Directory.Exists(dataPath))
                    Directory.CreateDirectory(dataPath);
                return dataPath + "SuperCache." + (IsData ? "dat" : "sys");
            }
     
            public V Get<K, V>(string Category, K Key)
            {
                var engine = Engine;
                var table = engine.OpenXIndex<K, string>(Category);
                string json;
                V result;
                if (table.TryGet(Key, out json))
                {
                    result = JSON.Instance.ToObject<V>(json);
                    var expiration = engine.OpenXIndex<K, DateTime>(KeyExpiration);
                    DateTime expirationDate;
                    //verify expiration date
                    if (expiration.TryGet(Key, out expirationDate))
                    {
                        //expired
                        if (expirationDate < DateTime.Now)
                        {
                            result = default(V);
                            table.Delete(Key);
                            table.Flush();
                            expiration.Delete(Key);
                            expiration.Flush();
                            engine.Commit();
                        }
                    }
                }
                else
                    result = default(V);
     
                if (!isMemory)
                    engine.Dispose();
     
                return result;
            }
        }
    }

      

      

    新建

    构造CacheEngine需要传递缓存保存到哪个文件夹。

    基于内存

    如果你不喜欢基于磁盘的缓存,可以使用基于内存,构造函数传递空字符串便可。

    增加/更新

    同一个方法:Add。用户可以指定类型(Category),譬如User,Employee等。键(Key)支持泛型,值(Data)是object。有一个overload是过期日期(ExpirationDate),默认当前时间30分钟后

    获取

    Get方法需要指定类型(Category)和键(Key)。

     

    例子

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Com.SuperCache.Engine;
     
    namespace Com.SuperCache.Test
    {
        public class Foo
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public double? Some { get; set; }
            public DateTime? Birthday { get; set; }
        }
     
        class Program
        {
            static void Main(string[] args)
            {
                TestAddGet();
                //TestExpiration();
     
                Console.Read();
            }
     
            private static void TestExpiration()
            {
                var engine = new CacheEngine(@"....data");
                var o = engine.Get<string, Foo>("User", "wchen");
                Console.WriteLine(o != null ? o.Name : "wchen does not exist or expired");
            }
     
            private static void TestAddGet()
            {
                var engine = new CacheEngine(@"....data");
                var f = new Foo { Name = "Wilson Chen", Age = 30, Birthday = DateTime.Now, Some = 123.456 };
                engine.Add("User", "wchen", f, DateTime.Now.AddSeconds(10));
     
                var t = @"Bla Bla Bla......";
                engine.Add("PlainText", "Bla", t);
     
                var o = engine.Get<string, Foo>("User", "wchen");
                Console.WriteLine(o.Name);
     
                var o4 = engine.Get<string, Foo>("User", "foo");
                Console.WriteLine(o4 != null ? o4.Name : "foo does not exist");
     
                var o2 = engine.Get<string, string>("PlainText", "Bla");
                Console.WriteLine(o2);
     
                var o3 = engine.Get<string, string>("PlainText", "A");
                Console.WriteLine(o3 ?? "A does not exist");
            }
        }
    }

    说明

    项目中引用了System.Management是因为STSdb支持内存数据库,需要判断最大物理内存。如果不喜欢,大家可以移除引用,并且去掉STSdb4.Database.STSdb.FromMemory方法便可。

    下载

    点击这里下载

     

     

     

     

     

  • 相关阅读:
    layui的模块化和非模块化使用
    layui实现类似于bootstrap的模态框功能
    ajax下载文件
    【IDEA】IDEA中maven项目pom.xml依赖不生效解决
    主-主数据库系统架构
    MyEclipse x.x各版本终极优化配置指南
    Cactus入门
    有史以来最出彩的编程语言名字
    安卓开发20:动画之Animation 详细使用-主要通过java代码实现动画效果
    第一次讲课总结
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3169649.html
Copyright © 2020-2023  润新知