您的位置:首页 > 其它

分布式缓存学习总结

2016-02-04 08:56 295 查看
一、分布式缓存简图

二、为什么使用Memcached分布式缓存呢?

三、Memcached基础原理

四、Memcache下载与安装

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

六、Redis和Memcache的区别总结

一、分布式缓存简图

[b]
using Memcached.ClientLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JOEY.BookShop.Common
{
public class MemcacheHelper
{
private static readonly MemcachedClient mc;
static MemcacheHelper()
{
string[] serverlist = { "127.0.0.1:11211" }; //服务器列表,可多个 用逗号隔开

//初始化池
SockIOPool pool = SockIOPool.GetInstance();

//根据实际情况修改下面参数
pool.SetServers(serverlist);
pool.InitConnections = 3;
pool.MinConnections = 3;
pool.MaxConnections = 5;
pool.SocketConnectTimeout = 1000;
pool.SocketTimeout = 3000;
pool.MaintenanceSleep = 30;
pool.Failover = true;
pool.Nagle = false;
pool.Initialize(); // initialize the pool for memcache servers

//获得客户端实例
mc = new MemcachedClient();//初始化一个客户端
mc.EnableCompression = false;
}

/// <summary>
/// 存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public static void Set(string key, object value)
{
mc.Set(key, value);
}

public static void Set(string key, object value, DateTime time)
{
mc.Set(key, value, time);
}

/// <summary>
/// 取
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static object Get(string key)
{
if (mc.KeyExists(key))
{
return mc.Get(key);
}
else
{
return null;
}

}

/// <summary>
/// 删除
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Delete(string key)
{
if (mc.KeyExists(key))
{
mc.Delete(key);
return true;
}
return false;
}
}
}

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JOEY.BookShop.Common
{
/// <summary>
/// Json.Net 序列化,对于由于相互引用类型导致的序列化死循环,可在该对象上加个特性标签[JsonIgnore] 如在Model中有外键,两个模型间相互引用即造成该问题
/// </summary>
public class SerializeHelper
{
/// <summary>
/// 传入对象,序列化成字符串返回
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string SerializeToString(object obj)
{
return JsonConvert.SerializeObject(obj);
}
/// <summary>
/// 传入序列化字符串,反序列化成对应对象返回
/// </summary>
/// <typeparam name="T">泛型,对应对象类型</typeparam>
/// <param name="serializeStr">序列化后的字符串</param>
/// <returns></returns>
public static T DeserializeToObject<T>(string serializeStr)
{
return JsonConvert.DeserializeObject<T>(serializeStr);
}
}
}

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 }


很容易就能看懂,对吧。当然驱动也是需要的。同样百度哦。

这里有个小问题,这个程序集,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的版本,估计是版本更新通知吧。具体为何这样做是有点迷糊的。谁能指点下呢,感激~~~

六、Redis和Memcache的区别总结(摘自百度知道)

1. Redis是什么

这个问题的结果影响了我们怎么用Redis。如果你认为Redis是一个key value store, 那可能会用它来代替MySQL;如果认为它是一个可以持久化的cache, 可能只是它保存一些频繁访问的临时数据。Redis是REmote 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并没有选择libevent。Libevent为了迎合通用性造成代码庞大(目前Redis代码还不到libevent的1/3)及牺牲了在特定平台的不少性能。Redis用libevent中两个文件修改实现了自己的epoll event loop(4)。业界不少开发者也建议Redis使用另外一个libevent高性能替代libev,但是作者还是坚持Redis应该小巧并去依赖的思路。一个印象深刻的细节是编译Redis之前并不需要执行./configure。

CAS问题。CAS是Memcached中比较方便的一种防止竞争修改资源的方法。CAS实现需要为每个cache key设置一个隐藏的cas token,cas相当value版本号,每次set会token需要递增,因此带来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. Redis的VM实现是重复造轮子

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

作者antirez在解释为什么要自己实现VM中提到几个原因(6)。主要OS的VM换入换出是基于Page概念,比如OS VM1个Page是4K, 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方式。aof即append only mode,在写入内存数据的同时将操作命令保存到日志文件,在一个并发更改上万的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高,恢复重建时间会非常长,这样导致失去aof高可用性本意。另外更重要的是Redis是一个内存数据结构模型,所有的优势都是建立在对内存复杂数据结构高效的原子操作上,这样就看出aof是一个非常不协调的部分。

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

小结

要想成功使用一种产品,我们需要深入了解它的特性。Redis性能突出,如果能够熟练的驾驭,对国内很多大型应用具有很大帮助。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: