分布式缓存学习总结— —竹子整理

1、分布式缓存简图linux

2、为何使用Memcached分布式缓存呢?web

3、Memcached基础原理数据库

4、Memcache下载与安装windows

5、MencacheHelper.cs 示例使用 结合Session与项目配置缓存设计模式

6、Redis和Memcache的区别总结缓存

 

1、分布式缓存简图服务器

 

 

2、为何使用Memcached分布式缓存呢?数据结构

首先先讲讲为什么要缓存,在数据驱动的web开发中,常常要重复从数据库中取出相同的数据,这种重复极大的增长了数据库负载。缓存是解决这个问题的好办法。可是ASP.NET中的虽然已经能够实现对页面局部进行缓存,但仍是不够灵活。Memcached应运而生。架构

 

1、高并发访问数据库的痛楚:死锁!并发

2、磁盘IO之痛,数据库读写说白了就是跟磁盘打交道,磁盘读取速度是有限制的,通常高点也就7200

3、多客户端共享缓存

4Net+Memory  >> IO

5、读写性能完美 1s 读取能够达到1w次 写:10w

6、超简单集群搭建 Clister

7、开源 Open Source

8、没有提供主从赋值功能,也没提供容灾等功能(容灾:即数据备份能使意外发生后恢复数据,Memcached不会进行备份,因为是缓存在内存中的,一断电就会失去数据

 

),因此全部的代码基本都只考虑性能最佳。如要考虑容灾,则可以使用Redis分布式缓存

9、学习成本很是低,入门很是容易

10、丰富的成功的案例。不少大型公司都是用这个来作分布式缓存

注:Memcached在企业中通常都是在linux下跑,才能达到性能最佳。

 

3、Memcached基础原理

底层通讯是使用Socket

能够将缓存服务器理解为Socket服务端,将WEB服务器理解为客户端。

 

4、Memcache下载与安装

下载,百度一下或者直接在csdn上搜一下windows Memcache稳定版就行,0积分。

1、下载完后,就这么个exe

 

2、安装,敲几行cmd命令就行,截图以下,左边是咱们计算机服务列表,能够看到,已经启动了咱们的MemcachedMemcache是这个开源项目的名称,加个dMemcached是具体这个应用程序,也就是这个exe的名称)

 

三、如今已经启动服务了,且将其安装到windows服务上了,这样一来,就不用每次手动去启动了,随电脑而启动。

 

如今来测试下,随便新建个控制台应用程序

  

 1 using Memcached.ClientLibrary;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace 测试
 9 {
10     public class MemcacheTset
11     {
12         public static void Test()
13         {
14             string[] serverlist = { "127.0.0.1:11211" }; //服务器列表,可多个 用逗号隔开     
15             
16             //初始化池
17             SockIOPool pool = SockIOPool.GetInstance();
18 
19             //根据实际状况修改下面参数
20             pool.SetServers(serverlist);
21             pool.InitConnections = 3;
22             pool.MinConnections = 3;
23             pool.MaxConnections = 5;
24             pool.SocketConnectTimeout = 1000;
25             pool.SocketTimeout = 3000;
26             pool.MaintenanceSleep = 30;
27             pool.Failover = true;
28             pool.Nagle = false;
29             pool.Initialize(); // initialize the pool for memcache servers  
30          
31             //得到客户端实例
32             MemcachedClient mc = new MemcachedClient();//初始化一个客户端 
33             mc.EnableCompression = false;
34 
35             Console.WriteLine("---------测试----------");
36             mc.Set("test","my value");//存储数据到缓存服务器,这里将字符串"my value"缓存,key
37 
38             if (mc.KeyExists("test"))//测试缓存存在key为test的项目
39             {
40                 Console.WriteLine("test is Exists");
41                 Console.WriteLine(mc.Get("test").ToString());//在缓存中获取key为test的项目
42             }
43             else 
44             {
45                 Console.WriteLine("test not Exists");
46             }
47 
48             Console.ReadLine();
49 
50             mc.Delete("test");//移除缓存中key为test的项目
51 
52             if (mc.KeyExists("test"))
53             {
54                 Console.WriteLine("test is Exists");
55                 Console.WriteLine(mc.Get("test").ToString());
56             }
57             else
58             {
59                 Console.WriteLine("test not Exists");
60             }
61 
62             Console.ReadLine();
63 
64             SockIOPool.GetInstance().Shutdown();//关闭池,关闭sockets
65         }
66     }
67 }
View Code

