在上一篇文章《大型网站系统架构分析》中,介绍了大型网站的系统架构,其中缓存环境非常重要,大型网站往往使用Squid前端缓存服务器群,memcache分布式缓存,负载均衡,CDN等来提升性能。但中小型网站使用这些大家伙就有杀鸡用牛刀的感觉,但毋庸置疑,缓存是需要的,开发者开发程序的时候若没有并发思维,一味注重业务逻辑,往往导致并发用户可能在差不多相同的时间请求相同的数据,网站再请求数据库,如果使用缓存,对这种相同的请求,或者不变的数据,可以缓存起来,直接从内存读取,可以大大提升并发访问的性能,减少数据库访问次数。因为数据库往往是最容易出现瓶颈的地方,而通过使用恰当的缓存技术可以非常有效地减轻数据库的负载。
根据我的经验,中小型网站可以采用这样的缓存策略 :
- 如果是单台服务器可以直接缓存在内存,怕内存不够就合理利用ASP.NET所提供的缓存机制,有现成的干嘛不用呢?缓存存在XML文件中也是一种备选方案,但首先速度没有内存的来的快,其次是写入并发写入XML的时候会锁住文件。可以选用现成的微软Cache application block来实现。
- 有多台的服务器的话可以考虑memcached,或者其他类似的东西,或者另加一个数据库作为缓存库也行啊。当然有钱的可以用内存数据库。
但不是所有数据都可以缓存的:
- 对于恒定不变的数据,系统启动后放入缓存就不过期不更新了。
- 对于偶尔改变的数据,缓存过期时间可以稍微长一些,比如15分钟。
- 对于经常改变的数据,但访问量极大的热点数据,可以缓存很短的时间,例如30秒,或60秒。(微软有一种数据库数据改变自动同步缓存的SqlDependency功能,有兴趣的读者可以关注)
如此,便可最大程度的利用缓存,从而可以有效提高系统性能,并明显减轻数据库和网络负载。
你也可以统计并发访问最高的页面和DB访问的频率,把网页静态化,或缓存该些热点,淘汰不热的(缓存替换算法),即使缓存30秒,或60秒也是很大的性能提升,因为他们的并发访问量很大,导致数据库的压力也很大。
代码参考:使用策略模式和单例模式的单机内存缓存:(可以扩展到其他的缓存策略,例如memcache等)
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;
调用时序图: