使用Memcached提升.NET应用程序的性能(转)

原创做品,容许转载,转载时请务必以超连接形式标明文章   原始出处  、做者信息和本声明。不然将追究法律责任。 http://zhoufoxcn.blog.51cto.com/792419/528212

在应用程序运行的过程当中总会有一些常常须要访问而且变化不频繁的数据,若是每次获取这些数据都须要从数据库或者外部文件系统中去读取,性能确定会受到影响,因此一般的作法就是将这部分数据缓存起来,只要数据没有发生变化每次获取这些数据的时候直接从内存中区获取性能确定会大大地提升。在.NET中提供了一个Cache类能够实现这些功能。在ASP.NET中能够经过HttpContext 对象的 Cache 属性或 Page 对象的 Cache 属性来获取这个类的实例。 在大部分状况下咱们均可以使用Cache类来提升ASP.NET的性能,可是使用Cache类也有一些不足,好比咱们不能指定Cache类所占用的内存的大小,此外在Cache中缓存的数据没有办法被另外一台机器上的应用程序直接访问,所以在本文中提出另外一种数据缓存方案,那就是使用分布式缓存。分布式缓存的特色是缓存的数据没必要和应用程序在同一台机器上,从而大大加强了缓存数据的复用性。在本文介绍如何在.NET应用中使用Memcache做为分布式缓存。
Memcached介绍
Memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。在一般的应用中咱们都会将数据保存到数据库中,每次须要的时候都会从数据库去查询这些数据,若是应用程序的用户不少就会出现大量并发访问数据库的状况,这样就会增长应用程序的响应时间,使用Memcached就能够有效解决这个问题。memcached是高性能的分布式内存缓存服务器。通常的使用目的是,经过缓存数据库查询结果,减小数据库访问次数,以提升动态Web应用的速度、提升可扩展性。像大名鼎鼎的Facebook网站就使用了Memcached。周公稍后会提供Windows平台上32位和64位的Memcached程序。
为了提升性能,Memcached中的数据都保存在Memcached内置的存储空间中。由于当Memcached重启会致使其中的数据所有丢失,因此通常的方案是将数据保存在数据库中,每次请求数据的时候先查看在Memcached有没有缓存,若是有就直接从缓存中取出数据;若是没有,就从数据库中取出数据返回给应用程序并将请求的数据缓存到Memcached中,这样一来下次请求相同的数据就能够直接从Memcached中读取而不用再去查数据库了;一旦对数据有更新,同时更新数据库和Memcached。
Memcached是一个命令行窗口程序,能够在命令行窗口中启动也能够封装在系统服务中启动。在启动Memcached时须要提供一些必须的参数,指定Memcached运行时监听的端口和最大使用的内存大小等。若是缓存的数据大小超过指定内存,那么Memcached就会按照LRU(Least Recently Used)算法自动“删除”不使用的缓存(标记为失效),新增的缓存数据就可使用这些标记为失效的数据所占用的内存,这样就不用担忧Memcached超出所指定内存的问题。此外,为了提升性能,在缓存数据过时后Memcached并非从物理内存中删除缓存的数据,仅仅在取出改数据的时候检查它是否已通过了有效期。
目前有多种平台的Memcached版本,好比Linux、FreeBSD、Solaris (memcached 1.2.5以上版本)、Mac OS X及Windows平台,在Windows平台上还有32位和64位版本。
Memcached有一套协议,利用这套协议能够对Memcached进行数据存取和查看Memcached的状态,不少程序语言都依据这套协议来操做Memcached,好比PHP、Java、C、C++及C#等。
获取了对应平台的Memcached版本就能够运行Memcached了。在这里仅以Windows平台上的32位Memcached为例。
运行Memcached:
memcached.exe -p 11121 -m 64
上面的命令是运行Memcached,指定它的监听端口是11121(这是它的默认端口,能够指定为其它大于1024的端口,由于小于1024的端口已经有了默认指定),最大使用内存为64m,若是启用了Windows防火墙,切记要在防火墙上打开这个端口。
在调试程序时可使用下面的命令行来运行:
memcached.exe -p 11121 -m 64 -vv
这样就会看到以下的结果:
slab class   1: chunk size     88 perslab 11915
slab class   2: chunk size    112 perslab  9362
slab class   3: chunk size    144 perslab  7281
slab class   4: chunk size    184 perslab  5698
slab class   5: chunk size    232 perslab  4519
slab class   6: chunk size    296 perslab  3542
slab class   7: chunk size    376 perslab  2788
slab class   8: chunk size    472 perslab  2221
slab class   9: chunk size    592 perslab  1771
slab class  10: chunk size    744 perslab  1409
slab class  11: chunk size    936 perslab  1120
slab class  12: chunk size   1176 perslab   891
slab class  13: chunk size   1472 perslab   712
slab class  14: chunk size   1840 perslab   569
slab class  15: chunk size   2304 perslab   455
slab class  16: chunk size   2880 perslab   364
slab class  17: chunk size   3600 perslab   291
slab class  18: chunk size   4504 perslab   232
slab class  19: chunk size   5632 perslab   186
slab class  20: chunk size   7040 perslab   148
slab class  21: chunk size   8800 perslab   119
slab class  22: chunk size  11000 perslab    95
slab class  23: chunk size  13752 perslab    76
slab class  24: chunk size  17192 perslab    60
slab class  25: chunk size  21496 perslab    48
slab class  26: chunk size  26872 perslab    39
slab class  27: chunk size  33592 perslab    31
slab class  28: chunk size  41992 perslab    24
slab class  29: chunk size  52496 perslab    19
slab class  30: chunk size  65624 perslab    15
slab class  31: chunk size  82032 perslab    12
slab class  32: chunk size 102544 perslab    10
slab class  33: chunk size 128184 perslab     8
slab class  34: chunk size 160232 perslab     6
slab class  35: chunk size 200296 perslab     5
slab class  36: chunk size 250376 perslab     4
slab class  37: chunk size 312976 perslab     3
slab class  38: chunk size 391224 perslab     2
slab class  39: chunk size 489032 perslab     2
<96 server listening
<112 server listening
<116 send buffer was 8192, now 268435456
<116 server listening (udp)
在客户端还能够经过telnet来查看和操做Memcached,前提是服务器端和客户端都支持Telnet协议,在Windows7和Windows2008中默认都不支持,须要在控制面板中安装和启用。
首先打开控制面板,而后点击“打开或关闭Windows功能”,以下图所示:
 html


