中小型网站的缓存策略

在上一篇文章《大型网站系统架构分析》中,介绍了大型网站的系统架构,其中缓存环境非常重要,大型网站往往使用Squid前端缓存服务器群,memcache分布式缓存,负载均衡,CDN等来提升性能。但中小型网站使用这些大家伙就有杀鸡用牛刀的感觉,但毋庸置疑,缓存是需要的,开发者开发程序的时候若没有并发思维,一味注重业务逻辑,往往导致并发用户可能在差不多相同的时间请求相同的数据,网站再请求数据库,如果使用缓存,对这种相同的请求,或者不变的数据,可以缓存起来,直接从内存读取,可以大大提升并发访问的性能,减少数据库访问次数。因为数据库往往是最容易出现瓶颈的地方,而通过使用恰当的缓存技术可以非常有效地减轻数据库的负载。

根据我的经验,中小型网站可以采用这样的缓存策略 :

  • 如果是单台服务器可以直接缓存在内存,怕内存不够就合理利用ASP.NET所提供的缓存机制,有现成的干嘛不用呢?缓存存在XML文件中也是一种备选方案,但首先速度没有内存的来的快,其次是写入并发写入XML的时候会锁住文件。可以选用现成的微软Cache application block来实现。
  • 有多台的服务器的话可以考虑memcached,或者其他类似的东西,或者另加一个数据库作为缓存库也行啊。当然有钱的可以用内存数据库。

但不是所有数据都可以缓存的:

  • 对于恒定不变的数据,系统启动后放入缓存就不过期不更新了。
  • 对于偶尔改变的数据,缓存过期时间可以稍微长一些,比如15分钟。
  • 对于经常改变的数据,但访问量极大的热点数据,可以缓存很短的时间,例如30秒,或60秒。(微软有一种数据库数据改变自动同步缓存的SqlDependency功能,有兴趣的读者可以关注)   

如此,便可最大程度的利用缓存,从而可以有效提高系统性能,并明显减轻数据库和网络负载。

你也可以统计并发访问最高的页面和DB访问的频率,把网页静态化,或缓存该些热点,淘汰不热的(缓存替换算法),即使缓存30秒,或60秒也是很大的性能提升,因为他们的并发访问量很大,导致数据库的压力也很大。

代码参考:使用策略模式和单例模式的单机内存缓存:(可以扩展到其他的缓存策略,例如memcache等)

ClassDiagram

ICacheStrategy.cs

 1: using System;
 2: using System.Text;
 3:  
 4: namespace Sample.Caching
 5: {
 6: /// <summary>
 7: /// 缓存策略接口
 8: /// </summary>
 9: public interface ICacheStrategy
 10: {
 11: void AddObject(string objId, object o);
 12: 
 13: void AddObjectWithTimeout(string objId, object o, int timeoutSec);
 14: 
 15: void AddObjectWithFileChange(string objId, object o, string[] files);
 16: 
 17: void AddObjectWithDepend(string objId, object o, string[] dependKey);
 18:  
 19: void RemoveObject(string objId);
 20:  
 21: object RetrieveObject(string objId);
 22:  
 23: int TimeOut { set;get;}
 24: }
 25: }