若是程序运行正常,说明咱们的Memcache服务已启动且运行正常。

有一点,别忘咯,就是咱们Memcache的驱动,一样,百度C# Memcache安装就OK。

好了,如今咱们就实际来应用下吧。

5、MencacheHelper.cs 示例使用 结合项目配置缓存

对于什么是项目配置,相信你们确定都熟悉的,就是将业务中不常常更改的数据如系统邮件地址,将其存在数据库中,方便更改。

而后,回头看这几个字,“不常常更改”,咱们就得注意了,这样的数据,咱们就得想到缓存了,缓存就是用来管理这样的数据。而后呢,咱们就可使用咱们的Memcache来管理它了。

项目中,咱们能够新建个MemcacheHelper类来封装下代码,我写了个最简单的存取删除。

 1 using Memcached.ClientLibrary;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace JOEY.BookShop.Common
 9 {
10     public class MemcacheHelper
11     {
12         private static readonly MemcachedClient mc;
13         static MemcacheHelper()
14         {
15             string[] serverlist = { "127.0.0.1:11211" }; //服务器列表,可多个 用逗号隔开     
16 
17             //初始化池
18             SockIOPool pool = SockIOPool.GetInstance();
19 
20             //根据实际状况修改下面参数
21             pool.SetServers(serverlist);
22             pool.InitConnections = 3;
23             pool.MinConnections = 3;
24             pool.MaxConnections = 5;
25             pool.SocketConnectTimeout = 1000;
26             pool.SocketTimeout = 3000;
27             pool.MaintenanceSleep = 30;
28             pool.Failover = true;
29             pool.Nagle = false;
30             pool.Initialize(); // initialize the pool for memcache servers  
31 
32             //得到客户端实例
33             mc = new MemcachedClient();//初始化一个客户端 
34             mc.EnableCompression = false;
35         }
36 
37         /// <summary>
38         ///39         /// </summary>
40         /// <param name="key"></param>
41         /// <param name="value"></param>
42         public static void Set(string key, object value)
43         {
44             mc.Set(key, value);
45         }
46 
47         public static void Set(string key, object value, DateTime time)
48         {
49             mc.Set(key, value, time);
50         }
51 
52         /// <summary>
53         ///54         /// </summary>
55         /// <param name="key"></param>
56         /// <returns></returns>
57         public static object Get(string key)
58         {
59             if (mc.KeyExists(key))
60             {
61                 return mc.Get(key);
62             }
63             else
64             {
65                 return null;
66             }
67             
68         }
69 
70         /// <summary>
71         /// 删除
72         /// </summary>
73         /// <param name="key"></param>
74         /// <returns></returns>
75         public static bool Delete(string key)
76         {
77             if (mc.KeyExists(key))
78             {
79                 mc.Delete(key);
80                 return true;
81             }
82             return false;
83         }
84     }
85 }
View Code

 

1 var setting = this.DbSession.SettingsDal.LoadEntities(c => c.Name == "系统邮件地址").FirstOrDefault();
2 string value = setting.Value.Trim();
3 MemcacheHelper.Set("setting_" + key, value);

这样就将咱们的从数据库中取出的系统邮件地址存储到了Memcache中,很方便吧。取的话就是:

object obj = MemcacheHelper.Get("setting_" + "系统邮件地址");

这里因为存取的数据均为字符串,不存在序列化的问题,若是存取的对象类型不是字符串,如某个表Model,那么就得经过序列化来进行操做,对于序列化,本人是使用Json.Net来操做。

