diff --git a/NewLife.Core/Caching/Readme.MD b/NewLife.Core/Caching/Readme.MD new file mode 100644 index 0000000000..0c0d97d008 --- /dev/null +++ b/NewLife.Core/Caching/Readme.MD @@ -0,0 +1,147 @@ +缓存架构以ICache接口为核心,包括MemoryCache、Redis和DbCache实现! +后续例程与使用说明均以Redis为例,各缓存实现类似。 + +### 内存缓存 MemoryCache +MemoryCache核心是并行字典ConcurrentDictionary,由于省去了序列化和网络通信,使得它具有千万级超高性能。 +MemoryCache支持过期时间,默认容量10万个,未过期key超过该值后,每60秒根据LRU清理溢出部分。 +常用于进程内千万级以下数据缓存场景。 + +```csharp +// 缓存默认实现Cache.Default是MemoryCache,可修改 +//var ic = Cache.Default; +//var ic = new MemoryCache(); +``` + +### 基础 Redis +Redis实现标准协议以及基础字符串操作,完整实现由独立开源项目[NewLife.Redis](https://github.com/NewLifeX/NewLife.Redis)提供。 +采取连接池加同步阻塞架构,具有超低延迟(200~600us)以及超高吞吐量的特点。 +在物流行业大数据实时计算中广泛应有,经过日均100亿次调用量验证。 + +```csharp +// 实例化Redis,默认端口6379可以省略,密码有两种写法 +//var ic = Redis.Create("127.0.0.1", 7); +var ic = Redis.Create("pass@127.0.0.1:6379", 7); +//var ic = Redis.Create("server=127.0.0.1:6379;password=pass", 7); +ic.Log = XTrace.Log; // 调试日志。正式使用时注释 +``` + +### 数据库 DbCache +DbCache属于实验性质,采用数据库存储数据,默认SQLite。 + +### 基本操作 +在基本操作之前,我们先做一些准备工作: ++ 新建控制台项目,并在入口函数开头加上 `XTrace.UseConsole();` ,这是为了方便查看调试日志 ++ 具体测试代码之前,需要加上前面MemoryCache或Redis的实例化代码 ++ 准备一个模型类User +```csharp +class User +{ + public String Name { get; set; } + public DateTime CreateTime { get; set; } +} +``` + +添删改查: +```csharp +var user = new User { Name = "NewLife", CreateTime = DateTime.Now }; +ic.Set("user", user, 3600); +var user2 = ic.Get("user"); +XTrace.WriteLine("Json: {0}", ic.Get("user")); +if (ic.ContainsKey("user")) XTrace.WriteLine("存在!"); +ic.Remove("user"); +``` + +执行结果: +```csharp +14:14:25.990 1 N - SELECT 7 +14:14:25.992 1 N - => OK +14:14:26.008 1 N - SETEX user 3600 [53] +14:14:26.021 1 N - => OK +14:14:26.042 1 N - GET user +14:14:26.048 1 N - => [53] +14:14:26.064 1 N - GET user +14:14:26.065 1 N - => [53] +14:14:26.066 1 N - Json: {"Name":"NewLife","CreateTime":"2018-09-25 14:14:25"} +14:14:26.067 1 N - EXISTS user +14:14:26.068 1 N - => 1 +14:14:26.068 1 N - 存在! +14:14:26.069 1 N - DEL user +14:14:26.070 1 N - => 1 +``` + +保存复杂对象时,默认采用Json序列化,所以上面可以按字符串把结果取回来,发现正是Json字符串。 +Redis的strings,实质上就是带有长度前缀的二进制数据,[53]表示一段53字节长度的二进制数据。 + +### 集合操作 +GetAll/SetAll 在Redis上是很常用的批量操作,同时获取或设置多个key,一般有10倍以上吞吐量。 + +批量操作: +```csharp +var dic = new Dictionary +{ + ["name"] = "NewLife", + ["time"] = DateTime.Now, + ["count"] = 1234 +}; +ic.SetAll(dic, 120); + +var vs = ic.GetAll(dic.Keys); +XTrace.WriteLine(vs.Join(",", e => $"{e.Key}={e.Value}")); +``` + +执行结果: +```csharp +MSET name NewLife time 2018-09-25 15:56:26 count 1234 +=> OK +EXPIRE name 120 +EXPIRE time 120 +EXPIRE count 120 +MGET name time count +name=NewLife,time=2018-09-25 15:56:26,count=1234 +``` + +集合操作里面还有 `GetList/GetDictionary/GetQueue/GetSet` 四个类型集合,分别代表Redis的列表、哈希、队列、Set集合等。 +基础版Redis不支持这四个集合,完整版[NewLife.Redis](https://github.com/NewLifeX/NewLife.Redis)支持,MemoryCache则直接支持。 + +### 高级操作 ++ Add 添加,当key不存在时添加,已存在时返回false。 ++ Replace 替换,替换已有值为新值,返回旧值。 ++ Increment 累加,原子操作 ++ Decrement 递减,原子操作 + +高级操作: +```csharp +var flag = ic.Add("count", 5678); +XTrace.WriteLine(flag ? "Add成功" : "Add失败"); +var ori = ic.Replace("count", 777); +var count = ic.Get("count"); +XTrace.WriteLine("count由{0}替换为{1}", ori, count); + +ic.Increment("count", 11); +var count2 = ic.Decrement("count", 10); +XTrace.WriteLine("count={0}", count2); +``` + +执行结果: +```csharp +SETNX count 5678 +=> 0 +Add失败 +GETSET count 777 +=> 1234 +GET count +=> 777 +count由1234替换为777 +INCRBY count 11 +=> 788 +DECRBY count 10 +=> 778 +count=778 +``` + +### 性能测试 +Bench 会分根据线程数分多组进行添删改压力测试。 +rand 参数,是否随机产生key/value。 +batch 批大小,分批执行读写操作,借助GetAll/SetAll进行优化。 + +Redis默认设置AutoPipeline=100,无分批时打开管道操作,对添删改优化。 \ No newline at end of file diff --git a/NewLife.Core/NewLife.Core.csproj b/NewLife.Core/NewLife.Core.csproj index 2d0919b769..0633c0edcc 100644 --- a/NewLife.Core/NewLife.Core.csproj +++ b/NewLife.Core/NewLife.Core.csproj @@ -328,6 +328,7 @@ SerialPortList.cs + diff --git a/Test/Program.cs b/Test/Program.cs index e66d62bb7c..5b32855cf3 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -11,7 +11,6 @@ using NewLife.Remoting; using NewLife.Security; using NewLife.Serialization; -using NewLife.Threading; using XCode.DataAccessLayer; using XCode.Membership; using XCode.Service; @@ -138,24 +137,6 @@ static void Test2() Console.WriteLine(gs.First().Logins); } - class CacheItem - { - /// 数值 - public TValue Value { get; set; } - - /// 过期时间 - public DateTime ExpiredTime { get; set; } - - /// 是否过期 - public Boolean Expired => ExpiredTime <= TimerX.Now; - - public CacheItem(TValue value, Int32 seconds) - { - Value = value; - if (seconds > 0) ExpiredTime = TimerX.Now.AddSeconds(seconds); - } - } - static void Test3() { var svr = new ApiServer(3344) @@ -295,10 +276,51 @@ static void Test5() static void Test6() { - var rds = Redis.Create("127.0.0.1", 7); + // 缓存默认实现Cache.Default是MemoryCache,可修改 + //var ic = Cache.Default; + //var ic = new MemoryCache(); + + // 实例化Redis,默认端口6379可以省略,密码有两种写法 + var ic = Redis.Create("127.0.0.1", 7); + //var ic = Redis.Create("pass@127.0.0.1:6379", 7); + //var ic = Redis.Create("server=127.0.0.1:6379;password=pass", 7); + ic.Log = XTrace.Log; // 调试日志。正式使用时注释 + + var user = new User { Name = "NewLife", CreateTime = DateTime.Now }; + ic.Set("user", user, 3600); + var user2 = ic.Get("user"); + XTrace.WriteLine("Json: {0}", ic.Get("user")); + if (ic.ContainsKey("user")) XTrace.WriteLine("存在!"); + ic.Remove("user"); + + var dic = new Dictionary + { + ["name"] = "NewLife", + ["time"] = DateTime.Now, + ["count"] = 1234 + }; + ic.SetAll(dic, 120); + + var vs = ic.GetAll(dic.Keys); + XTrace.WriteLine(vs.Join(",", e => $"{e.Key}={e.Value}")); + + var flag = ic.Add("count", 5678); + XTrace.WriteLine(flag ? "Add成功" : "Add失败"); + var ori = ic.Replace("count", 777); + var count = ic.Get("count"); + XTrace.WriteLine("count由{0}替换为{1}", ori, count); - //rds.AutoPipeline = 1000; - rds.Bench(); + ic.Increment("count", 11); + var count2 = ic.Decrement("count", 10); + XTrace.WriteLine("count={0}", count2); + + //ic.Bench(); + } + + class User + { + public String Name { get; set; } + public DateTime CreateTime { get; set; } } static void Test7()