首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

内存缓存MemoryCache

内存缓存MemoryCache实现了ICache接口,Redis同样实现了ICache接口,两者在缓存操作上达到了高度抽象统一。应用设计时一律使用ICache接口,开发环境装配为MemoryCache,生产环境根据分布式需要可以装配为Redis。如果应用系统没有分布式需求,继续使用MemoryCache更好。

超高性能

MemoryCache核心是并行字典ConcurrentDictionary,由于省去了序列化和网络通信,使得它具有千万级超高性能(普通台式机实测2.87亿tps)。

MemoryCache支持过期时间,默认容量10万个,未过期key超过该值后,每60秒根据LRU清理溢出部分。

常用于进程内千万级以下数据缓存场景。

本机测试数据如下(I9-10900K):

Testv1.0.0.1130Build2021-01-3119:33:32.NETCoreApp,Version=v5.

Test

Memory性能测试[顺序],批大小[],逻辑处理器20个

测试10,000,000项,1线程

赋值耗时934ms速度10,706,638ops

读取耗时989ms速度10,111,223ops

删除耗时310ms速度32,258,064ops

累加耗时584ms速度17,123,287ops

测试20,000,000项,2线程

赋值耗时927ms速度21,574,973ops

读取耗时1,024ms速度19,531,250ops

删除耗时319ms速度62,695,924ops

累加耗时594ms速度33,670,033ops

测试40,000,000项,4线程

赋值耗时1,011ms速度39,564,787ops

读取耗时1,039ms速度38,498,556ops

删除耗时1,636ms速度24,449,877ops

累加耗时608ms速度65,789,473ops

测试80,000,000项,8线程

赋值耗时989ms速度80,889,787ops

读取耗时1,227ms速度65,199,674ops

删除耗时1,858ms速度43,057,050ops

累加耗时675ms速度118,518,518ops

测试200,000,000项,20线程

赋值耗时1,644ms速度121,654,501ops

读取耗时1,807ms速度110,680,686ops

删除耗时2,936ms速度68,119,891ops

累加耗时1,569ms速度127,469,725ops

测试200,000,000项,64线程

赋值耗时1,686ms速度118,623,962ops

读取耗时1,877ms速度106,553,010ops

删除耗时695ms速度287,769,784ops

累加耗时1,585ms速度126,182,965ops

总测试数据:2,200,000,042

ICache接口

ICache是缓存抽象接口,主要实现是MemoryCache和Redis

/// 缓存接口

publicinterfaceICache

