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

时间:2021-08-25 23:48:50

一个基于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方法便可。

下载

点击这里下载

 
 

一个基于STSdb和fastJson的磁盘/内存缓存的更多相关文章

  1. 基于STSdb和fastJson的磁盘&sol;内存缓存

    更新 1. 增加了对批量处理的支持,写操作速度提升5倍,读操作提升100倍 2. 增加了对并发的支持 需求 业务系统用的是数据库,数据量大,部分只读或相对稳定业务查询复杂,每次页面加载都要花耗不少时间 ...

  2. C&num;开源磁盘&sol;内存缓存引擎

    前言 昨天写了个 <基于STSdb和fastJson的磁盘/内存缓存>,大家可以先看看.下午用到业务系统时候,觉得可以改进一下,昨晚想了一个晚上,刚才重新实现一下. 更新 1. 增加了对批 ...

  3. 基于&period;net的通用内存缓存模型组件

    谈到缓存,我们自然而然就会想到缓存的好处,比如: 降低高并发数据读取的系统压力:静态数据访问.动态数据访问 存储预处理数据,提升系统响应速度和TPS 降低高并发数据写入的系统压力 提升系统可用性,后台 ...

  4. 基于&period;net的分布式系统限流组件 C&num; DataGridView绑定List对象时,利用BindingList来实现增删查改 &period;net中ThreadPool与Task的认识总结 C&num; 排序技术研究与对比 基于&period;net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

  5. Memory Cache&lpar;内存缓存&rpar;

    当Google测试了Google Search服务的可用性后,发现速度是最影响Web应用的可用性的因素之一.相对于作用相同但是速度慢的应用,用户更喜欢速度快的应用.多来年,Google已经掌握了如何使 ...

  6. Go&sol;Python&sol;Erlang编程语言对比分析及示例 基于RabbitMQ&period;Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog&plus;NLog&period;Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C&num; B&sol;S 、C&sol;S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  7. 关于实现一个基于文件持久化的EventStore的核心构思

    大家知道enode框架的架构是基于ddd+event sourcing的思想.我们持久化的不是聚合根的最新状态,而是聚合根产生的领域事件.最近我在思考如何实现一个基于文件的eventstore.目标有 ...

  8. zabbix&lpar;一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案&rpar;

    zabbix 是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系统管理员快速定位/解决 ...

  9. 一个基于图的数据管理系统-gStore

    gStore是遵循 BSD协议的一个开源项目.一个基于图的 RDF 三元组存储的数据管理系统.该项目是北京大学.滑铁卢大学.香港科技大学的联合研究项目.中国北京大学计算机科学与技术研究所的数据库组对该 ...

随机推荐

  1. Java编写ArrayBasic制作一个简单的酒店管理系统

    听老师讲了一些ArrayBasic的一些知识,让制作一个酒店管理系统,要求:显示酒店所有房间列表,预订房间.... 经过老师的指导写了一个代码,如下: import java.util.Scanner ...

  2. C&plus;&plus;学习之路,漫长而遥远

    一.C/C++语言 如果你的基础很差, 建议不要一开始就学C++语言,从C开始学起,对程序有个初步的认识,循序渐进.C语言的书嘛,先买一本 300 页以内的,把书中的每一个例子都通过键盘敲打进去到 V ...

  3. js实现table中前端搜索&lpar;模糊查询&rpar;

    项目中用到js前端搜索功能,根据 姓名或姓名 进行 搜索,实现方法如下,遍历table所有行中的某列,符合条件则置tr为display:'',不满足条件置tr为display:none. 代码如下: ...

  4. 慢查询日志 与 general&lowbar;log

            慢查询日志: 打开慢查询日志: set global slow_query_log=on; 输出格式定义:log_output: [file|table] FILE: set glob ...

  5. Delphi编程中资源文件的应用

    Delphi编程中资源文件的应用/转自 http://chamlly.spaces.live.com/blog/cns!548f73d8734d3acb!236.entry一.引子: 现在的Windo ...

  6. PAT 团体程序设计天梯赛-练习集 L1-016&period; 查验身份证

    一个合法的身份证号码由17位地区.日期编号和顺序编号加1位校验码组成.校验码的计算规则如下: 首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8, ...

  7. 执行yum提示错误:rpmdb&colon; BDB0113 Thread&sol;process 424227&sol;139826856310848 failed

    [问题]在执行yum安装或者其他命令时,有如下提示: [解决办法:重新构建rpm数据库] [root@cly ~]# cd /var/lib/rpm [root@cly rpm]# ls Basena ...

  8. Linux更新阿里源

    阿里云Linux安装镜像源地址:http://mirrors.aliyun.com/ CentOS系统更换软件安装源: 第一步:备份你的原镜像文件,以免出错后可以恢复. mv /etc/yum.rep ...

  9. 撰写一篇博客要求讲述四则运算2的设计思想,源程序代码、运行结果截图、编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志。

    一.撰写一篇博客要求讲述四则运算2的设计思想,源程序代码.运行结果截图.编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志. 1.设计思想: ①创建test.jsp建立第一个前端界面,提 ...

  10. RegExp exec有记忆性的问题

    当 RegExpObject 是作为一个变量时时.每次调用完exec()后.它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string.当 exec() 找 ...