基于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 static object syncRoot = new object();
        private bool isMemory = false;

        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)
                {
                    lock (syncRoot)
                    {
                        if (memoryInstance == null)
                            memoryInstance = STSdb.FromMemory();
                    }
                    return memoryInstance;
                }
                else
                    return STSdb.FromFile(GetFile(false),GetFile(true));
            }
        }

        public void Add<K>(string Category,object Data,DateTime? ExpirationDate)
        {
            var engine = Engine;
            var table = engine.OpenXIndex<K,string>(Category);
            var result = Data is string ? (string)Data : JSON.Instance.ToJSON(Data);
            table[Key] = result;
            table.Flush();

            var expiration = engine.OpenXIndex<K,DateTime>(KeyExpiration);
            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 buffer;
            V result;
            if (table.TryGet(Key,out buffer))
            {
                result = typeof(V) == typeof(string) ? (V)(object)buffer : JSON.Instance.ToObject<V>(buffer);
                var expiration = engine.OpenXIndex<K,DateTime>(KeyExpiration);
                DateTime expirationDate;
                if (expiration.TryGet(Key,out expirationDate))
                {
                    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 System.Diagnostics;
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();
            TestPerformance();

            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 TestPerformance()
        {
            var engine = new CacheEngine(@"..\..\data");
            var w = new Stopwatch();
            w.Start();
            for (int i = 0; i < 100; i++)
            {
                var o2 = engine.Get<string,string>("PlainText","Bla");
            }
            w.Stop();
            Console.WriteLine(w.Elapsed);
        }

        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,"wchen");

            var o4 = engine.Get<string,"foo");
            Console.WriteLine(o4 != null ? o4.Name : "foo does not exist");

            var o2 = engine.Get<string,"Bla");
            Console.WriteLine(o.Name);

            var o3 = engine.Get<string,"A");
            Console.WriteLine(o3 ?? "A does not exist");
        }
    }
}

说明

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

下载

点击这里下载

猜你在找的Json相关文章