c#滑窗缓存
前言
在大数据时代,软件系统需要具备处理海量数据的能力,同时也更加依赖于系统强大的存储能力与数据响应能力。各种大数据的工具如雨后春笋般孕育而生,这对于系统来说是极大的利好。但在后端采用分布式、云存储和虚拟化等技术大刀阔斧地解决大部分存储问题后,仍然不足以满足所有的业务需求。对于以用户为终点的软件系统来说,无论后台多么强大都难以避免有一部分数据流向终端,面向用户。为了应对这最后一公里的通勤问题,我们得在终端缓存部分数据来提高系统的响应效率。另外一方面,受限于用户终端的机器性能,缓存大量的数据反而会降低系统响应速度,甚至让系统崩溃。为此,我们需要一个根据系统当前状态动态调整最急需的数据的缓存器,滑窗缓存是一个很不错的选择。最终,我们找到了SlidingWindowCache,一个基于 .NET standard 实现的滑窗缓存。
SlidingWindowCache 简介
SlidingWindowCache 基于键值对缓存,可以缓存以特定序列序列组织的数据,比如时间序列数据。其本身带有预先缓存的能力,当系统状态满足预设条件后将自动缓存数据。每次自动缓存的量可自行配置。当缓存超出窗口后即被视为无用数据,会被自动释放。同样的,缓存窗口大小可进行配置。
作为 key/value 缓存,该缓存的 value 可以是任意类型的数据。但为了满足有序组织,目前的 key 只支持 int、long、float 和 double 四种类型。对于时间序列数据来说,可以将时间转化为 long 作为 key 使用。后面将以 DataTime 转为 Ticks 为例进行演示(事实上转为时间戳更具有通用性),直接展示使用例程更加容易说明问题。
SlidingWindowCache 使用
SlidingWindowCache 配置
SlidingWindowCache 的绝大部分配置都在ISlidingWindowConfig<TKey>
接口中定义,目前具有以下重要的配置:
TKey StartPoint { get; set; }
—— 缓存序列的起点TKey EndPoint { get; set; }
—— 缓存序列的终点TKey PerLoadSize { get; set; }
—— 每次缓存请求的大小。在自动缓存中,将自动向数据源请求数据TKey TotalLoadSize { get; set; }
—— 总共加载的数据大小。在自动缓存中,缓存数据到达该阈值则停止自动缓存TKey TotalCacheSize { get; set; }
—— 总共缓存的数据大小。即滑动窗口的大小,超出该窗口的数据被自动释放int LoadParallelLimit { get; set; }
—— 自动加载数据时并发量阈值float LoadTriggerFrequency { get; set; }
—— 加载触发频率。为 1 时,只要状态一改变,立即触发自动加载。float RemoveTriggerFrequency { get; set; }
—— 移除触发频率。为 1 时,只要状态一改变,立即触发自动移除。float ForwardAndBackwardScale { get; set; }
—— 前后比例(TKey 大端为前,习惯了以时间箭头为前)。以缓存大小来说,当前 TKey 作为分割点。
我们可以用形象的比喻来做进一步的解释。StartPoint
和EndPoint
限定了窗体能滑动的边界。TotalCacheSize
限定了窗体的大小,在某种意义上来说,该窗体是残破不堪的,因其并未随时拥有所有的数据。它等待着修补匠进行破窗修补(数据源加载)。TotalLoadSize
限定了每个状态生命周期中修补破窗的总大小,也就是自动请求数据量的大小。PerLoadSize
则为每次修补的大小,即每次向数据源请求的数据量。LoadParallelLimit
可以理解为可以同时工作的修补匠的最多人数。LoadTriggerFrequency
则可以理解为当状态变更时,修补匠的出勤率。
SlidingWindowCache 缓存
SlidingWindowCache 当前只提供少数重要的功能,全在ISlidingWindowCache<TKey, TData>
接口中进行定义。
// 当前点,用来标记缓存状态
TKey CurrentPoint { get; set; }
// 当前缓存的key的个数
int Count { get; }
// 从缓存中获取数据
Task<IEnumerable<TData>> GetCacheData(TKey start, TKey end, Func<TData, TKey> keyOfTData);
// 加载源数据的委托(必须进行赋值)
Func<TKey, TKey, CancellationToken, Task<IEnumerable<TData>>> DataSourceDelegate { get; set; }
// 自动加载任务状态报告事件
event EventHandler<TaskStatus> OnDataAutoLoaderStatusChanged;
SlidingWindowCache 具体使用
下面以缓存时间序列数据为例做一具体使用介绍
// 自定义数据模拟类
public class DataModel
{
private static readonly Lazy<DataModel> _lazy = new Lazy<DataModel>(() => new DataModel());
public static DataModel Instance => _lazy.Value;
public long Point { get; set; }
// 模拟大量数据,占用内存
public long[] data = new long[1000];
// 模拟服务器数据请求
public Task<IEnumerable<DataModel>> LoadDataFromSource(long s, long e,
CancellationToken cancellationToken)
{
return Task.Run(() =>
{
var rd = new Random();
// 模拟远程访问数据时可能的延迟
Task.Delay(rd.Next(50, 400), cancellationToken).Wait(cancellationToken);
var diff = (int)(e - s);
var count = diff > 100 ? 100 : diff;
var result = Enumerable.Range(0, count)
.Select(t => new DataModel { Point = s + rd.Next(diff) })
.OrderBy(t => t.Point)
.ToList();
return (IEnumerable<DataModel>)result;
}, cancellationToken);
}
}
// 滑窗配置
var config = new SlidingWindowConfig<long>
{
PerLoadSize = new TimeSpan(0, 2, 0).Ticks,
StartPoint = new DateTime(2019, 1, 1).Ticks,
EndPoint = new DateTime(2019, 2, 1).Ticks,
TotalLoadSize = new TimeSpan(0, 30, 0).Ticks,
TotalCacheSize = new TimeSpan(7, 0, 0).Ticks
};
// 实例化缓存器
var cache = new SlidingWindowCache<long, DataModel>(config)
{
// 提供获取源数据的委托
DataSourceDelegate = DataModel.Instance.LoadDataFromSource,
CurrentPoint = config.StartPoint
};
// 获取2019-1-1 0:1:39至2019-1-1 0:2:0之间的数据
// lamda表达式t => t.Point提供缓存类型DataModel中的TKey的获取方法,用于数据过滤
var data = await cache.GetCacheData(
new DateTime(2019, 1, 1, 0, 1, 39).Ticks,
new DateTime(2019, 1, 1, 0, 2, 0).Ticks,
t => t.Point);
上述例子中,我们可能查看的数据总范围为:2019-1-1 至 2019-2-1,总共为一个月的数据量。而终端机器允许缓存的数据量最多只能有 7 个小时。为了减少服务器压力,每次请求两分钟的数据量,预先自动缓存为半小时的数据量。在某一次数据获取中(2019-1-1 0:1:39 至 2019-1-1 0:2:0),获取 21 秒的数据,lamda 将提供自动筛选的凭据。随着cache.CurrentPoint
逐渐增加(这里模拟时间增加),可以看到内存的大致变化趋势:
随着时间增加,内存使用量首先会持续增加,当达到设定阈值后便自动下降。此后,便在某一窗口之间重复震荡。符合滑动窗口缓存的预期。
后记
SlidingWindowCache 已经投入实际使用环境中,每次请求的量达到千级甚至万级,总共缓存的量达到百万级别(后端使用 Hbase 作为最终的存储方案,前端以 SlidingWindowCache 作为最前的缓存方案)。
SlidingWindowCache 项目刚刚起步,欢迎提出改进意见。
c#滑窗缓存的更多相关文章
- TCP系列31—窗口管理&流控—5、TCP流控与滑窗
一.TCP流控 之前我们介绍过TCP是基于窗口的流量控制,在TCP的发送端会维持一个发送窗口,我们假设发送窗口的大小为N比特,网络环回时延为RTT,那么在网络状况良好没有发生拥塞的情况下,发送端每个R ...
- (转)协议森林10 魔鬼细节 (TCP滑窗管理)
协议森林10 魔鬼细节 (TCP滑窗管理) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在TCP协议与"流" ...
- 【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】
比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; ...
- matlab核函数与滑窗
在处理图像时,为了提取特征,经常用各种核函数和图像进行卷积,其实就是通过一个矩阵以滑窗的形式与原图像进行点乘求和,可以看作对一个像素和附近像素进行了加权平均. 比如经常用3x3的近似高斯卷积核 0 1 ...
- 计蒜客 A2232.程序设计:蒜厂年会-单调队列(双端队列(STL deque)实现)滑窗维护最小前缀和
程序设计:蒜厂年会 问答问题反馈 只看题面 16.79% 1000ms 262144K 在蒜厂年会上有一个抽奖,在一个环形的桌子上,有 nn 个纸团,每个纸团上写一个数字,表示你可以获得多少蒜币. ...
- [poj 3261]后缀数组+滑窗最小值
题目链接:http://poj.org/problem?id=3261 这个是可以交叉的重复串,所以用height就可以了,但是题目说让重复k次以上,也就是直接做一个k-1长度的滑窗最小值,从这些最小 ...
- HDU 6319.Problem A. Ascending Rating-经典滑窗问题求最大值以及COUNT-单调队列 (2018 Multi-University Training Contest 3 1001)
2018 Multi-University Training Contest 3 6319.Problem A. Ascending Rating 题意就是给你长度为k的数列,如果数列长度k<n ...
- HDU 5696 RMQ+滑窗
区间的价值 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Subm ...
- UVA 1619/POJ2796 滑窗算法/维护一个单调栈
Feel Good Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 12409 Accepted: 3484 Case T ...
随机推荐
- 将web工程署到Linux简单实现
1,将数据库文件导出并导入到Linux下的数据库中 2,将数据库连接池的连接IP改为Linux所在服务器的 3,将工程文件以war包形式导出 4,利用secureCRT,Xshell等工具远程连接Li ...
- Android 隐藏软键盘功能的实现
Activity context = (Activity) mContext; final View v = context.getWindow().peekDecorView(); if (v != ...
- C#2.0增功能04 可以为 null 的类型
连载目录 [已更新最新开发文章,点击查看详细] 可以为 null 的类型是 System.Nullable<T> 结构的实例. 可以为 null 的类型可表示一个基础类型的所有值 T ...
- 脑裂是什么?Zookeeper是如何解决的?
什么是脑裂 脑裂(split-brain)就是"大脑分裂",也就是本来一个"大脑"被拆分了两个或多个"大脑",我们都知道,如果一个人有多个大 ...
- hdu6383 p1m2(二分答案)
p1m2 题目传送门 解题思路 因为x都是非负数,且每一次操作其实就是把总和减少了1,所以可以得出最后都可以到达稳定.最后稳定的数的下界是0,最大也不会超过其初始数的最大值,所以可以用二分答案来求解. ...
- springboot启动不设置端口
非web工程 在服务架构中,有些springboot工程只是简单的作为服务,并不提供web服务 这个时候不需要依赖 <dependency> <groupId>org.spri ...
- 浅谈hosts文件
1.什么是hosts文件?这个文件在哪? hosts文件(域名解析文件)是将主机名映射到IP地址的一个纯文本文件,原始名称是HOSTS.TXT(IP,Internet Protocol,Interne ...
- Apache Tomcat 绿色版安装Service(服务)
1.配置CATALINA_HOME的环境变量: 变量名:CATALINA_HOME 值:tomcat安装或解压的根目录如:c:\Apache tomcat6.0 2.开始->运行->c ...
- 用户体验要素——产品系统设计方法
用户体验已经成为了每个互联网人的口头词,特别是互联网产品经理或产品设计师. 的确,对于任何一个互联网产品而言,体验都是非常重要的. 但是具体的用户体验到底指的是哪些方面,界面,UI,还是交互,其中到底 ...
- java中代码的注释和快捷
添加必要的注释,对一个有责任心.有道德模范的前端必须具备的好习惯, 可以大大提高代码的可维护性.可读性. java代码注释快捷键:ctrl+shift+/首先熟悉一下html.css.js的注释的写法 ...