点击“打开或关闭Windows功能”以后会看到当前系统启用的功能的状态,根据当前机器选择打开Telnet服务器端或者客户端功能,以下图所示:
 git

 
通过上面的操做以后就能够在客服端远程查看Memcached的状态或者操做Memcached了。下面的命令就是链接到Memcached:
telnet localhost 11121
链接以后会出现一个命令行窗口,在这个命令行窗口中输入"stats"就能够看到当前Memcached的状态,以下就是刚刚启动的Memcached的状态数据:
STAT pid 852
STAT uptime 1399
STAT time 1300979378
STAT version 1.2.5
STAT pointer_size 32
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 3
STAT total_connections 5
STAT connection_structures 4
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT evictions 0
STAT bytes_read 23
STAT bytes_written 415
STAT limit_maxbytes 67108864
STAT threads 1
END
经过这个数据咱们就能够了解Memcached的状态了。
这些数据所表明的意义以下:
pid:32u,服务器进程ID。 
uptime:32u, 服务器运行时间,单位秒。 
time :32u, 服务器当前的UNIX时间。
version :string, 服务器的版本号。 
curr_items :32u, 服务器当前存储的内容数量 Current number of items stored by the server 
total_items :32u, 服务器启动以来存储过的内容总数。
bytes :64u, 服务器当前存储内容所占用的字节数。
curr_connections :32u, 链接数量。 
total_connections :32u, 服务器运行以来接受的链接总数。
connection_structures:32u, 服务器分配的链接结构的数量。 
cmd_get :32u, 取回请求总数。 
cmd_set :32u, 存储请求总数。 
get_hits :32u, 请求成功的总次数。
get_misses :32u, 请求失败的总次数。
bytes_read :64u, 服务器从网络读取到的总字节数。
bytes_written :64u, 服务器向网络发送的总字节数。
limit_maxbytes :32u, 服务器在存储时被容许使用的字节总数。
上面的描述中32u和64u表示32位和64位无符号整数,string表示是string类型数据。github


在.NET中应用Memcached
有不少.NET版本的Memcached客户端程序,在这里周公使用的Enyim Memcached,能够到https://github.com/enyim/EnyimMemcached/下载最新的版本。
要想在项目中使用Memcached,须要添加对Enyim.Caching.dll的应用。除此以外,咱们可能还须要在config文件中配置Memcached的信息(也能够在程序代码中指定,但那样不灵活),以下就是一个config文件配置的例子:
 web

  1. <?xml version="1.0" encoding="utf-8" ?> 
  2. <configuration> 
  3.   <configSections> 
  4.     <sectionGroup name="enyim.com"> 
  5.       <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" /> 
  6.     </sectionGroup> 
  7.   </configSections> 
  8.   <enyim.com protocol="Binary"> 
  9.     <memcached> 
  10.       <servers> 
  11.         <add address="localhost" port="11121" /> 
  12.         <!--<add address="localhost" port="11131" /> 
  13.         <add address="localhost" port="11141" /> 
  14.         <add address="localhost" port="11151" />--> 
  15.       </servers> 
  16.       <socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> 
  17.     </memcached> 
  18.   </enyim.com> 
  19. </configuration> 