再来个辅助类吧。

 1 using Newtonsoft.Json;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace JOEY.BookShop.Common
 9 {
10     /// <summary>
11     /// Json.Net 序列化,对于因为相互引用类型致使的序列化死循环,可在该对象上加个特性标签[JsonIgnore] 如在Model中有外键,两个模型间相互引用即形成该问题
12     /// </summary>
13     public class SerializeHelper
14     {
15         /// <summary>
16         /// 传入对象,序列化成字符串返回
17         /// </summary>
18         /// <param name="obj"></param>
19         /// <returns></returns>
20         public static string SerializeToString(object obj)
21         {
22             return JsonConvert.SerializeObject(obj);
23         }
24         /// <summary>
25         /// 传入序列化字符串,反序列化成对应对象返回
26         /// </summary>
27         /// <typeparam name="T">泛型,对应对象类型</typeparam>
28         /// <param name="serializeStr">序列化后的字符串</param>
29         /// <returns></returns>
30         public static T DeserializeToObject<T>(string serializeStr)
31         {
32             return JsonConvert.DeserializeObject<T>(serializeStr);
33         }
34     }
35 }
View Code

很容易就能看懂,对吧。固然驱动也是须要的。一样百度哦。

这里有个小问题,这个程序集,Newtonsoft.Json在个人MVC项目中自己就存在,而我在其余项目(即项目Common)中用的时候用的网上下的,选的版本是4.5,因为MVC项目引用了Common,这样貌似就出现了版本不一致的状况,貌似是这样,会提示错误。因而我把MVC中的dll给删除了,从新加载Common下的dll(如今想一想我为何不把Common下的删了,去引用MVC下这个呢- -),这样一来,又出现一个问题,未能加载文件或程序集“Newtonsoft.Json,Version=4.5.0.0。估计是配置项的缘由,因而,百度了下,在web.config runtime节点下添加了这么几行,修改后为:

<dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
      </dependentAssembly>

8.0.0.0是当前这个dll的版本,估计是版本更新通知吧。具体为什么这样作是有点迷糊的。谁能指点下呢,感激~~~

6、RedisMemcache的区别总结(摘自百度知道)

1. Redis是什么

这个问题的结果影响了咱们怎么用Redis。若是你认为Redis是一个key value store, 那可能会用它来代替MySQL;若是认为它是一个能够持久化的cache, 可能只是它保存一些频繁访问的临时数据。RedisREmote DIctionary Server的缩写,在Redis在官方网站的的副标题是A persistent key-value database with built-in net interface written in ANSI-C for Posix systems,这个定义偏向key value store。还有一些见解则认为Redis是一个memory database,由于它的高性能都是基于内存操做的基础。另一些人则认为Redis是一个data structure server,由于Redis支持复杂的数据特性,好比List, Set等。对Redis的做用的不一样解读决定了你对Redis的使用方式。

互联网数据目前基本使用两种方式来存储,关系数据库或者key value。可是这些互联网业务自己并不属于这两种数据类型,好比用户在社会化平台中的关系,它是一个list,若是要用关系数据库存储就须要转换成一种多行记录的形式,这种形式存在不少冗余数据,每一行须要存储一些重复信息。若是用key value存储则修改和删除比较麻烦,须要将所有数据读出再写入。Redis在内存中设计了各类数据类型,让业务可以高速原子的访问这些数据结构,而且不须要关心持久存储的问题,从架构上解决了前面两种存储须要走一些弯路的问题。

2. Redis不可能比Memcache

不少开发者都认为Redis不可能比Memcached快,Memcached彻底基于内存,而Redis具备持久化保存特性,即便是异步的,Redis也不可能比Memcached快。可是测试结果基本是Redis占绝对优点。一直在思考这个缘由,目前想到的缘由有这几方面。

Libevent。和Memcached不一样,Redis并无选择libeventLibevent为了迎合通用性形成代码庞大(目前Redis代码还不到libevent1/3)及牺牲了在特定平台的很多性能。Redislibevent中两个文件修改实现了本身的epoll event loop(4)。业界很多开发者也建议Redis使用另一个libevent高性能替代libev,可是做者仍是坚持Redis应该小巧并去依赖的思路。一个印象深入的细节是编译Redis以前并不须要执行./configure