DefaultCacheStrategy.cs

 1: using System;
 2: using System.Web;
 3: using System.Collections;
 4: using System.Web.Caching;
 5:  
 6: namespace Sample.Caching
 7: {
 8: /// <summary>
 9: /// 默认的缓存策略,实现了缓存策略接口
 10: /// </summary>
 11: public class DefaultCacheStrategy : ICacheStrategy
 12: {
 13: private static readonly DefaultCacheStrategy instance = new DefaultCacheStrategy();
 14:  
 15: protected static volatile System.Web.Caching.Cache webCache = System.Web.HttpRuntime.Cache;
 16:  
 17: protected int _timeOut = 1; //默认缓存一分钟,也可以单独设置对象的超时时间
 18:  
 19: private static object syncObj = new object();
 20:  
 21: /// <summary>
 22: /// Initializes the <see cref="DefaultCacheStrategy"/> class.
 23: /// </summary>
 24: static DefaultCacheStrategy()
 25: {
 26: //lock (syncObj)
 27: //{
 28: // //System.Web.HttpContext context = System.Web.HttpContext.Current;
 29: // //if(context != null)
 30: // // webCache = context.Cache;
 31: // //else
 32: // webCache = System.Web.HttpRuntime.Cache;
 33: //} 
 34: }
 35:  
 36:  
 37: public int TimeOut
 38: {
 39: set { _timeOut = value > 0 ? value : 6000; }
 40: get { return _timeOut > 0 ? _timeOut : 6000; }
 41: }
 42:  
 43: 
 44: public static System.Web.Caching.Cache GetWebCacheObj
 45: {
 46: get { return webCache; }
 47: }
 48:  
 49: public void AddObject(string objId, object o)
 50: {
 51:  
 52: if (objId == null || objId.Length == 0 || o == null)
 53: {
 54: return;
 55: }
 56:  
 57: CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(onRemove);
 58:  
 59: if (TimeOut == 6000)
 60: {
 61: webCache.Insert(objId, o, null, DateTime.MaxValue, TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, callBack);
 62: }
 63: else
 64: {
 65: webCache.Insert(objId, o, null, DateTime.Now.AddMinutes(TimeOut), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, callBack);
 66: }
 67: }
 68:  
 69: public void AddObjectWith(string objId, object o)
 70: {
 71: if (objId == null || objId.Length == 0 || o == null)
 72: {
 73: return;
 74: }
 75:  
 76: CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(onRemove);
 77:  
 78: webCache.Insert(objId, o, null, System.DateTime.Now.AddHours(TimeOut), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, callBack);
 79: }
 80:  
 81: 
 82: public void AddObjectWithTimeout(string objId, object o, int timeoutSec)
 83: {
 84: if (objId == null || objId.Length == 0 || o == null || timeoutSec <= 0)
 85: {
 86: return;
 87: }
 88:  
 89: CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(onRemove);
 90:  
 91: webCache.Insert(objId, o, null, System.DateTime.Now.AddSeconds(timeoutSec), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, callBack);
 92: }
 93:  
 94: public void AddObjectWithFileChange(string objId, object o, string[] files)
 95: {
 96: if (objId == null || objId.Length == 0 || o == null)
 97: {
 98: return;
 99: }
 100:  
 101: CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(onRemove);
 102:  
 103: CacheDependency dep = new CacheDependency(files, DateTime.Now);
 104:  
 105: webCache.Insert(objId, o, dep, System.DateTime.Now.AddHours(TimeOut), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, callBack);
 106: }
 107:  
 108: 
 109: public void AddObjectWithDepend(string objId, object o, string[] dependKey)
 110: {
 111: if (objId == null || objId.Length == 0 || o == null)
 112: {
 113: return;
 114: }
 115:  
 116: CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(onRemove);
 117:  
 118: CacheDependency dep = new CacheDependency(null, dependKey, DateTime.Now);
 119:  
 120: webCache.Insert(objId, o, dep, System.DateTime.Now.AddMinutes(TimeOut), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, callBack);
 121: }
 122:  
 123: public void onRemove(string key, object val, CacheItemRemovedReason reason)
 124: {
 125: switch (reason)
 126: {
 127: case CacheItemRemovedReason.DependencyChanged:
 128: break;
 129: case CacheItemRemovedReason.Expired:
 130: {
 131: //CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(this.onRemove);
 132:  
 133: //webCache.Insert(key, val, null, System.DateTime.Now.AddMinutes(TimeOut),
 134: // System.Web.Caching.Cache.NoSlidingExpiration,
 135: // System.Web.Caching.CacheItemPriority.High,
 136: // callBack);
 137: break;
 138: }
 139: case CacheItemRemovedReason.Removed:
 140: {
 141: break;
 142: }
 143: case CacheItemRemovedReason.Underused:
 144: {
 145: break;
 146: }
 147: default: break;
 148: }
 149:  
 150: //TODO: write log here
 151: }
 152:  
 153: 
 154: public void RemoveObject(string objId)
 155: {
 156: //objectTable.Remove(objId);
 157: if (objId == null || objId.Length == 0)
 158: {
 159: return;
 160: }
 161: webCache.Remove(objId);
 162: }
 163:  
 164:  
 165: public object RetrieveObject(string objId)
 166: {
 167: //return objectTable[objId];
 168:  
 169: if (objId == null || objId.Length == 0)
 170: {
 171: return null;
 172: }
 173:  
 174: return webCache.Get(objId);
 175: }
 176:  
 177: }
 178: }

