zoukankan      html  css  js  c++  java
  • 分布式缓存学习总结

    一、分布式缓存简图

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

    三、Memcached基础原理

    四、Memcache下载与安装

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

    六、Redis和Memcache的区别总结

    一、分布式缓存简图

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

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

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

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

    3、多客户端共享缓存

    4、Net+Memory  >> IO

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

    6、超简单集群搭建 Clister

    7、开源 Open Source

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

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

    9、学习成本非常低,入门非常容易

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

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

    三、Memcached基础原理

    底层通信是使用Socket

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

    四、Memcache下载与安装

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

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

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

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

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

      

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

    namespace 测试
    {
    public class MemcacheTset
    {
    public static void Test()
    {
    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

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

    Console.WriteLine("---------测试----------");
    mc.Set("test","my value");//存储数据到缓存服务器,这里将字符串"my value"缓存,key

    if (mc.KeyExists("test"))//测试缓存存在key为test的项目
    {
    Console.WriteLine("test is Exists");
    Console.WriteLine(mc.Get("test").ToString());//在缓存中获取key为test的项目
    }
    else
    {
    Console.WriteLine("test not Exists");
    }

    Console.ReadLine();

    mc.Delete("test");//移除缓存中key为test的项目

    if (mc.KeyExists("test"))
    {
    Console.WriteLine("test is Exists");
    Console.WriteLine(mc.Get("test").ToString());
    }
    else
    {
    Console.WriteLine("test not Exists");
    }

    Console.ReadLine();

    SockIOPool.GetInstance().Shutdown();//关闭池,关闭sockets
    }
    }
    }


    复制代码
     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 }
    复制代码

    如果程序运行正常,说明我们的Memcache服务已启动且运行正常。

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

    好了,现在我们就实际来应用下吧。

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

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

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

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

    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;
    }
    }
    }

    复制代码
     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 }
    复制代码
    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来操作。

    再来个辅助类吧。

    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性能突出,如果能够熟练的驾驭,对国内很多大型应用具有很大帮助。

  • 相关阅读:
    7月的尾巴,你是XXX
    戏说Android view 工作流程《下》
    “燕子”
    Android开机动画bootanimation.zip
    戏说Android view 工作流程《上》
    ViewController里已连接的IBOutlet为什么会是nil
    My first App "Encrypt Wheel" is Ready to Download!
    iOS开发中角色Role所产生的悲剧(未完)
    UIScrollView实现不全屏分页的小技巧
    Apple misunderstood my app,now my app status changed to “In Review”
  • 原文地址:https://www.cnblogs.com/Alex80/p/5180923.html
Copyright © 2011-2022 走看看