CAS问题。CASMemcached中比较方便的一种防止竞争修改资源的方法。CAS实现须要为每一个cache key设置一个隐藏的cas tokencas至关value版本号,每次settoken须要递增,所以带来CPU和内存的双重开销,虽然这些开销很小,可是到单机10G+ cache以及QPS上万以后这些开销就会给双方相对带来一些细微性能差异(5)

3. 单台Redis的存放数据必须比物理内存小

Redis的数据所有放在内存带来了高速的性能,可是也带来一些不合理之处。好比一个中型网站有100万注册用户,若是这些资料要用Redis来存储,内存的容量必须可以容纳这100万用户。可是业务实际状况是100万用户只有5万活跃用户,1周来访问过1次的也只有15万用户,所以所有100万用户的数据都放在内存有不合理之处,RAM须要为冷数据买单。

这跟操做系统很是类似,操做系统全部应用访问的数据都在内存,可是若是物理内存容纳不下新的数据,操做系统会智能将部分长期没有访问的数据交换到磁盘,为新的应用留出空间。现代操做系统给应用提供的并非物理内存,而是虚拟内存(Virtual Memory)的概念。

基于相同的考虑,Redis 2.0也增长了VM特性。让Redis数据容量突破了物理内存的限制。并实现了数据冷热分离。

4. RedisVM实现是重复造轮子

RedisVM依照以前的epoll实现思路依旧是本身实现。可是在前面操做系统的介绍提到OS也能够自动帮程序实现冷热数据分离,Redis只须要OS申请一块大内存,OS会自动将热数据放入物理内存,冷数据交换到硬盘,另一个知名的“理解了现代操做系统(3)”的Varnish就是这样实现,也取得了很是成功的效果。

做者antirez在解释为何要本身实现VM中提到几个缘由(6)。主要OSVM换入换出是基于Page概念,好比OS VM1Page4K, 4K中只要还有一个元素即便只有1个字节被访问,这个页也不会被SWAP, 换入也一样道理,读到一个字节可能会换入4K无用的内存。而Redis本身实现则能够达到控制换入的粒度。另外访问操做系统SWAP内存区域时block进程,也是致使Redis要本身实现VM缘由之一。

5. get/set方式使用Redis

做为一个key value存在,不少开发者天然的使用set/get方式来使用Redis,实际上这并非最优化的使用方法。尤为在未启用VM状况下,Redis所有数据须要放入内存,节约内存尤为重要。

假如一个key-value单元须要最小占用512字节,即便只存一个字节也占了512字节。这时候就有一个设计模式,能够把key复用,几个key-value放入一个key中,value再做为一个set存入,这样一样512字节就会存放10-100倍的容量。

这就是为了节约内存,建议使用hashset而不是set/get的方式来使用Redis,详细方法见参考文献(7)

6. 使用aof代替snapshot

Redis有两种存储方式,默认是snapshot方式,实现方法是定时将内存的快照(snapshot)持久化到硬盘,这种方法缺点是持久化以后若是出现crash则会丢失一段数据。所以在完美主义者的推进下做者增长了aof方式。aofappend only mode,在写入内存数据的同时将操做命令保存到日志文件,在一个并发更改上万的系统中,命令日志是一个很是庞大的数据,管理维护成本很是高,恢复重建时间会很是长,这样致使失去aof高可用性本意。另外更重要的是Redis是一个内存数据结构模型,全部的优点都是创建在对内存复杂数据结构高效的原子操做上,这样就看出aof是一个很是不协调的部分。

其实aof目的主要是数据可靠性及高可用性,在Redis中有另一种方法来达到目的:Replication。因为Redis的高性能,复制基本没有延迟。这样达到了防止单点故障及实现了高可用。

小结

要想成功使用一种产品,咱们须要深刻了解它的特性。Redis性能突出,若是可以熟练的驾驭,对国内不少大型应用具备很大帮助。

相关文章
相关标签/搜索