CachingManager.cs

 1: using System;
 2: using System.Collections.Generic;
 3: using System.Text;
 4:  
 5: namespace Sample.Caching
 6: {
 7: /// <summary>
 8: /// The caching manager
 9: /// </summary>
 10: public class CachingManager
 11: {
 12: private static ICacheStrategy cs;
 13: private static volatile CachingManager instance = null;
 14: private static object lockHelper = new object();
 15: 
 16: //private static System.Timers.Timer cacheConfigTimer = new System.Timers.Timer(15000);//Interval in ms
 17:  
 18: 
 19: private CachingManager()
 20: {
 21: cs = new DefaultCacheStrategy();
 22:  
 23: ////Set timer
 24: //cacheConfigTimer.AutoReset = true;
 25: //cacheConfigTimer.Enabled = true;
 26: //cacheConfigTimer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
 27: //cacheConfigTimer.Start();
 28: }
 29:  
 30: 
 31: private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
 32: {
 33: //TODO:
 34: }
 35:  
 36: 
 37: public static CachingManager GetCachingService()
 38: {
 39: if (instance == null)
 40: {
 41: lock (lockHelper)
 42: {
 43: if (instance == null)
 44: {
 45: instance = new CachingManager();
 46: }
 47: }
 48: }
 49:  
 50: return instance;
 51: }
 52:  
 53: 
 54: public virtual void AddObject(string key, object o)
 55: {
 56: if (String.IsNullOrEmpty(key) || o == null) return;
 57:  
 58: lock (lockHelper)
 59: {
 60: if (cs.TimeOut <= 0) return;
 61:  
 62: cs.AddObject(key, o);
 63: }
 64: }
 65:  
 66: 
 67: public virtual void AddObject(string key, object o, int timeout)
 68: {
 69: if (String.IsNullOrEmpty(key) || o == null) return;
 70:  
 71: lock (lockHelper)
 72: {
 73: if (cs.TimeOut <= 0) return;
 74:  
 75: cs.AddObjectWithTimeout(key, o, timeout);
 76: }
 77: }
 78:  
 79: 
 80: public virtual object RetrieveObject(string objectId)
 81: {
 82: return cs.RetrieveObject(objectId);
 83: }
 84:  
 85: 
 86: public virtual void RemoveObject(string key)
 87: {
 88: lock (lockHelper)
 89: {
 90: cs.RemoveObject(key);
 91: }
 92: }
 93:  
 94: 
 95: public void LoadCacheStrategy(ICacheStrategy ics)
 96: {
 97: lock (lockHelper)
 98: {
 99: cs = ics;
 100: }
 101: }
 102:  
 103: 
 104: public void LoadDefaultCacheStrategy()
 105: {
 106: lock (lockHelper)
 107: {
 108: cs = new DefaultCacheStrategy();
 109: }
 110: }
 111: }
 112: }

调用:

 1: CachingManager cm = CachingManager.GetCachingService();
 2: cm.LoadDefaultCacheStrategy();
 3: //从缓存获取
 4: IList list = cm.RetrieveObject("mykey") as IList;
 5: if (list == null)
 6: {
 7: list = DAL.GetData(); //从数据库查询
 8: cm.AddObject("mykey", list, 2 * 60); //缓存2分钟
 9: }
 10: return list;

调用时序图:

SequenceDiagram1

posted on 2009-05-05 13:32  Mainz  阅读(2849)  评论(2编辑  收藏  举报

导航