若是咱们配置了多个Memcached的实例,能够想上面的注释部分那样在<servers>节点下添加多个Memcached的实例配置。
这里须要说明的是若是咱们须要向Memcached中添加自定义数据类型时,咱们须要将该数据类型添加上[Serializable]标记。
下面是一个Enyim Memcached的例子:
 算法

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using Enyim.Caching;  
  6. using Enyim.Caching.Memcached;  
  7. /*  
  8.  * 做者:周公(zhoufoxcn)  
  9.  * 日期:2011-03-24  
  10.  * 原文出处:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com  
  11.  * 版权说明:本文能够在保留原文出处的状况下使用于非商业用途,周公对此不做任何担保或承诺。  
  12.  * */ 
  13. namespace MemcachedMonitor  
  14. {  
  15. [Serializable]  
  16. public class Person  
  17. {  
  18.     public int UserId { getset; }  
  19.     public string UserName { getset; }  
  20. }  
  21. public class MemcachedDemo  
  22. {  
  23.     private static MemcachedClient client = new MemcachedClient("enyim.com/memcached");  
  24.  
  25.     public void SetDemo()  
  26.     {  
  27.         Person person = new Person { UserId = 1, UserName = "李刚" };  
  28.         //不带过时时间的存储,Memcached将根据LRU来决定过时策略  
  29.         bool success=client.Store(StoreMode.Add, person.UserName, person);  
  30.         //带过时时间的缓存  
  31.         //bool success = client.Store(StoreMode.Add, person.UserName, person, DateTime.Now.AddMinutes(10));  
  32.         Console.WriteLine("存储[{0}]的结果:{1}", person.UserName, success);  
  33.     }  
  34.  
  35.     public void GetDemo()  
  36.     {  
  37.         Person person = client.Get<Person>("李刚");  
  38.         if (person != null)  
  39.         {  
  40.             Console.WriteLine("取回[{0}]的结果——UserId:{1},UserName:{2}""李刚", person.UserId, person.UserName);  
  41.         }  
  42.         else 
  43.         {  
  44.             Console.WriteLine("取回[{0}]失败!""李刚");  
  45.         }  
  46.     }  
  47.  
  48.     public void MultiGetDemo()  
  49.     {  
  50.         List<string> personNameList = new List<string>();  
  51.         for (int i = 0; i < 10; i++)  
  52.         {  
  53.             personNameList.Add("李刚00" + i);  
  54.         }  
  55.         //批量获取,只经过一次网络通信就取回全部personNameList中的指定的全部数据  
  56.         IDictionary<stringobject> resultList = client.Get(personNameList);  
  57.         Person person;  
  58.         foreach (KeyValuePair<stringobject> item in resultList)  
  59.         {  
  60.             person = item.Value as Person;  
  61.             if (person != null)  
  62.             {  
  63.                 Console.WriteLine("取回[{0}]的结果——UserId:{1},UserName:{2}""李刚", person.UserId, person.UserName);  
  64.             }  
  65.             else 
  66.             {  
  67.                 Console.WriteLine("取回[{0}]失败!""李刚");  
  68.             }  
  69.         }  
  70.     }  
  71. }  


说明:若是须要一次从Memcached中取回多个缓存的数据,能够参考MultiGetDemo()方法,这样一来只须要一次网络通信就能够取回所有数据,减小网络链接时间。此外,在Memcached客户端可使用Text或者Binary协议,通过周公千万次测试比较,使用Binary协议性能略高于使用Text协议。在上面的config文件中周公就配置使用了Binary协议。
总结,使用Memcached这样的分布式缓存能够大大提升应用程序的性能,通过周公测试,正确使用Memcached能够将单台服务器的并发访问数从20提升到1000左右,也就是提升了50倍,这是一个至关客观的提高!限于篇幅,关于Memcached的更深更详细的用法没有在本篇介绍,此文算做抛砖引玉,读者能够自行参考其它相关资料。数据库

周公
2011-03-25缓存

本文出自 “周公(周金桥)的专栏” 博客,请务必保留此出处http://zhoufoxcn.blog.51cto.com/792419/528212服务器

相关文章
相关标签/搜索