设计一个高效的缓存管理服务2011-07-10摘要:一般大家做的缓存都是实时更新,并且用LRU算法实现缓存过期策略,但当缓存越来越大的时候,对缓存做的线程同步会导致应用的响应便慢。如何更有效的使用缓存,如何提高缓存命中率,如何减少对缓存加锁操作,如何提高缓存的性能,我们来讨论一下。1、找出活跃数据,我们用一种分离的方式来找出活跃数据,单独写一个提取活跃数据的后台程序从数据库里统计出最近一小时查阅次数最多的前1w篇文章的ID,这些文章肯定是用户最常访问的文章,把这些文章的数据取出来用FTP上传到缓存服务器上,并发消息给缓存服务器通知它有新的缓存数据可用了,因为提取的都是活跃数据,所以这样的数据做缓存命中率会高一些。2、缓存服务器收到提取活跃数据程序的通知后,从本机磁盘上读取缓存信息加载到内存里,并替换到上次使用的缓存,这样缓存就不是实时更新的,而是相对只读的,每1小时才更新一次,所以使用缓存不需要加锁,只需使用缓存的时候把缓存对象用一个临时变量引用一下,防止在新旧缓存交替的时候被变为null。3、用一个单独的DB来作为数据版本数据库,里面保存着每篇文章的版本信息,版本信息是int类型的,无论谁修改了某篇文章,都要在版本数据库里让相应的文章版本号加1,写一个版本管理服务提供查询某个文章版本和更新某个文章版本的功能。因为版本数据库的字段大多都是int型,表应该很窄,性能不会很差。4、用户请求一篇文章的时候,先看缓存服务器有没有,如果没有,直接从数据库里取出来;如果有,取出缓存数据版本号,并从版本服务器上获取该文章真实版本号,如果一直,就使用缓存数据,如不一直,从数据库里取出文章数据,并更新缓存。这里虽然用户的每个请求都要访问版本数据库,但因为版本数据库结构简单,容易优化,所以出现性能瓶颈的的可能性比较小,而如果缓存命中率足够高的话能减少大量对文章数据库的请求。using System; using System.Collections.Generic; using System.Threading; using System.Diagnostics;
namespace DataCache {
//可缓存的实体类 public class Canbecached{public int Version;} public class Article : Canbecached { } public class CacheItem : Canbecached { public Article Article; }
//每个文章都在版本数据库里保存版本号,该接口提供查询版本方法 //如果某个文章修改了,要调用这个接口的UpdateXXVersion方法 //来更新数据版本,以便让缓存服务器可以得到真实数据的最新的版本 public interface VersionManager { int GetArticleVersion(int articleId); void UpdateArticleVserion(int articleId); }
//该类用于管理缓存,以及提供缓存使用接口 //缓存和使用缓存的服务不一定是一个进程,甚至不是一台机器,通过socket //或者Remoting来使用及更新缓存你 internal static class ArticleCacheManager { private static volatile bool _cacheAvailable = false; static Dictionary<int, CacheItem> _cache = new Dictionary<int, CacheItem>(); public static bool CacheAvailable { get { return _cacheAvailable; } } public static void InitCache() { _cache = new Dictionary<int, CacheItem>(); _cacheAvailable = true; }
} } class Program { static Dictionary<int, ArticleItem> _articles = new Dictionary<int, ArticleItem>(); static void Main(string[] args) { //初始化缓存 ArticleCacheManager.InitCache(); //检查是否有新的缓存可用 new Thread(checkCacheProc).Start();
//用户请求一篇文章 ArticleItem article1 = new ArticleItem(1); article1.Load(); }
public static void checkCacheProc(Object state) { Thread.CurrentThread.Name = "Check whether there is a new cache Thread"; while (true) { try { if (newCacheAvailable()) ArticleCacheManager.LoadCache(); Thread.Sleep(TimeSpan.FromMinutes(60)); } catch (Exception ex) { Trace.TraceError("check cache occur an error:"+ ex.Message); } } }