基于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, K Key, 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;
- }
- }
- }
相关文章