{

#region属性

/// 名称

StringName{get; }

/// 默认缓存时间。默认0秒表示不过期

Int32Expire{get;set; }

/// 获取和设置缓存,永不过期

///

///

Objectthis[Stringkey] {get;set; }

/// 缓存个数

Int32Count{get; }

/// 所有键

ICollectionKeys{get; }

#endregion

#region基础操作

/// 是否包含缓存项

///

///

BooleanContainsKey(Stringkey);

/// 设置缓存项

/// 键

/// 值

/// 过期时间,秒。小于0时采用默认缓存时间

///

BooleanSet(Stringkey,Tvalue,Int32expire=-1);

/// 设置缓存项

/// 键

/// 值

/// 过期时间

///

BooleanSet(Stringkey,Tvalue,TimeSpanexpire);

/// 获取缓存项

/// 键

///

TGet(Stringkey);

/// 批量移除缓存项

/// 键集合

///

Int32Remove(paramsString[]keys);

/// 清空所有缓存项

voidClear();

/// 设置缓存项有效期

/// 键

/// 过期时间

BooleanSetExpire(Stringkey,TimeSpanexpire);

/// 获取缓存项有效期

/// 键

///

TimeSpanGetExpire(Stringkey);

#endregion

#region集合操作

/// 批量获取缓存项

///

///

///

IDictionaryGetAll(IEnumerablekeys);

/// 批量设置缓存项

///

///

/// 过期时间,秒。小于0时采用默认缓存时间

voidSetAll(IDictionaryvalues,Int32expire=-1);

/// 获取列表

/// 元素类型

/// 键

///

IListGetList(Stringkey);

/// 获取哈希

/// 元素类型

/// 键

///

IDictionaryGetDictionary(Stringkey);

/// 获取队列

/// 元素类型

/// 键

///

IProducerConsumerGetQueue(Stringkey);

/// 获取栈

/// 元素类型

/// 键

///

IProducerConsumerGetStack(Stringkey);

/// 获取Set

///

///

///

ICollectionGetSet(Stringkey);

#endregion

#region高级操作

/// 添加,已存在时不更新

/// 值类型

/// 键

/// 值

/// 过期时间,秒。小于0时采用默认缓存时间

///

BooleanAdd(Stringkey,Tvalue,Int32expire=-1);

/// 设置新值并获取旧值,原子操作

///

/// 常常配合Increment使用,用于累加到一定数后重置归零,又避免多线程冲突。

///

/// 值类型

/// 键

/// 值

///

TReplace(Stringkey,Tvalue);

/// 尝试获取指定键,返回是否包含值。有可能缓存项刚好是默认值,或者只是反序列化失败,解决缓存穿透问题

/// 值类型

/// 键

/// 值。即使有值也不一定能够返回,可能缓存项刚好是默认值,或者只是反序列化失败

/// 返回是否包含值,即使反序列化失败

BooleanTryGetValue(Stringkey,outTvalue);

/// 累加,原子操作

/// 键

/// 变化量

///

Int64Increment(Stringkey,Int64value);

/// 累加,原子操作

/// 键

/// 变化量

///

DoubleIncrement(Stringkey,Doublevalue);

/// 递减,原子操作

/// 键

/// 变化量

///

Int64Decrement(Stringkey,Int64value);

/// 递减,原子操作

/// 键

/// 变化量

///

DoubleDecrement(Stringkey,Doublevalue);

#endregion

#region事务

/// 提交变更。部分提供者需要刷盘

///

Int32Commit();

/// 申请分布式锁

/// 要锁定的key

///

///

IDisposableAcquireLock(Stringkey,Int32msTimeout);

#endregion

#region性能测试

/// 多线程性能测试

/// 随机读写。顺序,每个线程多次操作一个key;随机,每个线程每次操作不同key

/// 批量操作。默认0不分批,分批仅针对随机读写,对顺序读写的单key操作没有意义

Int64Bench(Booleanrand=false,Int32batch=);

#endregion

}

基本用法

添删改查基本功能,Get/Set/Count/ContainsKey/Remove

varic=newMemoryCache();

varkey="Name";

varkey2="Company";

ic.Set(key,"大石头");

ic.Set(key2,"新生命");

Assert.Equal("大石头",ic.Get(key));

Assert.Equal("新生命",ic.Get(key2));

varcount=ic.Count;

Assert.True(count>=2);

// Keys

varkeys=ic.Keys;

Assert.True(keys.Contains(key));

// 过期时间

ic.SetExpire(key,TimeSpan.FromSeconds(1));

varts=ic.GetExpire(key);

Assert.True(ts.TotalSeconds>&&ts.TotalSeconds

varrs=ic.Remove(key2);

Assert.Equal(1,rs);

Assert.False(ic.ContainsKey(key2));

ic.Clear();

Assert.True(ic.Count==);

其中Set的第三个参数支持过期时间,单位秒。

集合操作

SetAll/GetAll 是高吞吐的关键,其中SetAll第二参数支持过期时间,单位秒

varic=newMemoryCache();

vardic=newDictionary

{

["111"]="123",

["222"]="abc",

["大石头"]="学无先后达者为师"

};

ic.SetAll(dic);

vardic2=ic.GetAll(dic.Keys);

Assert.Equal(dic.Count,dic2.Count);

foreach(varitemindic)

{

Assert.Equal(item.Value,dic2[item.Key]);

}

高级操作

MemoryCache有几个非常好用的高级操作,全部都是线程安全:

Add。添加,已存在时不更新,常用于锁争夺。例如,可用于判断指定订单是否处理过,加上过期时间,就是我们经常说的多少小时去重。

Replace。设置新值并获取旧值,原子操作

TryGetValue。尝试获取指定键,返回是否包含值。有可能缓存项刚好是默认值

Increment。累加

Decrement。累减

缓存过期策略

MemoryCache内置LRU淘汰算法,当缓存项超过最大值Capacity(默认10万)时,剔除最久未使用的缓存项,以避免内存占用过大。

缓存项未达到最大值Capacity时,MemoryCache定时检查并剔除过期项。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210131A0A7PQ00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券